其他答案假設兩個輸入列表是有限的。通常,慣用的Haskell代碼包含無限列表,所以如果需要的話,如何生成無限笛卡兒積是值得簡短評論的。
標準方法是使用對角化;寫的頂端和沿着左邊的另一個輸入的一個輸入,我們可以編寫一個包含完整的笛卡爾乘積像這樣的二維表:
1 2 3 4 ...
a a1 a2 a3 a4 ...
b b1 b2 b3 b4 ...
c c1 c2 c3 c4 ...
d d1 d2 d3 d4 ...
. . . . . .
. . . . . .
. . . . . .
當然,在任何單行工作將給我們無限元素在到達下一行之前;同樣,按專欄分類將是災難性的。但是我們可以沿着對角線向左下方走,每當我們到達網格邊緣時,再往右開一點。
a1
a2
b1
a3
b2
c1
a4
b3
c2
d1
...等等。爲了,這將給我們:
a1 a2 b1 a3 b2 c1 a4 b3 c2 d1 ...
要在Haskell代碼這一點,我們可以先寫的版本產生的二維表:
cartesian2d :: [a] -> [b] -> [[(a, b)]]
cartesian2d as bs = [[(a, b) | a <- as] | b <- bs]
角化的低效的方法是再首先沿着對角線迭代,然後沿着每個對角線的深度迭代,每次都拉出適當的元素。爲了簡化說明,我假設兩個輸入列表都是無限的,所以我們不必亂用邊界檢查。
diagonalBad :: [[a]] -> [a]
diagonalBad xs =
[ xs !! row !! col
| diagonal <- [0..]
, depth <- [0..diagonal]
, let row = depth
col = diagonal - depth
]
這個實現是有點可惜:重複列表索引操作!!
變得越來越像我們走得更貴,給人相當糟糕的漸近性能。更高效的實現將採用上述想法,但使用拉鍊實現它。所以,我們會分散我們的無限電網分爲三個形狀是這樣的:
a1 a2/a3 a4 ...
/
/
b1/b2 b3 b4 ...
/
/
/
c1 c2 c3 c4 ...
---------------------------------
d1 d2 d3 d4 ...
. . . . .
. . . . .
. . . . .
左上方的三角形將是我們已經發出的位;右上方的四邊形將是部分排出的行,但仍會對結果作出貢獻;底部的矩形將是我們尚未開始發射的行。首先,上三角形和上四邊形將是空的,底部矩形將是整個網格。在每一步中,我們可以發出上四邊形中每行的第一個元素(基本上將斜線移動一個),然後從底部矩形向上四邊形添加一行(基本上將水平線向下移動一個)。
diagonal :: [[a]] -> [a]
diagonal = go [] where
go upper lower = [h | h:_ <- upper] ++ case lower of
[] -> concat (transpose upper')
row:lower' -> go (row:upper') lower'
where upper' = [t | _:t <- upper]
雖然這看起來有點複雜,但它顯着更高效。它還處理我們在簡單版本中打出的邊界檢查。
但是你不應該自己編寫所有的代碼,當然!相反,你應該使用universe包。在Data.Universe.Helpers
,有(+*+)
,這包上面cartesian2d
和diagonal
功能一起給剛笛卡爾積操作:
Data.Universe.Helpers> "abcd" +*+ [1..4]
[('a',1),('a',2),('b',1),('a',3),('b',2),('c',1),('a',4),('b',3),('c',2),('d',1),('b',4),('c',3),('d',2),('c',4),('d',3),('d',4)]
您還可以看到對角線自己,如果這個結構變成有用:
Data.Universe.Helpers> mapM_ print . diagonals $ cartesian2d "abcd" [1..4]
[('a',1)]
[('a',2),('b',1)]
[('a',3),('b',2),('c',1)]
[('a',4),('b',3),('c',2),('d',1)]
[('b',4),('c',3),('d',2)]
[('c',4),('d',3)]
[('d',4)]
如果您有許多產品清單可供您一起使用,則迭代(+*+)
會不公平地偏倚某些清單;您可以使用choices :: [[a]] -> [[a]]
來滿足您的n維笛卡爾產品需求。
謝謝你,這麼簡單但優雅,真的幫助其他問題:) – 2010-11-08 09:20:14
好,也爲Erlang,謝謝。語法變化不大: cartProd(Xs,Ys) - > [{X,Y} || X < - Xs,Y < - Ys]。 – Dacav 2011-10-31 11:18:33