在ghci中,我写道:
let x = do
i <- [1..5]
j <- [2..4]
return i
预期结果:[1,2,3,4,5]
实际结果:[1,1,1,2,2,2,3,3,3,4,4,4,5,5,5]
我不明白该输出背后的逻辑。我认为原因可能与 monad 有关,但我对函数式编程很陌生,我希望有人能解释一下。我也试过 List-comprehension 中的等价形式,结果是一样的,这意味着我在这里误解了一些基本的东西。
最佳答案
这是因为 do 机制并不关心(幸运的是)最里面的代码是否实际引用了(一些)循环变量。
看你总是得到 3*5=15 个值,而不管最里面的代码:
λ>
λ> xs1 = do { i <- [1..5] ; j <- [2..4] ; return i }
λ> xs1
[1,1,1,2,2,2,3,3,3,4,4,4,5,5,5]
λ>
λ> xs2 = do { i <- [1..5] ; j <- [2..4] ; return 9 }
λ> xs2
[9,9,9,9,9,9,9,9,9,9,9,9,9,9,9]
λ>
λ> xs3 = do { i <- [1..5] ; j <- [2..4] ; return (i,j) }
λ> xs3
[(1,2),(1,3),(1,4),(2,2),(2,3),(2,4),(3,2),(3,3),(3,4),(4,2),(4,3),(4,4),(5,2),(5,3),(5,4)]
λ>
λ> length xs1
15
λ> length xs2
15
λ> length xs3
15
λ>
据我所知,这是完全标准的行为,Haskell 与 C、C++、Fortran、Python 共享......C++ 等效示例:
#include <vector>
#include <iostream>
int main()
{
std::vector<int> vi{1,2,3,4,5};
std::vector<int> vj{2,3,4};
for (int i: vi)
for (int j: vj)
std::cout << i << ", ";
std::cout << std::endl;
return EXIT_SUCCESS;
}
C++ 输出:$ ./a.out
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5,
$
关于list - 需要解释基本的 do block 语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63486205/