Haskell中两个列表元素的所有组合

Posted

技术标签:

【中文标题】Haskell中两个列表元素的所有组合【英文标题】:All combinations of elements of two lists in Haskell 【发布时间】: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]] 【参考方案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']

【讨论】:

以上是关于Haskell中两个列表元素的所有组合的主要内容,如果未能解决你的问题,请参考以下文章

Haskell-将两个列表放入一个元组列表中

比较两个列表的某些元素,Haskell

比较列表haskell中的所有元素[重复]

Haskell 总结了通过树的所有路径

从 Prolog 到 Haskell 的思考——生成真值组合列表

具有两个参数的 Haskell 组合