我有两个(可能无限的)列表[a]
和[b]
。我想构建一个包含所有对[(a, b)]
的(可能是无限的)列表,每个对只包含一次。这似乎应该是Haskell的小巷,但我被卡住了。
我从理性的经典对角化开始:
pairs :: [a] -> [b] -> [(a, b)]
pairs x y =
let diag (lhs, rhs) = zip lhs $ reverse rhs
in concatMap diag $ zip (inits x) (inits y)
这通过“枚举对角线:”它从两个列表中取出(例如)长度4的初始子序列,将其中一个反转,然后将它们拉到一起。对所有长度的所有初始子序列重复,并连接所有结果。当两个列表都是无限的时,这都有效。
但是,如果任一列表是有限的,则会失败。问题是,一旦我们到达最长的对角线,我们就会停止,因此不会枚举网格的右下角三角形。例如inits [1, 2]
会产生[[1], [1, 2], [2, 1]]
,我们永远不会枚举值(2, 2)
。
所以我脱口而出一个函数来生成前向和后向对角线:
inits_bidir :: [a] -> [[a]]
inits_bidir [] = []
inits_bidir x =
let forwards = tail $ inits x -- skip empty
backwards = map reverse $ (tail . reverse . tail . inits . reverse) x
in forwards ++ backwards
pairs :: [a] -> [b] -> [(a, b)]
pairs x y =
let diag (lhs, rhs) = zip lhs $ reverse rhs
in concatMap diag $ zip (inits_bidir x) (inits_bidir y)
示例:
> inits_bidir [1..3]
[[1],[1,2],[1,2,3],[2,3],[3]]
现在,如果两个列表的长度相同(可能是无限长),则此方法有效。但如果一个列表比另一个列表长,它就会失败,因为我们仍然枚举一个正方形的对角线,但我们的矩阵是矩形的。
在我深入挖掘自己之前,还有更好的方法可以解决这个问题吗?