为什么使用多个生成器的Haskell列表推导将最右边的生成器视为最紧密的循环?

kin*_*ini 15 haskell conventions

我正在阅读Gentle Introduction,我想知道为什么在使用两个生成器的列表理解中,最右边的生成器是"最快的"迭代(即编译到最里面的循环,我猜).观察以下GHCi输出:

*Main> concat [[(x,y) | x <- [0..2]] | y <- [0..2]]
[(0,0),(1,0),(2,0),(0,1),(1,1),(2,1),(0,2),(1,2),(2,2)]
*Main> [(x,y) | x <- [0..2], y <- [0..2]]
[(0,0),(0,1),(0,2),(1,0),(1,1),(1,2),(2,0),(2,1),(2,2)]
Run Code Online (Sandbox Code Playgroud)

如果最左边的生成器迭代最快,上面两个表达式将具有相同的值,我认为这使得选择此约定更加自然.

那么有谁知道为什么选择相反的惯例?我注意到Python与Haskell具有相同的约定(甚至可能从Haskell借用它?),而在Python世界中,这个词似乎是选择了"因为这是你编写for循环的顺序",但是我认为,就for循环来说,思考并不是大多数Haskell程序员所做的......

思考?


从我对Louis Wasserman的回答如下:

我猜这里对应于理解的命令式解释的顺序被认为比嵌套列表更自然.所以在本质上,Haskell对此的解释与我在问题中链接的Python解释相同,毕竟,似乎.

Lou*_*man 22

所以事情的范围是理智的.

[(x, y) | x <- [1..10], y <- [1..x]]
Run Code Online (Sandbox Code Playgroud)

有意义 - x是在理解的范围y- 但是

[(x, y) | y <- [1..x], x <- [1..10]]
Run Code Online (Sandbox Code Playgroud)

有点不太有意义.

另外,这种方式与domonad语法一致:

do x <- [1..10]
   y <- [1..x]
   return (x, y)
Run Code Online (Sandbox Code Playgroud)

  • 对于未经证实的人来说,使用`do` monad语法显示相同代码的外观可能是一个好主意. (3认同)
  • 嵌套是(并且意味着)与链式理解语法完全不同.无论如何,"do"符号具有列表推导同意的明确顺序. (2认同)

Chr*_*lor 6

如果将列表理解首先扩展为do符号然后再转换为monadic绑定,则可能更有意义.假设我们想要写一个理解,我们引用已经绑定的名称:

[ (x,y) | x <- [1,2,3], y <- [x+1,x+2] ]
Run Code Online (Sandbox Code Playgroud)

这扩展到

do x <- [1,2,3]
   y <- [x+1,x+2]
   return (x,y)
Run Code Online (Sandbox Code Playgroud)

扩展到

[1,2,3] >>= \x ->
[x+1,x+2] >>= \y -> 
return (x,y)
Run Code Online (Sandbox Code Playgroud)

这清楚地说明了x它在需要时的确切范围.

如果扩展到do符号从右到左而不是从左到右发生,那么我们的原始表达式将扩展为

[x+1,x+2] >>= \y ->
[1,2,3] >>= \x ->
return (x,y)
Run Code Online (Sandbox Code Playgroud)

这显然是荒谬的 - 它指的是xx尚未约束的范围内的价值.所以我们必须把我们原来的理解写成

[ (x,y) | y <- [x+1,x+2], x <- [1,2,3] ]
Run Code Online (Sandbox Code Playgroud)

得到我们想要的结果,这似乎是不自然的 - 当你的眼睛扫描短语时,y <- [x+1,x+2]你实际上并不知道是什么x.你必须向后阅读理解才能找到答案.

因此,不需要将最右边的绑定展开到"内部循环"中,但是当您考虑到人类将不得不阅读生成的代码时,这是有道理的.

  • 这是一个有效的观点.我怀疑用这种方式编写它的原因是它与数学[set-builder notation](http://en.wikipedia.org/wiki/Set-builder_notation#Parallels_in_programming_languages)相似(请记住,Haskell有很多来自数学的影响).万一它可以帮助你 - 我总是把竖条"|"看成"哪里"(在我认为自己更像数学家而不是程序员的时候,我常常把它读成"这样") (2认同)