Как мне реализовать понимание списка, используя карту и фильтр? - PullRequest
5 голосов
/ 30 мая 2019

И map, и filter могут быть реализованы с использованием понимания списка:

map f xs    = [f x | x <- xs]
filter p xs = [x | x <- xs, p x] 

Я хотел бы показать, что обратное утверждение также справедливо, используя следующий пример:

[expr | p <- s]

У меня так далеко:

map (\p -> expr) s

Но это работает только тогда, когда сопоставление с шаблоном с p выполняется на всех элементах s.В некотором смысле, я сначала хочу отфильтровать s, используя сопоставление с шаблоном на p.Естественно, я пытался разобраться в этом вопросе, но мне не удалось найти решение, в котором не использовалось бы использование списков или LambdaCase.

Ответы [ 2 ]

9 голосов
/ 30 мая 2019

Но это работает только тогда, когда сопоставление с шаблоном с p выполняется на всех элементах s.

Действительно: описанное вами поведение сопоставления с образцом, в общем, не может быть достигнуто только с помощью map и filter. Выражение пониманий в таких терминах хорошо работает только в более простом случае пониманий с одним генератором и шаблонами, которые не подведут. Скорее, списочные значения указаны в отчете Haskell в терминах concatMap. В частности, пункт о генераторах охватывает возможность сбоя сопоставления с образцом:

--This is pseudo-Haskell; see the Report for the fine print.
[  <em>e</em> | <em>p</em> <- <em>l</em>,  <em>Q</em> ] = let ok <em>p</em> = [  <em>e</em> | <em>Q</em> ]
                          ok _ = []
                          in concatMap ok <em>l</em>

Обработка ошибок совпадения соответствует тому, что fail делает в десагеринге моноблочных do-блоков списка.

3 голосов
/ 30 мая 2019

Да, вы не можете выполнить сопоставление с образцом в лямбде, не используя \ x -> case x of ... (или LambdaCase, чтобы сократить его); ваш пример:

[2*x | (x,2) <- [(1,2), (3,4)]]

должно быть реализовано как:

map (\(x,_) -> 2*x) $ filter (\(_,y) -> y == 2) [(1,2), (3,4)]

Или, используя LambdaCase:

map (\(x,_) -> 2*x) $ filter (\case (_,2) -> True; _ -> False) [(1,2), (3,4)]

Также, для бессрочной версии:

map ((2*) . fst) $ filter ((==2) . snd) [(1,2), (3,4)]
...