Список пониманий похож на вложенные циклы:
xs >>| foo = [ y | x <- xs, y <- foo x]
-- = for x in xs:
-- for y in (foo x):
-- yield y
Таким образом, мы имеем
[1,2,3,4] >>| (\x -> [x, x+10])
=
[ y | x <- [1,2,3,4], y <- (\x -> [x, x+10]) x]
=
[ y | x <- [1] ++ [2,3,4], y <- [x, x+10]]
=
[ y | x <- [1], y <- [x, x+10]] ++ [ y | x <- [2,3,4], y <- [x, x+10]] -- (*)
=
[ y | y <- [1, 1+10]] ++ [ y | x <- [2,3,4], y <- [x, x+10]]
=
[ y | y <- [1]] ++ [ y | y <- [11]] ++ [ y | x <- [2,3,4], y <- [x, x+10]]
=
[1] ++ [11] ++ [ y | x <- [2,3,4], y <- [x, x+10]]
=
[1, 11] ++ [2, 12] ++ [ y | x <- [3,4], y <- [x, x+10]]
=
[1, 11] ++ [2, 12] ++ [3, 13] ++ [ y | x <- [4], y <- [x, x+10]]
=
[1, 11] ++ [2, 12] ++ [3, 13] ++ [4, 14]
Важный шаг отмечен (*)
. Вы можете принять это как определение того, что список пониманий .
Особый случай, когда функция foo
возвращает одноэлементный список, как в вашем вопросе. Тогда это действительно равносильно отображению , поскольку каждый элемент в списке ввода превращается в один (преобразованный) элемент в списке вывода.
Но понимание списка более мощное. Элемент ввода также можно условно превратить в элементы без (работающие как filter ) или в несколько элементов:
[ a, [a1, a2] ++ concat [ [a1, a2], [ a1, a2,
b, ==> [b1] ++ == [b1], == b1,
c, [] ++ [],
d ] [d1, d2] [d1, d2] ] d1, d2 ]
Выше эквивалентно
concat (map foo [a,b,c,d])
=
foo a ++ foo b ++ foo c ++ foo d
для некоторых подходящих foo
.
concat
- это список монад join
, а map
- список монад fmap
. В общем, для любой монады
m >>= foo = join (fmap foo m)
Суть Монады заключается в следующем: из каждой сущности "в" "структуре", условно создавая новые элементы в той же структуре и объединяя их на месте:
[ a , b , c , d ]
/ \ | | / \
[ [a1, a2] , [b1] , [] , [d1, d2] ] -- fmap foo = [foo x | x <- xs]
-- = [y | x <- xs, y <- [foo x]]
[ a1, a2 , b1 , d1, d2 ] -- join (fmap foo) = [y | x <- xs, y <- foo x ]