简体   繁体   English

需要解释基本的 do 块语法

[英]Need explanation for basic do block syntax

In ghci, I wrote:在 ghci 中,我写道:

 let x = do
    i <- [1..5]
    j <- [2..4]
    return i 

Expected result:预期结果:

[1,2,3,4,5]

Actual result:实际结果:

[1,1,1,2,2,2,3,3,3,4,4,4,5,5,5]

I don't understand the logic behind that output.我不明白 output 背后的逻辑。 I think the reason might be something about monad, but I am very new to functional programming, I wish someone could explain it a little bit.我认为原因可能与 monad 有关,但我对函数式编程很陌生,我希望有人能解释一下。

I've also tried the equavalent form in List-comprehension and the result is the same, which means there is something basic I misunderstood here.我也尝试了 List-comprehension 中的等价形式,结果是一样的,这意味着我在这里误解了一些基本的东西。

This is because the do mechanism does not care (fortunately) about whether or not the innermost code actually refers to (some of) the loop variables.这是因为 do 机制并不关心(幸运的是)最里面的代码是否实际上引用了(某些)循环变量。

See you always get 3*5=15 values regardless of innermost code:看到你总是得到 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
 λ> 

As far as I can tell, this is perfectly standard behavior, that Haskell shares with C, C++, Fortran, Python... As far as I can tell, this is perfectly standard behavior, that Haskell shares with C, C++, Fortran, Python...

A C++ equivalent example: 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++ output: C++ output:

$ ./a.out
1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 
$ 

I've also tried the equavalent form in List-comprehension and the result is the same我也尝试过 List-comprehension 中的等价形式,结果是一样的

Good idea.好主意。 It so happens that for lists, do notation does exactly the same as list comprehensions.碰巧的是,对于列表, do表示法与列表推导式完全相同。 (In fact, there'sa syntactic extension that allows you to use list-comprehension notation for any monad, like you can use do notation for any monad.) (事实上,有一个语法扩展允许您对任何 monad 使用列表理解表示法,就像您可以对任何 monad 使用do表示法一样。)

So, you're asking why [a | a<-[0,1], b<-[2,3]]所以,你问为什么[a | a<-[0,1], b<-[2,3]] [a | a<-[0,1], b<-[2,3]] gives [0,0,1,1] instead of [0,1] . [a | a<-[0,1], b<-[2,3]]给出[0,0,1,1]而不是[0,1] The way this looks surprising is if you think about list comprehensions as set comprehensions like you'd find in maths.这看起来令人惊讶的方式是,如果您将列表推导视为集合推导,就像您在数学中发现的那样。 But lists aren't sets, though Haskellers do often use lists as a makeshift stand-in for sets.但是列表不是集合,尽管 Haskeller 经常使用列表作为集合的临时替代品。 If lists comprehensions acted as set comprehension, then如果列表推导作为集合推导,那么

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

should also yield only [0,1] as its result (or at least, it should yield the same result as [x|x<-[0,1]] does).也应该只产生[0,1]作为其结果(或者至少,它应该产生与[x|x<-[0,1]]相同的结果)。

In general this sort of weeding-out-duplicates requires equality checks, and if you want to make it efficient also either an ordering or a hashing method.一般来说,这种清除重复项需要相等性检查,如果你想让它高效,也可以使用排序或散列方法。 Lists don't do any such thing, so if you want set-like behaviour you should use a set-implementing data structure.列表不做任何这样的事情,所以如果你想要类似集合的行为,你应该使用集合实现数据结构。 Set and HashSet are the most common. SetHashSet是最常见的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM