【问题标题】:Need explanation for basic do block syntax需要解释基本的 do 块语法
【发布时间】:2020-08-19 11:40:11
【问题描述】:

在 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 中的等价形式,结果是一样的,这意味着我在这里误解了一些基本的东西。

【问题讨论】:

    标签: list haskell list-comprehension monads do-notation


    【解决方案1】:

    这是因为 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, 
    $ 
    
    

    【讨论】:

      【解决方案2】:

      我也试过List-comprehension中的等价形式,结果是一样的

      好主意。碰巧对于列表,do 表示法与列表推导完全相同。 (事实上​​,a syntactic extension 允许您对任何 monad 使用列表理解符号,就像您可以对任何 monad 使用 do 符号一样。)

      所以,你问为什么[a | a&lt;-[0,1], b&lt;-[2,3]] 给出[0,0,1,1] 而不是[0,1]。这看起来令人惊讶的是,如果您将列表推导视为 集合推导,就像在数学中发现的那样。但是列表不是集合,尽管 Haskeller 确实经常使用列表作为集合的临时替代品。如果列表推导作为集合推导,那么

        [x | x <- [0,1,0]]
      

      也应该只产生[0,1]作为它的结果(或者至少,它应该产生与[x|x&lt;-[0,1]]相同的相同结果)。

      一般来说,这种清除重复项需要相等性检查,如果你想让它高效,也可以使用排序或散列方法。列表不做任何这样的事情,所以如果你想要类似集合的行为,你应该使用集合实现数据结构。 SetHashSet 是最常见的。

      【讨论】:

        猜你喜欢
        • 2013-03-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多