【问题标题】:All combinations of elements of two lists in HaskellHaskell中两个列表元素的所有组合
【发布时间】:2015-11-12 15:49:19
【问题描述】:

给定两个列表,[a, b] 和 [c, d],我想得到以下结果:

[(a,c), (a,d), (b,c), (b,d)]

如何在 Haskell 中做到这一点?是否有一个内置函数,或者我应该自己实现一个?

【问题讨论】:

  • 当[a, b]和[c, d]的类型相等时,可以写成sequence [[a, b], [c, d]]。

标签: list haskell tuples combinations


【解决方案1】:
[ (x,y) | x<-[a,b], y<-[c,d] ]

这真的不需要任何进一步的解释,不是吗?

【讨论】:

  • 不,确实不是,谢谢;我只是想知道是否有一个内置函数可以做到这一点;类似combos xs ys 的东西会产生相同的结果。
  • @Ben:combos = liftA2(,),基本相当于Jubobs的建议。
  • @leftaroundabout 非常好的应用程序!
【解决方案2】:

一路应用风格!

λ> :m + Control.Applicative
λ> (,) <$> ['a','b'] <*> ['c','d']
[('a','c'),('a','d'),('b','c'),('b','d')]

(我避开了上面的任何String 语法糖,以便与您的示例保持接近。)

有关信息,(,) 是一个函数的特殊语法,它接受两个参数并用它们组成一对:

λ> :t (,)
(,) :: a -> b -> (a, b)

编辑:正如leftaroundabout 在his comment 中所指出的,您也可以使用liftA2:

λ> :m + Control.Applicative
λ> let combine = liftA2 (,)
λ> combine "ab" "cd"
[('a','c'),('a','d'),('b','c'),('b','d')]

【讨论】:

    【解决方案3】:

    如何在命令式伪代码中做到这一点?

    for each element x in [a,b]:
        for each element y in [c,d]:
            produce (x,y)
    

    在 Haskell 中,这写为

    outerProduct xs ys =
       do
           x <- xs          -- for each x drawn from xs:
           y <- ys          --   for each y drawn from ys:
           return (x,y)     --      produce the (x,y) pair
    

    (通过 leftaroundabout 跟随 cmets) 这当然 非常 接近于 liftM2 monadic 组合器的定义方式,所以事实上

    outerProduct = liftM2 (,)
    

    这与liftA2 (,) 相同,它在列表推导、concatMap 函数、&gt;&gt;=、&lt;$&gt; 和&lt;*&gt; 运算符方面进行了各种重写。

    尽管这是Applicative 的概念——最好命名为Pairing——因为这是两个“容器”⁄“载体”⁄其他元素的配对 正是 Applicative Functor 的意义所在。碰巧 Haskell 的 do 表示法适用于 monad,而不是 (yet) for applicatives。

    在某种意义上编译时嵌套循环是适用性/配对仿函数; Monad 添加了动态创建嵌套循环的能力,具体取决于“外部”枚举产生的值。

    【讨论】:

    • 值得注意的是,do-notation 和列表推导实际上都是相同单子组合子的语法糖,即[a,b] &gt;&gt;= \x -&gt; [c,d] &gt;&gt;= \y -&gt; return (x,y)。
    • @leftaroundabout,这根本不是真的。列表推导在 desugarer 中没有一种而是两种处​​理方式,它们都不会产生 &gt;&gt;= 或 return。
    • 确实;需要启用-XMonadComprehensions,因此列表推导实际上使用&gt;&gt;= 和return。如果没有该扩展,它们将使用等效的特定于列表的功能。
    【解决方案4】:

    最直观的方法是使用列表推导,其他方法包括使用应用函子:

    (,) <$> [1,2,3] <*> [4,5,6]
    

    那么这有什么作用呢?

    记住(,) :: a -&gt; b -&gt; (a, b) 接受两个参数并返回一个元组。

    &lt;$&gt; 实际上是 fmap,(&lt;$&gt;) :: Functor f =&gt; (a -&gt; b) -&gt; f a -&gt; f b 它需要一个函数并将其提升。在这种情况下,它需要(,) 并将其提升到列表中。所以let x = (,) &lt;$&gt; [1,2] 会生成x :: [b -&gt; (Integer, b)],它是接受b 并返回带有一个固定参数(整数,b)的元组的函数列表。最后我们使用&lt;*&gt; 应用它来生成所有的组合。

    【讨论】:

      【解决方案5】:

      使用列表理解:

      s = [a,b]
      s' = [c,d]
      
      all_combinations = [(x,y) | x <- s, y <- s']
      

      【讨论】:

        猜你喜欢
        • 2021-04-22
        • 1970-01-01
        • 1970-01-01
        • 2021-03-02
        • 2023-03-09
        • 1970-01-01
        • 2021-11-01
        • 1970-01-01
        • 2017-12-14
        相关资源
        最近更新 更多