list - 需要解释基本的 do block 语法

标签 list haskell list-comprehension monads do-notation

在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/

相关文章:

python - 如何在 Python 中将 dict 元素值作为列表

python - csv 中第一位数字的数字频率,不导入

python - 如果满足条件,则从列表列表中删除列表

C++ 列表实现

python - 删除列表中没有出现在另一个列表中的子字符串的项目的优雅方法

Haskell - 定义函数

haskell - 导出 ((.) foldr) 的类型

Haskell GHC-7.6.2 使用 HashMap 导出数据和类型

python - 我们可以在列表推导式中使用嵌套 for 循环的幕后原因是什么

python - python中的list1 = [] list2 = []和list1 = list2 = []有什么区别?