2012-10-13 46 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) (,)