【问题标题】:Why >> duplicates right-hand side operand in haskell为什么>>在haskell中重复右侧操作数
【发布时间】:2022-01-21 13:57:29
【问题描述】:

我正在寻找重复字符串n 次的解决方案。我从这个solution 中找到了duplicate n str = [1..n] >> str。 我想知道为什么这种方法会重复 str

我搜索了>>,我发现了这一点:

k >> f = k >>= \_ -> f

a >> b >> c >> d
-- is is equivalent to
do a
   b
   c
   d

那我试试这个

ghci> do [1..3]; "a"
"aaa"

但我仍然不明白它是如何工作的。谁能解释这种行为?

【问题讨论】:

    标签: haskell monads do-notation


    【解决方案1】:

    >>= 对列表的定义是concatMap:对列表的每个元素应用一个函数,然后将结果连接起来。当您使用>> 时,这意味着应用一个函数忽略列表中的每个元素,而是返回>> 右侧的常量值

    [1..3] >> "a" == [1..3] >>= \_ -> "a"
                  == concatMap (\_ -> "a") [1..3]
                  == "a" ++ "a" ++ "a"
                  == "aaa"
    

    【讨论】:

    • 非常感谢,如果我们不知道 >>= 是 concatMap 会很混乱。
    • 好吧,如果你想有效地使用列表单子,你应该知道(>>=)是如何在列表上工作的。
    【解决方案2】:

    根据单子定律,

    a >> b >> c >> d
    -- is equivalent to
    --                                        (values)   (computations)
    do a             do {      a            do {  _x   <-     a
       b                ;      b               ;  _y   <-     b
       c                ;      c               ;  _z   <-     c
       d                ; r <- d               ;   r   <-     d
                        ; return r             ;   return r
                        }                      }
    

    并用 Monad Comprehensions 编写它——这对于列表而言,非巧合地,与 List Comprehensions 完全相同——它变成了

        [ r | _x <- a,  _y <- b,  _z <- c,  r <- d ]
    

    这意味着,在伪代码中,

    for each _x in a:             for each _x in a:
      for each _y in b:             for each _y in b:
        for each _z in c:             for each _z in c:
          for each r in d:              do  d
             do  yield r
    

    所以我们只是“做”同样的d,一遍又一遍,以嵌套的方式上面操作的每个结果组合

    对于列表,这意味着将d 的元素重复拼接到结果列表中。

    在某种意义上,广义(嵌套)循环是(monads)/applicative/functors,描述了从最深层次产生最终结果的组合计算: (*)

      Functor                Applicative                  Monad
        
    for x in a:             for x in a                for x in a:
      do  yield (foo x)     and y in b:                 for y in (bar x):
                              do  yield (foo x y)         do  yield (foo x y)
    
      "loop"                  "loops"                    "nested loops"
                                                       (created on the fly)
    

    (*) 无论有多少层,从整体上看,“循环”组合都会一个接一个地产生结果,就像每个“循环”在分离中产生它的枚举一样,逐个。它是如此微不足道,我们甚至在命令式循环中都不会注意到它。

    每种特定类型的 Functor 赋予上述“for”、“in”和“do yield”含义。对于列表,这意味着在结果中进行拼接,通过concat/concatMap 实现。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-05-26
    • 2020-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-29
    • 1970-01-01
    相关资源
    最近更新 更多