【问题标题】:How does `[ (x !! 0, x !! 1) | x <- mapM (const ['A', 'B', 'C'] ) [1..2], head x < head (tail x) ]` work?`[ (x !! 0, x !! 1) | x <- mapM (const ['A', 'B', 'C'] ) [1..2], head x < head (tail x) ]` 有用吗?
【发布时间】:2020-05-05 18:48:46
【问题描述】:

我是 Haskell 的新手,想知道声明是如何的

[ (x !! 0, x !! 1) | x <- mapM (const ['A', 'B', 'C'] ) [1..2], head x < head (tail x) ]

有效。 (我在 StackOverflow 上找到它。)我知道它输出什么,但我并不真正理解它。

【问题讨论】:

  • 我认为你“开始太高了”。上面的例子需要对 monadsmapM 使用)有基本的了解,以及列表是如何成为 monads 的。此外,它也很丑陋,而且不是真正地道的 Haskell。
  • 我同意,您有其他解决方案吗?我需要输出[('A', 'B'), ('A', 'C'), ('B', 'C')] 作为输入['A', 'B', 'C']
  • 所以您正在寻找独特的组合。你可以看这里:stackoverflow.com/questions/52602474/…
  • 你说,你是在 StackOverflow 上找到的,你能编辑你的帖子并添加链接吗?

标签: haskell list-comprehension


【解决方案1】:

上面的表达式可能不是 Haskell 所认为的 惯用。可能更好的版本是:

[ <b>(x0, x1)</b> | <b>(x0:x1:_)</b> &lt;- mapM (const "ABC") [1..2], <b>x0 &lt; x1</b> ]

这样更简洁,如果mapM (const "ABC") 中的列表将返回包含少于两个元素的列表(这在此处是不可能的),则不会出错。

这里的核心问题可能是了解mapM 的工作原理。表达式mapM (const "ABC") [1..2] 归结为:

mapM (\_ -&gt; "ABC") [1..2]

由于我们不考虑列表的值,所以相当于:

replicateM 2 "ABC"

2replicateM 可以重写为(伪 Haskell 语法):

replicateM 2 :: [a] -< [[a]]
replicateM 2 l = do
    x0 <- l
    x1 <- l
    return [x0, x1]

或者如果我们去糖:

replicateM 2 l = l &gt;&gt;= \x0 -&gt; l &gt;&gt;= \x1 -&gt; return [x0, x1]

对于列表,Monad 的实例实现为:

instance Monad [] where
    return x = [x]
    (>>=) = flip concatMap

这意味着对于列表,此replicateM 2 实现为:

replicateM 2 l :: [a] -> [[a]]
replicateM 2 l = concatMap (\x0 -> concatMap (\x1 -> [[x0, x1]]) l) l

或更简单:

replicateM 2 l :: [a] -> [[a]]
replicateM 2 l = concatMap (\x0 -> map (\x1 -> [x0, x1]) l) l

因此,我们对列表中的两个项目进行了所有可能的组合。这意味着:

Prelude Control.Monad> replicateM 2 "ABC"
["AA","AB","AC","BA","BB","BC","CA","CB","CC"]

然后我们在列表推导中使用它,对于每个包含两个元素的子列表,我们检查第一个元素 x0 是否小于列表中具有 filter 部分的第二个元素理解(x0 &lt; x1)。如果是这种情况,我们将这些元素生成为 2 元组。

因此,如果第一个元素(严格)小于第二个元素,则它为 "ABC" 中的每两个项目创建 2 元组。

然而,在这里我们做了一些“太多的工作”:超过一半的元素将被拒绝。我们可以通过使用tails :: [a] -&gt; [[a]]来优化它:

import Data.List(tails)

[(x0, x1) | (x0:xs) <- tails "ABC", x1 <- xs ]

产生相同的值:

Prelude Control.Monad Data.List> [(x0, x1) | (x0:xs) <- tails "ABC", x1 <- xs ]
[('A','B'),('A','C'),('B','C')]

【讨论】:

    【解决方案2】:

    忽略原始代码如何工作的问题,直接跳到如何更惯用地编写此代码,以 myList = ['A', 'B', 'C'] 开头。

    您可以获得所有个可能对的列表;选择一个x,选择一个y,并将它们放在一个元组中:

    > [ (x, y) | x <- myList, y <- myList ]
    [('A','A'),('A','B'),('A','C'),('B','A'),('B','B'),('B','C'),('C','A'),('C','B'),('C','C')]
    

    但你只想要x &lt; y:

    > [ (x, y) | x <- myList, y <- myList, x < y ]
    [('A','B'),('A','C'),('B','C')]

    【讨论】:

      【解决方案3】:

      根据mapM f xs = sequence (map f xs)的定义重写,我们得到

      [ (x !! 0, x !! 1) | x <- mapM (const ['A', 'B', 'C'] ) [1..2], head x < head (tail x) ]
      =
      [ (a, b) | [a,b] <- mapM (const "ABC" ) [1,2], a < b ]
      =
      [ (a, b) | [a,b] <- sequence ["ABC" | _ <- [1,2]], a < b ]
      =
      [ (a, b) | [a,b] <- sequence ["ABC", "ABC"], a < b ]
      =
      [ (a, b) | [a,b] <- [[a,b] | a <- "ABC", b <- "ABC"], a < b ]   -- by def of sequence
      =
      [ (a, b) | a <- "ABC", b <- "ABC", a < b ]                 -- by associativity of (++)
      =
      [ (a, b) | a <- "ABC", b <- [b | b <- "ABC", b > a] ]      -- by associativity of (++)
      =
      [ (a, b) | a <- "A", b <- [b | b <- "ABC", b > a] ]
         ++
         [ (a, b) | a <- "B", b <- [b | b <- "ABC", b > a] ]
           ++
           [ (a, b) | a <- "C", b <- [b | b <- "ABC", b > a] ]
      =
      [ (a, b) | a <- "A", b <- [b | b <- "BC"] ]                -- by pre-evaluation
         ++
         [ (a, b) | a <- "B", b <- [b | b <- "C"] ]
           ++
           [ ]
      =
      [ (a, b) | a <- "A", b <- "BC" ]
         ++
         [ (a, b) | a <- "B", b <- "C" ]
      =
      [ (a, b) | (a:bs) <- ["ABC", "BC"], b <- bs ]
      

      看到了吗? List comprehensions are fun。你也可以玩这个游戏,然后通过这种方式找到答案。

      Monadic do 表示法也很有趣,可以写成 Monad Comprehensions,对于列表看起来和 List Comprehensions 完全相同。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-08
        • 1970-01-01
        • 2017-01-19
        相关资源
        最近更新 更多