Ответ 1
Прочитайте [
как "список", |
как "для", <-
как "in", ,
как "и".
Перечисления выполняются вложенным образом. [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2]
действительно
for c from 1 to 10 step 1:
for b from 1 to c step 1:
for a from 1 to b step 1:
if (a^2 + b^2 == c^2):
emit (a,b,c)
Однако в Haskell вышеупомянутое достигается следующим переводом
[1..10] >>= (\c-> -- (a function of 'c', producing ...
[1..c] >>= (\b-> -- (a function of 'b', producing ...
[1..b] >>= (\a-> -- (a function of 'a', producing ...
if a^2+b^2==c^2 then [(a,b,c)] else []
-- or: [(a,b,c) | a^2+b^2==c^2]
)))
чтобы вы могли видеть вложенную структуру здесь. (>>=)
тоже ничего загадочного. Прочитайте >>=
как "поданный" или "пробитый", хотя его официальное название "привязывается". Он определен (для списков) как
(xs >>= f) = concatMap f xs = concat (map f xs)
f
здесь называется (через map
) для каждого элемента из xs
по порядку. Он должен создавать списки, чтобы их можно было комбинировать с concat
. Поскольку пустые списки []
удаляются на concat
(например, concat [[1], [], [3]] == [1,3]
), все элементы, которые не проходят тест, исключаются из окончательного вывода.
Полный перевод см. в разделе раздел 3.11 "Пояснения к списку" отчета Haskell 98. В общем случае понимание списка может содержать шаблон, а не только имя переменной. Понимание
[e | pat <- ls, ...]
переводится как
ls >>= (\x -> case x of pat -> [e | ...] ;
_ -> [] )
где pat
- некоторый шаблон, а x
- новая переменная. Когда происходит несоответствие шаблона, создается пустой список (вместо ошибки времени выполнения) и этот элемент x
of ls
пропускается. Это полезно для дополнительной фильтрации на основе шаблонов, например, например. [x | Just x <- ls, even x]
, где все Nothing
в ls
игнорируются.