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
函数、>>=
、<$>
和<*>
运算符方面进行了各种重写。
尽管这是Applicative
的概念——最好命名为Pairing
——因为这是两个“容器”⁄“载体”⁄其他元素的配对 正是 Applicative Functor 的意义所在。碰巧 Haskell 的 do
表示法适用于 monad,而不是 (yet) for applicatives。
在某种意义上编译时嵌套循环是适用性/配对仿函数; Monad 添加了动态创建嵌套循环的能力,具体取决于“外部”枚举产生的值。
【讨论】:
值得注意的是,do
-notation 和列表推导实际上都是相同单子组合子的语法糖,即[a,b] >>= \x -> [c,d] >>= \y -> return (x,y)
。
@leftaroundabout,这根本不是真的。列表推导在 desugarer 中没有一种而是两种处理方式,它们都不会产生 >>=
或 return
。
确实;需要启用-XMonadComprehensions
,因此列表推导实际上使用>>=
和return
。如果没有该扩展,它们将使用等效的特定于列表的功能。【参考方案4】:
最直观的方法是使用列表推导,其他方法包括使用应用函子:
(,) <$> [1,2,3] <*> [4,5,6]
那么这有什么作用呢?
记住(,) :: a -> b -> (a, b)
接受两个参数并返回一个元组。
<$>
实际上是 fmap,(<$>) :: Functor f => (a -> b) -> f a -> f b
它需要一个函数并将其提升。在这种情况下,它需要(,)
并将其提升到列表中。所以let x = (,) <$> [1,2]
会生成x :: [b -> (Integer, b)]
,它是接受b
并返回带有一个固定参数(整数,b)的元组的函数列表。最后我们使用<*>
应用它来生成所有的组合。
【讨论】:
【参考方案5】:使用列表理解:
s = [a,b]
s' = [c,d]
all_combinations = [(x,y) | x <- s, y <- s']
【讨论】:
以上是关于Haskell中两个列表元素的所有组合的主要内容,如果未能解决你的问题,请参考以下文章