2012-10-13 50 views
24

我需要将列表拆分为所有可能元组的列表,但我不确定如何去做。将列表拆分为可能的元组列表

例如

pairs ["cat","dog","mouse"] 

应导致

[("cat","dog"), ("cat","mouse"), ("dog","cat"), ("dog","mouse"), ("mouse","cat"), ("mouse","dog")]

我能组成第2,但我不确定如何得到休息。

这是我到目前为止有:

pairs :: [a] -> [(a,a)] 
pairs (x:xs) = [(m,n) | m <- [x], n <- xs] 

回答

24

您可以使用列表理解:

allpairs :: Eq a => [a] -> [(a,a)] 
allpairs xs = [ (x1,x2) | x1 <- xs, x2 <- xs, x1 /= x2 ] 
+0

谢谢!我完全忘记了列表理解。 – user1742646

2

另一种可能性是使用一元表示法:

pairs :: (Eq a) => [a] -> [(a,a)] 
pairs l = do 
    x <- l 
    y <- l 
    guard (x /= y) 
    return (x, y) 

(最pairs这个定义的一般类型将是(MonadPlus m, Eq a) => m a -> m (a,a),但我相信有没有MonadPlus的实例,而不是[],因此它是有意义的。)

+0

这是'guard'的一个很好的用法。 – AJFarmar

98

此答案分为两部分。第一部分直接解决问题。第二部分为了深入研究第一部分背后的数学问题,从切线(字面上)切入:它可能因此被证明是有限兴趣的难题材料,但我认为一些极端主义者可能会喜欢它。

我到目前为止看到整齐做出使用列表理解或他们的单子等同的答案,但他们使用平等排除重复,因此需要额外的Eq约束。这里有一个解决方案,它使得两个不同的职位中的所有元素对成对。首先,我写了一个方便的函数,它用一系列其他位置上的元素列表来装饰列表中的每个元素:所有“选择一个并离开其他位置”的方法。每当列表被用于收集东西以进行选择而不用替换时,这非常有用,而且我发现我使用了很多东西。

picks :: [x] -> [(x, [x])] 
picks [] = [] 
picks (x : xs) = (x, xs) : [(y, x : ys) | (y, ys) <- picks xs] 

注意map fst . picks = id,使结果的每个位置选定元是从原来的列表中位置的元素:这就是我的意思是“装饰”。

现在很容易选择两个,使用与其他答案相同的列表理解方法。但不是从列表本身中选择第一个组件,我们可以从其picks中进行选择,同时获取第二个组件的候选列表。

allPairs :: [x] -> [(x, x)] 
allPairs xs = [(y, z) | (y, ys) <- picks xs, z <- ys] 

这只是因为容易得到的三元组的保持,同时picks两次。

allTriples :: [x] -> [(x, x, x)] 
allTriples ws = [(x, y, z) | (x, xs) <- picks ws, (y, ys) <- picks xs, z <- ys] 

对于均匀性,它几乎是诱人,使代码略效率较低,写(z, _) <- picks ys而不是z <- ys两个。

如果输入列表没有重复项,则不会在输出中获得任何重复的元组,因为元组从其他位置获取其元素。然而,你会得到

Picks> allPairs ["cat", "cat"] 
[("cat","cat"),("cat","cat")] 

为了避免这种情况,可随时使用allPairs . nub,从而消除选择之前重复和需求再一次的Eq实例元素类型。


极端分子只:容器,微积分,comonads和组合啊嗬!

picks是由微分算法产生的更一般构造的一个实例。这是一个有趣的事实,对于任何给定的容器类型的函子f,其数学导数∂f代表f - 结构中删除了一个元素。例如,

newtype Trio x = Trio (x, x, x) -- x^3 

具有衍生物

data DTrio x = Left3 ((), x, x) | Mid3 (x,(), x) | Right3 (x, x,()) -- 3*x^2 

多个操作可以与该结构相关联。想象一下,我们真的可以使用∂(我们可以使用类型族进行编码)。然后我们可以说

data InContext f x = (:-) {selected :: x, context :: ∂f x} 

给出一种由上下文装饰的选定元素。我们当然应该期望有操作

plug :: InContext f x -> f x -- putting the element back in its place 

plug操作使我们向根如果我们zippering约在一棵树上其节点被视为子树的容器。

我们也应该期望InContext f是一个comonad,与

counit :: InContext f x -> x 
counit = selected 

伸出选定的元素和

cojoin :: InContext f x -> InContext f (InContext f x) 

装饰的每一个元素与它的背景下,显示出所有可能的方式你可以再聚焦,选择一个不同的元素。

不可估量的彼得汉考克曾经向我建议,我们也应该期望能够“向下”(意思是“远离根部”),收集所有可能的方法来从上下文中选择元素上下文整个结构。

picks :: f x -> f (InContext f x) 

应该装点每x - 元素在输入f - 结构与它的上下文。我们应该预料到

fmap selected . picks = id 

这就是我们前面有规律,也是

fmap plug (picks fx) = fmap (const fx) fx 

告诉我们,每一个装饰元素是原始数据的分解。我们上面没有这个法律。我们有

picks :: [x] -> [(x, [x])] 

装饰的每一个元素不够的东西有点像它的背景:从刚才的其他元素的列表中,你看不到的地方“洞”是。实际上,

∂[] x = ([x], [x]) 

将孔之前的元素列表与孔之后的元素分开。可以说,我应该写

picks :: [x] -> [(x, ([x], [x]))] 
picks [] = [] 
picks (x : xs) = (x, ([], xs)) : [(y, (x : ys, ys')) | (y, (ys, ys')) <- picks xs] 

这也是一个非常有用的操作。

但真正发生的事情是相当明智的,只是轻微的滥用。在我最初编写的代码中,我在本地以[]表示有限包无序列表。包包是没有特定位置概念的列表,所以如果选择一个元素,其上下文就是剩余元素的包。事实上

∂Bag = Bag -- really? why? 

所以picks权概念确实

picks :: Bag x -> Bag (x, Bag x) 

通过[]代表Bag,这就是我们有什么。此外,对于行李,plug只是(:),并且,直到行李相等(即排列),第二定律为 确实是成立。

看袋子的另一种方式是作为电源系列。一个包是任意大小的元组的选择,具有所有可能的排列组合(n!,大小为n)。因此,我们可以将它组合地写成由因式分解的大量权力,因为您必须用x^n除以n!为了说明每个n!您可以选择x的订单为您提供相同的包。

Bag x = 1 + x + x^2/2! + x^3/3! + ... 

所以

∂Bag x = 0 + 1 + x  + x^2/2! + ... 

侧向移位系列。事实上,你可能已经认识到Bag的幂级数为exp(或^x),这是因其本身的衍生而闻名。

因此,唷!你走了。自然而然地由指数函数的数据类型解释产生的操作(它自己的衍生物)是用于解决基于选择而无需替换的问题的便利工具包。

+14

很棒的回答。人们还应该看到Brent Yorgey的[博客文章](http://byorgey.wordpress。com/2012/08/24/unordered-tuples-and-type-algebra /),他描述了Set的类型,它们是不允许重复的Bag。这句妙话:而不是exp函数(满足∂f= f),你可以得到一个函数g,它由满足Δg= g的下降因子构成,其中Δ是*有限差分*而不是导数(也可以参见本文[sigfpe post ](http://blog.sigfpe.com/2009/09/finite-differences-of-types.html),通过引用@ pigworker的论文来完成该圈子!) –

2
import Control.Applicative 

pairs xs = filter (uncurry (/=)) $ (,) <$> xs <*> xs 
5

我的方法与其他方法有些相似。它不需要Eq

allpairs :: [t] -> [(t,t)] 
allpairs [] = [] 
allpairs [_] = [] 
allpairs (x:xs) = concatMap (\y -> [(x,y),(y,x)]) xs ++ allpairs xs 
2
pairs = (filter.uncurry) (/=) . (join.liftA2) (,)