Haskell无限列表的笛卡尔积

时间:2011-08-21 21:27:06

标签: haskell

我想从基础对生成一个向量空间,它看起来像:

genFromPair (e1, e2) = [x*e1 + y*e2 | x <- [0..], y <- [0..]]

当我检查输出时,它就像我得到[0, e2, 2*e2,...](即x永远不会超过0)。当我考虑如何编写代码来执行此列表理解时,哪种有意义。

我写了一些代码来从原点扩展“shell”(首先是0的整数,然后是norm 1,然后是norm 2 ......)但是这有点烦人且特定于Z ^ 2 - I我必须为Z ^ 3或Z [i]等重写它。有更清洁的方法吗?

4 个答案:

答案 0 :(得分:12)

data-ordlist包具有一些功能,这些功能对于处理已排序的无限lits非常有用。其中一个是mergeAllBy,它使用一些比较函数组合了无限列表的无限列表。

然后想法建立一个无限的列表列表,以便y在每个列表中得到修复,而x增长。只要我们可以保证每个列表都被排序,并且列表的头部被排序,根据我们的排序,我们会得到一个合并的排序列表。

这是一个简单的例子:

import Data.List.Ordered
import Data.Ord

genFromPair (e1, e2) = mergeAllBy (comparing norm) [[x.*e1 + y.*e2 | x <- [0..]] | y <- [0..]]

-- The rest just defines a simple vector type so we have something to play with
data Vec a = Vec a a
    deriving (Eq, Show)

instance Num a => Num (Vec a) where
    (Vec x1 y1) + (Vec x2 y2) = Vec (x1+x2) (y1+y2)
    -- ...

s .* (Vec x y) = Vec (s*x) (s*y)     
norm (Vec x y) = sqrt (x^2 + y^2)

在GHCi中尝试这个,我们得到了预期的结果:

*Main> take 5 $ genFromPair (Vec 0 1, Vec 1 0)
[Vec 0.0 0.0,Vec 0.0 1.0,Vec 1.0 0.0,Vec 1.0 1.0,Vec 0.0 2.0]

答案 1 :(得分:4)

你可以把你的空间看成一棵树。在树的根部选择第一个元素,在其子元素中选择第二个元素。

这是使用ListTree包定义的树:

import Control.Monad.ListT
import Data.List.Class
import Data.List.Tree
import Prelude hiding (scanl)

infiniteTree :: ListT [] Integer
infiniteTree = repeatM [0..]

spacesTree :: ListT [] [Integer]
spacesTree = scanl (\xs x -> xs ++ [x]) [] infiniteTree

twoDimSpaceTree = genericTake 3 spacesTree

这是一个无限的树,但我们可以枚举它,例如以DFS顺序:

ghci> take 10 (dfs twoDimSpaceTree)
[[],[0],[0,0],[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7]]

在树语中,您想要的顺序是无限树的最佳优先搜索的变体,其中假设树节点的子节点被排序(您无法比较所有节点的子节点,就像在正常情况下一样)最好的第一次搜索,因为有无数的那些)。幸运的是,这个变种已经实现了:

ghci> take 10 $ bestFirstSearchSortedChildrenOn sum $ genericTake 3 $ spacesTree
[[],[0],[0,0],[0,1],[1],[1,0],[1,1],[0,2],[2],[2,0]]

您可以使用您喜欢的任何规范来扩展shell,而不是上面的sum

答案 2 :(得分:2)

使用CodeCatalog中的diagonal代码段:

genFromPair (e1, e2) = diagonal [[x*e1 + y*e2 | x <- [0..]] | y <- [0..]]

diagonal :: [[a]] -> [a]
diagonal = concat . stripe
    where
    stripe [] = []
    stripe ([]:xss) = stripe xss
    stripe ((x:xs):xss) = [x] : zipCons xs (stripe xss)

    zipCons [] ys = ys
    zipCons xs [] = map (:[]) xs
    zipCons (x:xs) (y:ys) = (x:y) : zipCons xs ys

答案 3 :(得分:1)

对hammar的回复进行抄袭:他的方法似乎很容易扩展到更高的维度:

Prelude> import Data.List.Ordered
Prelude Data.List.Ordered> import Data.Ord
Prelude Data.List.Ordered Data.Ord> let norm (x,y,z) = sqrt (fromIntegral x^2+fromIntegral y^2+fromIntegral z^2)
Prelude Data.List.Ordered Data.Ord> let mergeByNorm = mergeAllBy (comparing norm)
Prelude Data.List.Ordered Data.Ord> let sorted = mergeByNorm (map mergeByNorm [[[(x,y,z)| x <- [0..]] | y <- [0..]] | z <- [0..]])
Prelude Data.List.Ordered Data.Ord> take 20 sorted
[(0,0,0),(1,0,0),(0,1,0),(0,0,1),(1,1,0),(1,0,1),(0,1,1),(1,1,1),(2,0,0),(0,2,0),(0,0,2),(2,1,0),(1,2,0),(2,0,1),(0,2,1),(1,0,2),(0,1,2),(2,1,1),(1,2,1),(1,1,2)]