【问题标题】:I am struggling to understand how concat is read in Haskell我正在努力理解如何在 Haskell 中读取 concat
【发布时间】:2021-05-10 00:03:47
【问题描述】:

concat的代码:

concat :: [[a]] -> [a]
concat xss = [x | xs <- xss, x <- xs]

我不明白它是如何被阅读的。 xss 如何拆分为 xsx?还是我看错了?

例如,给定:

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

[1,2,3,4,5]是如何实现的?

【问题讨论】:

  • 确实了解任何列表推导式吗?如果是的话,也许我们可以与他们进行类比,这将对您有所帮助;如果不是,那么下一步可能是查看许多优秀的 Haskell 教程之一,然后跳转到列表推导部分。
  • 或者,根据您的接线方式,您可能会发现the Report's section on desugaring 很有帮助。我也有一个答案here 给出了这些脱糖规则的工作示例。
  • 你好丹尼尔。我今天刚刚开始介绍列表推导,但是是的,我已经理解了我已经了解的所有其他内容,例如 factorsprimes - 只是 concat 令人困惑。有机会我会尽快阅读这两个链接,谢谢。

标签: list haskell concatenation list-comprehension


【解决方案1】:

身份很明显,

[ E | P <- (xs ++ ys), Q ]  ===  [ E | P <- xs, Q ] ++ [ E | P <- ys, Q ]

[ x | xs <- [E], x <- xs ]  ===  [ x | x <- E ]

[ x | x <- [E] ]  ===  [E]

我们可以按照代码,

concat        [[1,2,3],     [4], [5]]
 = {- by definition of `concat` -}
   [x | xs <- [[1,2,3],     [4], [5]], x <- xs]
 = {- by definition of `++`     -}
   [x | xs <- [[1,2,3]] ++ [[4], [5]], x <- xs]
 = {- by the first identity     -}
   [x | xs <- [[1,2,3]], x <- xs] ++ [x | xs <- [[4], [5]],  x <- xs]
 = {- by the second identity    -}
   [x | x <-   [1,2,3]          ] ++ [x | xs <- [[4], [5]],  x <- xs]
 = {- by definition of `++`     -}
   [x | x <-   [1,2,3]          ] ++ [x | xs <- [[4]] ++ [[5]], x <- xs]
 = {- by the first identity     -}
   [x | x <-   [1,2,3]] ++ [x | xs <- [[4]], x <- xs] ++ [x | xs <- [[5]], x <- xs]
 = {- by the second identity    -}
   [x | x <-   [1,2,3]] ++ [x | x <-   [4]          ] ++ [x | x <-   [5]          ]
 = {- and by the third          -}
   [x | x <-   [1,2,3]] ++ [            4           ] ++ [            5           ]
 = {- repeating as before       -}
   [x | x <- [1]] ++ [x | x <- [2]] ++ [x | x <- [3]] ++ [4] ++ [     5           ]
 = {- by the third identity     -}
   [          1 ] ++ [          2 ] ++ [          3 ] ++ [4] ++ [     5           ]
 = {- by definition of `++`     -}
   [          1   ,             2   ,             3   ,   4  ,        5           ]

因此concat,当应用于列表列表时,具有“打开”并消除其中的内括号的效果。

在这个过程中我们也发现了另一个身份,

[ x | x <- Q ]  ===  Q

从上面的那些和++的属性得出,其中

[A,B,C,...,Z]  ===  [A] ++ [B] ++ [C] ++ ... ++ [Z]

另见:

【讨论】:

    【解决方案2】:

    如果非要和命令式编程做个比较的话,我们可以想到

    [ expr | x1 <- list1 , x2 <- list2 , ....]
    

    作为一个嵌套的 for 循环,将 expr 产生的值累积到一个列表中,如下所示:

    result = []
    for x1 in list1:
      for x2 in list2:
        ...
        result.append(expr)
    

    在你的情况下,我们有

    result = []
    for xs in xss:
      for x in xs:
        result.append(x)
    

    所以,当xss = [[1,2,3],[4],[5]] 我们有:

    • xs = [1,2,3]
      • x = 1 被附加到结果中
      • x = 2 被附加到结果中
      • x = 3 被附加到结果中
    • xs = [4]
      • x = 4 被附加到结果中
    • xs = [5]
      • x = 5 被附加到结果中

    因此最终结果为[1,2,3,4,5]

    这种比较并不是一个完全真实的描述,因为它没有考虑惰性,并且 Haskell 并没有像上面那样通过将数据附加到可变列表来真正计算最终列表。也许 Python 的 yield 和生成器会更接近。不过,上面的比较应该能说明基本机制。

    【讨论】:

      【解决方案3】:

      在列表推导式中,a &lt;- b 位表示“对于 b 中的每个 a”。

      所以在你的情况下,xs &lt;- xss 应该读作“for each xs in xss”,然后x &lt;- xs 应该读作“for each xxs”中,这也是有效的,因为xs 本身是一个列表,因为xss 是一个列表的列表。

      因此,随着列表理解的展开,xs 首先绑定到 [1,2,3],然后绑定到 [4],然后绑定到 [5],并且在 xs 的每次迭代中,x 绑定到 @ 987654338@、23,然后是4,最后是5

      【讨论】:

      • 啊,这很有道理。我没有看到的是xs &lt;- xss 占据了列表列表的首位,这是第一个列表。然后x &lt;- xs 是该列表的头部,等等。完全有道理。谢谢你,费奥多尔 :)
      • xs &lt;- xss 以列表的头部开始,但它同样采用列表的每个元素。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-12
      • 2018-11-30
      • 2018-05-08
      • 1970-01-01
      • 2014-05-11
      • 1970-01-01
      • 2020-06-28
      相关资源
      最近更新 更多