2

I have two things for the desired infinite list: its first element

x :: A

and function which generates the next element

f :: [A] -> A

What's the best (most idiomatic? fastest?) way to create infinite list? I mean

xs = x : f [x] : f [x, f [x]] : f [x, f [x], f [x, f [x]]] : ...
aplavin
  • 2,199
  • 5
  • 32
  • 53
  • Are you searching for the [unfoldr](http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data-List.html#v:unfoldr) function? – fho Oct 13 '12 at 17:25
  • unfoldr has type `unfoldr :: (b -> Maybe (a, b)) -> b -> [a]`, so you mean returning "current" whole list as `b` and next single element as `a`? Thanks, this really seems to work =) – aplavin Oct 13 '12 at 17:30
  • But is there a more straightforward way? – aplavin Oct 13 '12 at 17:30
  • Do you need the whole list in every iteration? (This will slow down alot.) Or is a accumulator value sufficient? Then there is another function called [mapAccumL/R](http://hackage.haskell.org/packages/archive/base/latest/doc/html/Data-List.html#v:mapAccumL) that may help you. – fho Oct 13 '12 at 17:34
  • 2
    `infiniteList = x:map f (tail $ inits infiniteList)` where `x` is the first element and `f` is the function to be applied to each prefix. – is7s Oct 13 '12 at 17:37
  • @Florian: yes, the function needs the whole list (more exactly, it needs some elements from the list, but they can be located randomly). – aplavin Oct 13 '12 at 17:37
  • @is7s: oh, really nice method, and no function signature change needed :) – aplavin Oct 13 '12 at 17:39
  • @is7s: would you put this as an answer? And please add a note about its performance, through I don't need a speed up now, it's never a bad thing. – aplavin Oct 13 '12 at 17:48
  • 2
    You can use iterate: `map head $ iterate (\l -> f l : l) [x]`. Note that `l` is reversed (`l` is the list of previous elements), so maybe `\l -> f (reverse l) : l`. – huon Oct 13 '12 at 17:51
  • To make this a little bit mathematically cleaner, forget `x` and just use `f`. The first element is then the result of `f []`. Then in `constructInf` you no longer need the `tail`. I also suspect, based on intuition, that your `f` will be simpler in this case as well. – luqui Oct 13 '12 at 19:14
  • @luqui: probably a good idea in general case, but not in mine: the first element is special and can't be simply generate by the same function `f` as other elements. – aplavin Oct 14 '12 at 04:55

2 Answers2

7

The function you want can be implemented as:

constructInf :: ([a] -> a) -> a -> [a]
constructInf f x = xs
  where xs = x:map f (tail $ inits xs)

The performance of consrtuctInf depends on the performance of it's argument function f. Assuming f takes O(N) time, then constructInf will take O(M*N) time, where M is the number of elements from the result of constructInf that you will inspect.

is7s
  • 3,500
  • 1
  • 20
  • 41
  • 2
    Worthy of note is also the related [`Data.Vector.constructN`](http://hackage.haskell.org/packages/archive/vector/latest/doc/html/Data-Vector.html#v:constructN) which does the same thing for finite vectors, with potentially much better performance due to O(1) random access and using mutation under the hood. – hammar Oct 13 '12 at 17:58
  • And what about memory usage? Won't `inits` create many sublists which all be in use and not GC'ed? – aplavin Oct 13 '12 at 18:00
  • Just to note: the time in this case will be `O(M^2)` 'cause `N` changes from `1` to `M`. – aplavin Oct 13 '12 at 18:01
  • @hammar: thanks, this will be worth remembering when current code will be too slow. – aplavin Oct 13 '12 at 18:02
  • @chersanya for memory usage, yes `inits` will create many intermediate lists. However, these intermediate lists will only be constructed when needed and will be garbage-collected after they've been used. I'm not sure really if list fusion can do anything here. – is7s Oct 13 '12 at 18:04
1

You want iterate.

take 10 $ iterate (+1) 0

= [0,1,2,3,4,5,6,7,8,9]

If you need the whole list so far, and don't mind getting it reversed, you can do this:

mkl f x0 = x0 : unfoldr (\xs -> let x = f xs in Just (x, x:xs)) [x0]

If you need the whole list so far, and you want it in order, it will be really inefficient, but you can do this:

mkl' f x0 = x0 : unfoldr (\xs -> let x = f xs in Just (x, xs ++ [x])) [x0]

But I'm not sure why you need the whole list instead of just the last element.

MasterMastic
  • 20,711
  • 12
  • 68
  • 90
pat
  • 12,587
  • 1
  • 23
  • 52
  • Oh, you want the whole list so far in each iteration? `iterate` just gets the previous element. – pat Oct 13 '12 at 17:41