2016-04-27 2 views
3

У меня есть функция с большим количеством охранников, которые выглядят так:Как укоротить реализацию Haskell?

function 
    | p `elem` [0,1,2,3,4,5,6] = [0,1,2,3,4,5,6] 
    | p `elem` [7,8,9,10,11,12,13] = [7,8,9,10,11,12,13] 
    | p `elem` [14,15,16,17,18,19,20] = [14,15,16,17,18,19,20] 
    | otherwise = [] 

Я уверен, что я могу написать это гораздо короче, с Haskell. Если нет, тогда все в порядке. Я новичок в Haskell, и я хотел бы стать лучше, изучая разные подходы.

Возможно, использование «карты» может быть хорошим началом? Но тогда я не уверен, как пройти в этих конкретных списках.

Значения не всегда смежные.

+0

являются блоки номеров всегда непрерывные значения в группах 7? – jamshidh

+0

@jamshidh Они всегда в группах по 7, но не всегда содержат последовательные значения. – heyyo

+0

Для этой конкретной функции у меня возникло бы желание написать что-то вроде 'function p = [lo .. lo + 6], где lo = 7 * div p 7'. –

ответ

6

Как насчет простых проверок?

function p 
    | p < 0 = [] 
    | p < 7 = [0..6] 
    | p < 14 = [7..13] 
    | p < 21 = [14..20] 
    | otherwise = [] 

Это будет быстрее, и для некоторых приложений использование меньше памяти.

Если вы не хотите выполнять проверку границ (но проверку элемента), вы все равно можете использовать сокращенную нотацию списка.


В качестве альтернативы, можно построить вспомогательную функцию, что итерация по спискам:

helper (x:xs) p | elem p x = x 
       | otherwise = helper xs p 
helper [] _ = [] 

function = helper [[0..6],[7..13],[14..20]] 

Хотя это на самом деле больше, вы можете легко расширить function использовать другие списки. Обратите внимание, однако, что эта функция будет медленнее, так как elem требует O (n) время, тогда как проверка границ принимает O (1) время.


Вы можете также - как это предлагается в @jamshidh's answer построить Data.Map, который является структура данных, которая гарантирует время O (журнал N) подстановок:

import Data.Map (Map) 
import qualified Data.Map as Map 
import Data.Maybe(fromMaybe) 

helper2 :: Ord a => [[a]] -> a -> [a] 
helper2 lst p = fromMaybe [] $ Map.lookup p (Map.fromList $ concatMap (\x -> zip x (repeat x)) lst) 

function = helper2 [[0..6],[7..13],[14..20]] 

Для этой последней части, он генерирует (\x -> zip x (repeat x)) генерирует список кортежей, содержащий элемент списка e и весь список l. Например:

Prelude> (\x -> zip x (repeat x)) [0..6] 
[(0,[0,1,2,3,4,5,6]),(1,[0,1,2,3,4,5,6]),(2,[0,1,2,3,4,5,6]),(3,[0,1,2,3,4,5,6]),(4,[0,1,2,3,4,5,6]),(5,[0,1,2,3,4,5,6]),(6,[0,1,2,3,4,5,6])] 

Это работает следующим образом: x унифицирует со списком, например [0,1,2,3,4,5,6], теперь мы применяем zip функцию [0,1,2,3,4,5,6] и на бесконечном списке [[0,1,2,3,4,5,6],[0,1,2,3,4,5,6],[0,1,2,3,4,5,6],....]. zip генерирует кортежи до тех пор, пока оба элемента содержат элементы списка, поэтому он принимает первый элемент от [0,1,..,6] и первый от [[0,1,..,6],[0,1,..,6],[0,1,..,6],...], поэтому полученный результат равен (0,[0..6]), затем он берет второй элемент 1 из списка, а второй элемент из функции repeat , таким образом (1,[0..6]). Он продолжает делать это - хотя и лениво - до тех пор, пока один из списков не будет исчерпан, что имеет место для первого списка.

+0

В соответствии с вопросом значения не всегда смежны. Так что, к сожалению, это не сработает. – Sibi

+1

О, вау, почему я не подумал об этом! Благодаря! Это работает для некоторых условий. :) – heyyo

+0

@ Сиби: поэтому я обновил вопрос двумя дополнительными альтернативами. –

4

Сначала создайте список списков

lists = [[0,1,2,3,4,5,6], [7,8,9,10,11,12,13], [14,15,16,17,18,19,20]] 

Затем создайте отображение значения для вывода списка

theMap = concat $ map (\x -> zip x (repeat x)) lists 

Это даст вам то, что вам нужно

> lookup 1 
Just [0,1,2,3,4,5,6] 

Обратите внимание, что выход является Maybe, если вы не указали значение в любом списке.

+0

Как это влияет на эффективность программы? Это быстрее? – heyyo

+0

Это определенно бьет с использованием 'elem', я считаю – heyyo

+0

Если вы конвертируете в Map (используя fromList), то после создания сопоставления он будет намного быстрее, потому что вам не придется пересекать список, используя' elem'. Я сохранил его при обычном поиске по списку, но карта работает одинаково. – jamshidh

5

Здесь вы можете использовать список монада.

func p = join $ do x <- [[1,3,5], [2,4,6], [7,8,9]] 
        guard $ p `elem` x 
        return x 

Список списков - это то, что вы хотите проверить. Вызов guard отфильтровывает выбор, который не удался. Пока списки кандидатов не пересекаются, не более одного удастся. return x оценивает либо [] или [x] для одного из выборов x, поэтому join уменьшает [x] к [].

> func 1 
[1,3,5] 
> func 2 
[2,4,6] 
> func 7 
[7,8,9] 
> func 10 
[] 

как список понимания, это будет выглядеть как

func p = join [x | x <-[[1,3,5],[2,4,6],[7,8,9]], p `elem` x] 
+0

Я почти уверен, что есть способ взять * first *, а не только * только *, выбор 'x', для которого' elem p x' истинно. – chepner

Смежные вопросы