我刚刚开始学习haskell(字面意思,今晚!)而且我在理解列表推导的逻辑方面遇到了一些麻烦,更具体地说是<-
运算符。 Learn You Some Haskell上的一个小例子查找长度小于10的所有元组:
ghci> let triangles = [ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10] ]
我最初的理解是这些都会一起增加,但在看到输出后我真的不理解这些列表的递增方法。另一个似乎让我得到的例子是:
ghci> let rightTriangles = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2]
我真的很感激对这些的一点解释,感谢你对我缺乏耐克尔情报的耐心。
答案 0 :(得分:20)
将[
称为“列表”,将|
称为“for”,将<-
称为“in”,将,
称为“and”。
枚举以嵌套方式完成。 [ (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]
)))
所以你真的可以在这里看到嵌套结构。 (>>=)
也不是什么神秘的事。阅读>>=
为“fed into”或“push through”,尽管其官方名称为“bind”。它被定义为(对于列表)为
(xs >>= f) = concatMap f xs = concat (map f xs)
f
此处(按map
)按顺序调用xs
的每个元素。它必须生成列表,以便它们可以与concat
结合使用。由于在[]
上删除了空列表concat
(例如concat [[1], [], [3]] == [1,3]
),所有未通过测试的元素都会从最终输出中删除。
有关Haskell 98报告的完整翻译,请参阅section 3.11, List Comprehensions。通常,列表推导可以包含模式,而不仅仅是变量名称。理解
[e | pat <- ls, ...]
翻译为
ls >>= (\x -> case x of pat -> [e | ...] ;
_ -> [] )
其中pat
是某种模式,x
是一个新变量。当模式不匹配时,会生成一个空列表(而不是运行时错误),并跳过x
的元素ls
。这对于附加的基于模式的过滤是有用的,例如, <{1}},[x | Just x <- ls, even x]
中Nothing
的{{1}}被悄然忽略。
答案 1 :(得分:4)
[ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10] ]
表示,对于(a,b,c)的所有组合,其中a在[1..10]中,b在[1..10]中,c在[1..10]中]
如果你想要(1,1,1)(2,2,2)种类,你应该使用zip
:zip [1..10] [1..10]
或3个列表,zip3 [1..10] [1..10] [1..10]
答案 2 :(得分:1)
我认为列表理解语法是Haskell尝试在语言中获得Set-builder notation。我们使用'['而不是'{'和'&lt; - '而不是'ε'。列表理解语法甚至可以generalized到任意monad。