Read [
как "список", |
как «для ", <-
как" 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]
), все элементы, которые не проходят тест, исключаются из конечного вывода.
Для полного перевода см section 3.11, List Comprehensions, Отчета Haskell 98. В общем случае понимание списка может содержать шаблон, а не только имя переменной. Понимание
[e | pat <- ls, ...]
переводится как
ls >>= (\x -> case x of pat -> [e | ...] ;
_ -> [])
где pat
некоторый шаблон, и x
это новая переменная. Когда происходит несоответствие шаблона, создается пустой список (вместо ошибки времени выполнения), и этот элемент x
из ls
пропускается.Это полезно для дополнительной фильтрации на основе шаблонов, например, например. [x | Just x <- ls, even x]
, где все Nothing
s в ls
спокойно игнорируются.
Подробный и очень ясный, действительно помог мне понять материал. Спасибо! –