0

If I create a infinite list like this:

let t xs = xs ++ [sum(xs)]
let xs = [1,2] : map (t) xs
take 10 xs

I will get this result:

[
[1,2],
[1,2,3],
[1,2,3,6],
[1,2,3,6,12],
[1,2,3,6,12,24],
[1,2,3,6,12,24,48],
[1,2,3,6,12,24,48,96],
[1,2,3,6,12,24,48,96,192],
[1,2,3,6,12,24,48,96,192,384],
[1,2,3,6,12,24,48,96,192,384,768]
]

This is pretty close to what I am trying to do.

This current code uses the last value to define the next. But, instead of a list of lists, I would like to know some way to make an infinite list that uses all the previous values to define the new one.

So the output would be only

[1,2,3,6,12,24,48,96,192,384,768,1536,...]

I have the definition of the first element [1].

I have the rule of getting a new element, sum all the previous elements. But, I could not put this in the Haskell grammar to create the infinite list.

Using my current code, I could take the list that I need, using the command:

xs !! 10
> [1,2,3,6,12,24,48,96,192,384,768,1536]

But, it seems to me, that it is possible doing this in some more efficient way.

Some Notes

I understand that, for this particular example, that was intentionally oversimplified, we could create a function that uses only the last value to define the next.

But, I am searching if it is possible to read all the previous values into an infinite list definition.

I am sorry if the example that I used created some confusion.

Here another example, that is not possible to fix using reading only the last value:

isMultipleByList :: Integer -> [Integer] -> Bool
isMultipleByList _ [] = False
isMultipleByList v (x:xs) = if (mod v x == 0)
                        then True
                        else (isMultipleByList v xs)

nextNotMultipleLoop :: Integer -> Integer -> [Integer] -> Integer
nextNotMultipleLoop step v xs = if not (isMultipleByList v xs)
                        then v
                        else nextNotMultipleLoop step (v + step) xs

nextNotMultiple :: [Integer] -> Integer
nextNotMultiple xs  = if xs == [2]
                        then nextNotMultipleLoop 1 (maximum xs) xs
                        else nextNotMultipleLoop 2 (maximum xs) xs


addNextNotMultiple xs = xs ++ [nextNotMultiple xs] 
infinitePrimeList = [2] : map (addNextNotMultiple) infinitePrimeList
take 10 infinitePrimeList
[
[2,3],
[2,3,5],
[2,3,5,7],
[2,3,5,7,11],
[2,3,5,7,11,13],
[2,3,5,7,11,13,17],
[2,3,5,7,11,13,17,19],
[2,3,5,7,11,13,17,19,23],
[2,3,5,7,11,13,17,19,23,29],
[2,3,5,7,11,13,17,19,23,29,31]
]

infinitePrimeList !! 10
[2,3,5,7,11,13,17,19,23,29,31,37]

Thiago Mata
  • 2,825
  • 33
  • 32
  • 1
    Your list elements starting from 3 are 3*2^n. If your operation is associative, you don't need to consume the entire list, you just need to look at the last element because it already contains all relevant information from all the previous elements. – n. m. could be an AI Aug 31 '20 at 12:43
  • It is just one simple example. It could be a list of prime numbers. I understand that for many cases we can define the infinite list based only in the previous element. I would like to know if it is possible using all the list. – Thiago Mata Aug 31 '20 at 13:03
  • It is not possible to use *all* the list because it is *infinite*. You however may use an initial segment of the list. Each subsequent element would use a longer segment. – n. m. could be an AI Aug 31 '20 at 13:11
  • 1
    @n.'pronouns'm. The question clearly says "all the **previous** values". Emphasis mine. – user253751 Aug 31 '20 at 13:15
  • @user253751 an element doesn't know what its previous values are. You need to compare indices or have some other way to stop before the element being defined. – n. m. could be an AI Aug 31 '20 at 13:30
  • @n.'pronouns'm. Well that was the question. How can we make it so the element knows what the previous values are? – user253751 Aug 31 '20 at 13:31
  • @user253751 we can e.g. feed the index of the current element to the function that consumes the list, so that it knows where to stop. – n. m. could be an AI Aug 31 '20 at 13:54
  • @n.'pronouns'm. Sounds like an answer – user253751 Aug 31 '20 at 14:51

7 Answers7

3

You can think so:

  1. You want to create a list (call them a) which starts on [1,2]:
a = [1,2] ++ ???
  1. ... and have this property: each next element in a is a sum of all previous elements in a. So you can write
scanl1 (+) a

and get a new list, in which any element with index n is sum of n first elements of list a. So, it is [1, 3, 6 ...]. All you need is take all elements without first:

tail (scanl1 (+) a)

So, you can define a as:

a = [1,2] ++ tail (scanl1 (+) a)

This way of thought you can apply with other similar problems of definition list through its elements.

SergeyKuz1001
  • 875
  • 1
  • 4
  • 6
3

If we already had the final result, calculating the list of previous elements for a given element would be easy, a simple application of the inits function.

Let's assume we already have the final result xs, and use it to compute xs itself:

import Data.List (inits)

main :: IO ()
main = do    
    let is = drop 2 $ inits xs
        xs = 1 : 2 : map sum is
    print $ take 10 xs

This produces the list

[1,2,3,6,12,24,48,96,192,384]

(Note: this is less efficient than SergeyKuz1001's solution, because the sum is re-calculated each time.)

danidiaz
  • 26,936
  • 4
  • 45
  • 95
2

You could define it like this:

xs = 1:2:iterate (*2) 3

For example:

Prelude> take 12 xs
[1,2,3,6,12,24,48,96,192,384,768,1536]
Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Ooh, didn't notice their definition didn't match their example. You beat me to it! – dfeuer Aug 31 '20 at 12:44
  • 1
    The example was friendly. Maybe too friendly. For the particular case of the example, you are right. We can define a function based only in the previous value. But I am trying to find a way to create functions based in all the previous values. – Thiago Mata Aug 31 '20 at 13:00
  • You can simply define a function that abstracts this definition to any two initial values. No matter what they are, `iterate` operates on a seed equal to their sum: `make xs x1 x2 = x1:x2:iterate (*2) (x1+x2)` – chepner Aug 31 '20 at 13:57
2

unfoldr has a quite nice flexibility to adapt to various "create-a-list-from-initial-conditions"-problems so I think it is worth mentioning.

A little less elegant for this specific case, but shows how unfoldr can be used.

import Data.List

nextVal as = Just (s,as++[s]) 
  where s = sum as

initList = [1,2]

myList =initList ++ ( unfoldr nextVal initList)

main = putStrLn . show . (take 12) $ myList

Yielding

[1,2,3,6,12,24,48,96,192,384,768,1536]

in the end.


As pointed out in the comment, one should think a little when using unfoldr. The way I've written it above, the code mimicks the code in the original question. However, this means that the accumulator is updated with as++[s], thus constructing a new list at every iteration. A quick run at https://repl.it/languages/haskell suggests it becomes quite memory intensive and slow. (4.5 seconds to access the 2000nd element in myList

Simply swapping the acumulator update to a:as produced a 7-fold speed increase. Since the same list can be reused as accumulator in every step it goes faster. However, the accumulator list is now in reverse, so one needs to think a little bit. In the case of predicate function sum this makes no differece, but if the order of the list matters, one must think a little bit extra.

LudvigH
  • 3,662
  • 5
  • 31
  • 49
  • Your answer also worked with the prime numbers sequences. – Thiago Mata Aug 31 '20 at 13:28
  • 1
    This is nice but conceptually not all that different from the original attempt. It also creates N intermediate lists in order to compute the Nth element of the final list. I wonder how well compilers can optimise all these lists away. – n. m. could be an AI Aug 31 '20 at 13:38
  • 1
    Yes that is really a good point regarding the number of intermediate lists constructed. One big problem is the appending to the right. If the accumulator list can be done in reversed, this becomes quite a bit better, since the `++` can be swapped for a `:`. But then again, it depends on the predicate function. I'm not familiar enough with haskell benchmarking to see what difference that makes though. – LudvigH Aug 31 '20 at 14:39
  • 1
    you have rediscovered the oldest tool in FP programmer's tool-chest, building an intermediate list in reverse. :) this should bring the complexity down. you can [measure it empirically](http://en.wikipedia.org/wiki/Analysis_of_algorithms#Empirical_orders_of_growth), too. IOW it should become faster and faster compared to the original quadratic solution. of course a C programmer would just loop over the same input list with a counter, instead of looking for the final `[]` to stop, but Haskell still doesn't have *this* convenience even as it allows us to express the same concept, with `inits`. – Will Ness Sep 03 '20 at 16:16
  • 1
    (contd.) .... [with `inits`](https://stackoverflow.com/a/63603526/849891). so, "7-fold" should quickly become "70-", and that -- "700-". IOW speed comparisons at one size points are meaningless, what really counts is [Empirical_orders_of_growth](http://en.wikipedia.org/wiki/Analysis_of_algorithms#Empirical_orders_of_growth)). ---- BTW having switched to building it in reverse, we can then discover that we don't need the list at all, and thus discover the `1 : 2 : iterate (*2) 3` solution naturally. – Will Ness Sep 03 '20 at 16:18
0

So here's my take. I tried not to create O(n) extra lists.

explode ∷ Integral i ⇒ (i ->[a] -> a) -> [a] -> [a]
explode fn init = as where
                     as = init ++ [fn i as | i <- [l, l+1..]]
                     l = genericLength init

This convenience function does create additional lists (by take). Hopefully they can be optimised away by the compiler.

explode' f = explode (\x as -> f $ take x as)

Usage examples:

myList = explode' sum [1,2]

sum' 0 xs = 0
sum' n (x:xs) = x + sum' (n-1) xs
myList2 = explode sum' [1,2]

In my tests there's little performance difference between the two functions. explode' is often slightly better.

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243
  • You can manually fuse `++` with the length calculation to avoid traversing the initial list twice. – dfeuer Sep 01 '20 at 18:43
0

The solution from @LudvigH is very nice and clear. But, it was not faster.

I am still working on the benchmark to compare the other options.

For now, this is the best solution that I could find:

-------------------------------------------------------------------------------------
-- # infinite sum of the previous using fuse
-------------------------------------------------------------------------------------

recursiveSum xs = [nextValue] ++ (recursiveSum (nextList)) where
      nextValue = sum(xs)
      nextList = xs ++ [nextValue]

initialSumValues = [1]
infiniteSumFuse =  initialSumValues  ++ recursiveSum initialSumValues

-------------------------------------------------------------------------------------
-- # infinite prime list using fuse
-------------------------------------------------------------------------------------

-- calculate the current value based in the current list
-- call the same function with the new combined value
recursivePrimeList xs = [nextValue] ++ (recursivePrimeList (nextList)) where
      nextValue = nextNonMultiple(xs)
      nextList = xs ++ [nextValue]

initialPrimes = [2]
infiniteFusePrimeList =  initialPrimes ++ recursivePrimeList initialPrimes

This approach is fast and makes good use of many cores.

Maybe there is some faster solution, but I decided to post this to share my current progress on this subject so far.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Thiago Mata
  • 2,825
  • 33
  • 32
  • try replacing `nextList = xs ++ [nextValue]` with `nextList = [nextValue] ++ xs`, see if it brings any speedup. :) – Will Ness Sep 03 '20 at 16:30
0

In general, define

xs = x1 : zipWith f xs (inits xs)

Then it's xs == x1 : f x1 [] : f x2 [x1] : f x3 [x1, x2] : ...., and so on.

Here's one example of using inits in the context of computing the infinite list of primes, which pairs them up as

ps = 2 : f p1 [p1] : f p2 [p1,p2] : f p3 [p1,p2,p3] : ...

(in the definition of primes5 there).

Will Ness
  • 70,110
  • 9
  • 98
  • 181