Функция карты Haskell с предикатом - PullRequest
7 голосов
/ 12 февраля 2011

Мне кажется, это должно быть довольно очевидно или просто, но я просто не могу этого понять.То, что я хочу сделать, это применить функцию к списку (используя карту), но только если выполняется условие.Представьте, что вы хотели бы разделить только числа, которые были четными:

map (`div` 2) (even) [1,2,3,4]

И это дало бы [1,1,3,2], так как только четные числа имели бы к ним функцию.Очевидно, что это не работает, но есть ли способ сделать это без необходимости писать отдельную функцию, которую вы можете дать map?Фильтр почти готов, за исключением того, что я также хочу сохранить элементы, для которых условие не выполняется, и просто не применять к ним функцию.

Спасибо

Ответы [ 6 ]

10 голосов
/ 12 февраля 2011

Если вы не хотите определять отдельную функцию, тогда используйте лямбду.

map (\x -> if (even x) then (x `div` 2) else x) [1,2,3,4]

Или вместо карты, понимание списка, немного более читабельное, я думаю.

[if (even x) then (x `div` 2) else x | x <- [1,2,3,4]]
7 голосов
/ 12 февраля 2011
mapIf p f = map (\x -> if p x then f x else x)
3 голосов
/ 12 февраля 2011

В дополнение к ответу PiotrLegnica: часто легче читать, если вы объявляете вспомогательную функцию вместо использования лямбда-выражения. Учтите это:

map helper [1..4] where
  helper x | even x    = x `div` 2
           | otherwise = x

([1..4] - это сахар для [1,2,3,4])

Если вы хотите вместо этого удалить все другие элементы, рассмотрите возможность использования filter. filter удаляет все элементы, которые не удовлетворяют предикату:

filter even [1..4] -> [2,4]

Таким образом, вы можете построить канал map и отфильтровать или использовать вместо него списочное понимание:

map (`div` 2) $ filter even [1..4]
[x `div` 2 | x <- [1..4], even x]

Выберите то, что вам больше нравится.

2 голосов
/ 12 февраля 2011

Создайте свой собственный помощник:

ifP pred f x = 
    if pred x then f x
    else x

custom_f = ifP even f
map custom_f [..]

(предостережение: у меня сейчас нет доступа к компилятору. Думаю, это работает нормально ...)

1 голос
/ 13 февраля 2011

Мне нравятся другие, более общие решения, но в вашем особом случае вы можете избежать неприятностей с

map (\x -> x `div` (2 - x `mod` 2)) [1..4]
0 голосов
/ 13 февраля 2011

В основном это вырванные из существующих ответов, но согласно моему предвзятому определению «читабельный» (я люблю охранников больше, чем если бы, и where больше, чем let):

mapIf p f = map f'
    where f' x | p x = f x | otherwise = x

ghciговорит, что, вероятно, работает

ghci> let mapIf p f = map f' where f' x | p x = f x | otherwise = x
ghci> mapIf even (+1) [1..10]
[1,3,3,5,5,7,7,9,9,11]
...