Ну, вышеприведенное выражение, вероятно, не то, что считается идиоматизм c Haskell. Вероятно, лучшей версией будет:
[ <b>(x0, x1)</b> | <b>(x0:x1:_)</b> <- mapM (const "ABC") [1..2], <b>x0 < x1</b> ]
Это чище, и если списки в mapM (const "ABC")
вернут список, который содержит менее двух элементов (что здесь невозможно), это не приведет к ошибке.
Вероятно, основная проблема здесь заключается в том, чтобы понять, как работает mapM
. Выражение mapM (const "ABC") [1..2]
сводится к:
mapM (\_ -> "ABC") [1..2]
, поскольку мы не учитываем значения списка, оно эквивалентно:
replicateM 2 "ABC"
Это replicateM
для 2
может быть переписан как (псевдо-Haskell синтаксис):
replicateM 2 :: [a] -< [[a]]
replicateM 2 l = do
x0 <- l
x1 <- l
return [x0, x1]
или, если мы обнуляем это:
replicateM 2 l = l >>= \x0 -> l >>= \x1 -> return [x0, x1]
Для списка, экземпляр Monad
реализован как:
instance Monad [] where
return x = [x]
(>>=) = flip concatMap
, что означает, что для списка это replicateM 2
, реализовано как:
replicateM 2 l :: [a] -> [[a]]
replicateM 2 l = concatMap (\x0 -> concatMap (\x1 -> [[x0, x1]]) l) l
или проще:
replicateM 2 l :: [a] -> [[a]]
replicateM 2 l = concatMap (\x0 -> map (\x1 -> [x0, x1]) l) l
таким образом мы делаем все возможные комбинации двух пунктов списка. Таким образом, это означает, что:
Prelude Control.Monad> replicateM 2 "ABC"
["AA","AB","AC","BA","BB","BC","CA","CB","CC"]
Затем мы используем это в понимании списка, и для каждого из этих подсписков с двумя элементами мы проверяем, меньше ли первый элемент x0
, чем второй элемент с часть фильтра в понимании списка (x0 < x1
). Если это так, мы выдаем эти элементы как 2-кортеж.
Таким образом, он создает 2-кортежа для каждых двух элементов в "ABC"
, если первый элемент (строго) меньше, чем второй .
Здесь мы, однако, делаем немного «слишком много работы»: более половины элементов будут отклонены. Мы можем оптимизировать это, используя tails :: [a] -> [[a]]
:
import Data.List(tails)
[(x0, x1) | (x0:<b>xs</b>) <- <b>tails "ABC"</b>, x1 <- <b>xs</b> ]
, что дает то же значение:
Prelude Control.Monad Data.List> [(x0, x1) | (x0:xs) <- tails "ABC", x1 <- xs ]
[('A','B'),('A','C'),('B','C')]