15

I want to generate a vectorspace from a basis pair, which looks something like:

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

When I examine the output though, it sems like I'm getting [0, e2, 2*e2,...] (i.e. x never gets above 0). Which sort of makes sense when I think about how I would write the code to do this list comprehension.

I wrote some code to take expanding "shells" from the origin (first the ints with norm 0, then with norm 1, then norm 2...) but this is kind of annoying and specific to Z^2 - I'd have to rewrite it for Z^3 or Z[i] etc. Is there a cleaner way of doing this?

Rodrigo de Azevedo
  • 1,097
  • 9
  • 17
Xodarap
  • 11,581
  • 11
  • 56
  • 94
  • It's like showing that the rationals are countable. Instead of going (1,1), (1,2), (1,3), ..., (2,1), ..., which never gets you to the second row, increment by fixed sums: (1,1); (1,2), (2,1); (1,3), (2,2), (3,1); ... – Kerrek SB Aug 21 '11 at 21:30
  • @Kerrek SB: thanks, this is what I was trying to indicate by saying I took "expanding shells". I was just wondering if there was a haskelly way of doing it. – Xodarap Aug 21 '11 at 21:32
  • 2
    I don't know Haskell, but in C it'd be a double loop: `for (sum = 1; ; ++sum) for (i = 1; i < sum; ++i) print (i, sum - i);` – Kerrek SB Aug 21 '11 at 21:34
  • 8
    @Kerrek: In Haskell: `[(x, s-x) | s <- [0..], x <- [0..s]]` – hammar Aug 21 '11 at 22:09
  • @hammar: Thanks - surprisingly self-descriptive :-) – Kerrek SB Aug 21 '11 at 22:16

4 Answers4

12

The data-ordlist package has some functions which are extremely useful for working with sorted infinite lits. One of these is mergeAllBy, which combines an infinite list of infinite lists using some comparison function.

The idea is then to build an infinite list of lists such that y is fixed in each list, while x grows. As long as we can guarantee that each list is sorted, and that the heads of the lists are sorted, according to our ordering, we get a merged sorted list back.

Here's a quick example:

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)

Trying this in GHCi we get the expected result:

*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]
hammar
  • 138,522
  • 17
  • 304
  • 385
  • Isn't Manhattan norm a little bit more efficient? ;) – Rotsor Aug 22 '11 at 01:10
  • @Rotsor: Probably :) But this was just a toy example, you can use any ordering you'd like as long as the lists and heads of the lists obey that order. – hammar Aug 22 '11 at 01:12
  • 2
    With a continuation monad and monad comprehensions this can be written almost like the original code: https://gist.github.com/1162126 – Sjoerd Visscher Aug 22 '11 at 10:52
4

You could look at your space as a tree. At the root of the tree one picks the first element and in its child you pick the second element..

Here's your tree defined using the ListTree package:

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

It's an infinite tree, but we could enumerate over it for example in DFS order:

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

The order you want, in tree-speak, is a variant of best-first-search for infinite trees, where one assumes that the children of tree nodes are sorted (you can't compare all the node's children as in normal best-first-search because there are infinitely many of those). Luckily, this variant is already implemented:

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

You can use any norm you like for your expanding shells, instead of sum above.

yairchu
  • 23,680
  • 7
  • 69
  • 109
2

Using the diagonal snippet from CodeCatalog:

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
Landei
  • 54,104
  • 13
  • 100
  • 195
1

Piggybacking on hammar's reply: His approach seems fairly easy to extend to higher dimensions:

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)]
dainichi
  • 2,231
  • 1
  • 13
  • 12