Why does memoization not work? - haskell

After reading a memoization introduction I reimplemented the Fibonacci example by using a more general memoize function (only for learning purposes):
memoizer :: (Int -> Integer) -> Int -> Integer
memoizer f = (map f [0 ..] !!)
memoized_fib :: Int -> Integer
memoized_fib = memoizer fib
where fib 0 = 0
fib 1 = 1
fib n = memoized_fib (n-2) + memoized_fib (n-1)
This works, but when I just change the last line to the following code, memoization suddenly does not work as I expected (the program becomes slow again):
fib n = memoizer fib (n-2) + memoizer fib (n-1)
Where is the crucial difference w.r.t. to memoization?

It is about explicit vs. implicit sharing. When you explicitly name a thing, it naturally can be shared, i.e. exist as separate entity in memory, and reused. (Of course sharing is not part of the language per se, we can only nudge the compiler ever so slightly towards sharing certain things).
But when you write same expression twice or thrice, you rely on compiler to replace the common sub-expressions with one explicitly shared entity. That might or might not happen.
Your first variant is equivalent to
memoized_fib :: Int -> Integer
memoized_fib = (map fib [0 ..] !!) where
fib 0 = 0
fib 1 = 1
fib n = memoized_fib (n-2) + memoized_fib (n-1)
Here you specifically name an entity, and refer to it by that name. But that is a function. To make the reuse even more certain, we can name the actual list of values that gets shared here, explicitly:
memoized_fib :: Int -> Integer
memoized_fib = (fibs !!) where
fibs = map fib [0 ..]
fib 0 = 0
fib 1 = 1
fib n = memoized_fib (n-2) + memoized_fib (n-1)
The last line can be made yet more visually apparent, with explicit reference to the actual entity which is shared here - the list fibs which we just named in the step above:
fib n = fibs !! (n-2) + fibs !! (n-1)
Your second variant is equivalent to this:
memoized_fib :: Int -> Integer
memoized_fib = (map fib [0 ..] !!) where
fib 0 = 0
fib 1 = 1
fib n = (map fib [0 ..] !!) (n-2) + (map fib [0 ..] !!) (n-1)
Here we have three seemingly independent map expressions, which might or might not get shared by a compiler. Compiling it with ghc -O2 seems to reintroduce sharing, and with it the speed.

momoized_fib = ... - that's top-level simple definition. it might be read as a constant lazy value (without any additional arguments required to be bound before expanding it. That's kinda "source" of your memoized values.
When you use (memoizer fib) (n-2) creates new source of values which have no relation with memoized_fib and thus it isn't reused. Actually you move a lot of load on GC here since you produce a lot (map fib [0 ..]) sequences in second variant.
Consider also more simple example:
f = \n -> sq !! n where sq = [x*x | x <- [0 ..]]
g n = sq !! n where sq = [x*x | x <- [0 ..]]
First will generate single f and associated with it sq because there is no n in head of declaration. Second will produce family of lists for each different value of f n and move over it (without bounding down to actual values) to get value.

Related

Fibonacci numbers without using zipWith

I have been trying to implement a list of Fibonacci number sequence from 0 to n without using the lazy zipwith method. What I have so far is code that returns a list from n to 1. Is there any way I can change this code so it returns the list from 0-n at all?
Example:
fib_seq 4 = [3,2,1,1]
-- output wanted: [1,1,2,3]
If there is not a way to do what I want the code to do, is there a way to just return the list of Fibonacci numbers taking in a number say again 4 it would return [0, 1, 1, 2].
fib_seq :: Int -> [Int]
fib_seq 0 = [0]
fib_seq 1 = [1]
fib_seq n = sum (take 2 (fib_seq (n-1))) : fib_seq (n-1)
Another way you could choose to implement the fib numbers is the use of a helper function then a function on it's own that will produce the infinite list of fib numbers, or you could use take 10 fibs and the output for this would be the first 10 fib numbers. My function is definitely not the fastest way to work out the fib numbers infintely that would be with the zipWith function, but you are not using that here so here is my way to implement it without zipWith.
for example take 10 fibs would return: [0,1,1,2,3,5,8,13,21,34]
fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
fibs :: [Int]
fibs = (map fib [0..])
It is often the case that you can solve a problem by considering a slightly more general version of it.
Say we want the infinite Fibonacci list starting with two prescribed initial values a and b. There is an obvious recursive solution:
$ ghci
GHCi, version 8.8.4: https://www.haskell.org/ghc/ :? for help
...
λ>
λ> aux_fib a b = a : (aux_fib b (a+b))
λ>
λ> take 4 (aux_fib 1 1)
[1,1,2,3]
λ>
And so:
λ>
λ> fib_seq n = take n (aux_fib 1 1)
λ>
λ> fib_seq 4
[1,1,2,3]
λ>
Note: camel case is regarded as more idiomatic in Haskell, so it would be more like auxFib and fibSeq.
If you wanted to have the list start from 0 you could use a helper function and then use this helper function within your fib_seq (which i recommend you change to Camel case so like fibSeq, standard haskell notation)
Ok so the functions as follow fibSeq 7 would return [0,1,1,2,3,5,8]:
fibHelp :: Int -> Int -> [Int]
fibHelp x y = x : (fibHelp y (x+y))
fibSeq :: Int -> [Int]
fibSeq n = take n (fibHelp 0 1)
It feels a bit like cheating, but you could use the closed formula for the Fibonacci sequence like this:
fib n = (phi^n - psi^n) / sqrt 5
where
phi = (1 + sqrt 5) / 2
psi = (1 - sqrt 5) / 2
fibSeq n = fib <$> [1 .. n]
Otherwise the Haskell Wiki has many more implementation variants to chose from. For example very succinctly
fibs = 0 : 1 : next fibs
where
next (a : t#(b:_)) = (a+b) : next t

Create a new expression without pointer from previous one

I am reading the book https://www.packtpub.com/application-development/haskell-high-performance-programming and trying to figure out, what is the difference between those two functions:
This functions does memoize the intermediate numbers:
fib_mem :: Int -> Integer
fib_mem = (map fib [0..] !!)
where fib 0 = 1
fib 1 = 1
fib n = fib_mem (n-2) + fib_mem (n-1)
and this not:
fib_mem_arg :: Int -> Integer
fib_mem_arg x = map fib [0..] !! x
where fib 0 = 1
fib 1 = 1
fib n = fib_mem_arg (n-2) + fib_mem_arg (n-1)
The author tries to explain as following:
Running fib_mem_arg with anything but very small arguments, one can
confirm it does no memoization. Even though we can see that map fib
[0..] does not depend on the argument number and could be memorized,
it will not be, because applying an argument to a function will create
a new expression that cannot implicitly have pointers to expressions
from previous function applications.
What does he mean with the sentence, that is bold marked? Could someone provide me a simple example?
Why fib_mem is a constant applicative form?
Why fib_mem is a constant applicative form?
Not fib_mem, but (map fib [0..] !!). It is a CAF because it is a partially applied function (!!). As such it is subject to memory retention.
(see also: What are super combinators and constant applicative forms?)
Since the type is monomorphic, it is retained in memory even between calls to fib_mem, in effect as if having map fib [0..] "floated" to the top level, as if defined as
fib_mem_m :: Int -> Integer
fib_mem_m = (the_list !!)
where fib 0 = 1
fib 1 = 1
fib n = (the_list !! (n-2)) + (the_list !! (n-1))
the_list = map fib [0..]
If the type were polymorphic, the floating to top level wouldn't be possible, but it would still be retained for the duration of each call to fib_mem, as if defined as
fib_mem_p :: Num a => Int -> a
fib_mem_p = (the_list !!)
where fib 0 = 1
fib 1 = 1
fib n = (the_list !! (n-2)) + (the_list !! (n-1))
the_list = map fib [0..]
To see the difference, evaluate fib_mem_m 10000 twice, at the GHCi propt. The second attempt will take 0 seconds. But fib_mem_p 10000 will take same amount of time each time it is called. It will still be as fast as the first one, so there is still memoization going on there, it's just not retained between calls.
With this style of definition, the full application as in fib_mem_arg will too be memoized -- and just as the one above, not between the calls to fib_mem_arg, but only during each call.
fib_mem_arg :: Num a => Int -> Integer -- or polymorphic, makes no difference
fib_mem_arg x = the_list !! x
where fib 0 = 1
fib 1 = 1
fib n = (the_list !! (n-2)) + (the_list !! (n-1))
the_list = map fib [0..]

Haskell: Why is my implementation of the Fibonacci sequence inefficient?

I have written the following Fibonacci play program as part of learning Haskell:
fibonacci 0 = [0]
fibonacci 1 = [0,1]
fibonacci n = let
foo'1 = last (fibonacci (n-1))
foo'2 = last (fibonacci (n-2))
in reverse((foo'1 + foo'2):reverse (fibonacci (n-1)))
The program works:
ghci>fibonacci 6
[0,1,1,2,3,5,8]
But, the performance goes down exponentially with n. If I give it an argument of 30 it takes about a minute to run as opposed to running instantaneously at 6. It seems the lazy execution is burning me and fibonacci is getting run once for every element in the final list.
Am I doing something silly or missing something?
(I already got rid of the ++ thinking that might be doing it)
As pointed out in the comments, your approach is a tad overcomplicated. In particular, you don't need to use recursive calls, or even the reverse function, in order to generate the Fibonacci sequence.
A linear-time implementation
In addition to your own answer, here is a textbook one-liner, which uses memoization:
fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
Once you have fibs, writing your fib function is trivial:
fib :: Int -> [Integer]
fib n
| n < 0 = error "fib: negative argument"
| otherwise = take (n+1) fibs
This implementation of fib has complexity Θ(n), which is obviously much better than Θ(exp(n)).
Test in GHCi
λ> :set +s
λ> fib 6
[0,1,1,2,3,5,8]
(0.02 secs, 7282592 bytes)
λ> fib 30
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040]
(0.01 secs, 1035344 bytes)
As you can see, fib 30 is evaluated in well under one minute on my machine.
Further reading
For a much more comprehensive treatment of how to generate the Fibonacci sequence in Haskell, I refer you to this haskell.org wiki
Here is the answer to the question using #icktoofay's pointer to memoization. The answer included a function that quickly returned a given fibonacci number, so I used their example to create a solution to my original problem--creating a list of the Fibonacci numbers up to the requested number.
This solution runs pretty much instantaneously (the page has the additional benefit of referring to my approach as "naive")
memoized_fib :: Int -> Integer
memoized_fib = (map fib [0 ..] !!)
where fib 0 = 0
fib 1 = 1
fib n = memoized_fib (n-2) + memoized_fib (n-1)
fib 0 = [0]
fib 1 = [0,1]
fib n = reverse ((memoized_fib (n-2) + memoized_fib(n-1)) : reverse (fib (n-1)))
You don't need to add memoization to your function - it already has all the previous results, producing a list as it does. You just need to stop ignoring those results, as you do right now using last.
First of all, if it's more natural to build the list in reverse order, there's no reason not to:
revFib 0 = [0]
revFib 1 = [1,0]
revFib n | n > 0 = let f1 = head (revFib (n-1))
f2 = head (revFib (n-2))
in f1 + f2 : revFib (n-1)
This is still slow, as we're still ignoring all the previous results except the very last one, situated at the head of the list. We can stop doing that,
revFib 0 = [0]
revFib 1 = [1,0]
revFib n | n > 0 = let f1 = head (revFib (n-1))
f2 = head (tail (revFib (n-1)))
in f1 + f2 : revFib (n-1)
and then we'll name the common subexpression, so that it is shared among its uses, and is only calculated once:
revFib 0 = [0]
revFib 1 = [1,0]
revFib n | n > 0 = let prevs = revFib (n-1)
[f1,f2] = take 2 prevs
in f1 + f2 : prevs
and suddenly it's linear instead of exponential.

What does "!!" mean in haskell?

There are two functions written in the Haskell wiki website:
Function 1
fib = (map fib' [0 ..] !!)
where
fib' 0 = 0
fib' 1 = 1
fib' n = fib (n - 1) + fib (n - 2)
Function 2
fib x = map fib' [0 ..] !! x
where
fib' 0 = 0
fib' 1 = 1
fib' n = fib (n - 1) + fib (n - 2)
What does the "!!" mean?
This is actually more difficult to read then it would seem at first as operators in haskell are more generic then in other languages.
The first thing that we are all thinking of telling you is to go look this up yourself. If you do not already know about hoogle this is the time to become familiar with it. You can ask it either to tell you what a function does by name or (and this is even more cool) you can give it the type of a function and it can offer suggestions on which function implement that type.
Here is what hoogle tells you about this function (operator):
(!!) :: [a] -> Int -> a
List index (subscript) operator, starting from 0. It is an
instance of the more general genericIndex, which takes an index
of any integral type.
Let us assume that you need help reading this. The first line tell us that (!!) is a function that takes a list of things ([a]) and an Int then gives you back one of the thing in the list (a). The descriptions tells you what it does. It will give you the element of the list indexed by the Int. So, xs !! i works like xs[i] would in Java, C or Ruby.
Now we need to talk about how operators work in haskell. I'm not going to give you the whole thing here, but I will at least let you know that there is something more here then what you would encounter in other programming languages. Operators "always" take two arguments and return something (a -> b -> c). You can use them just like a normal function:
add x y
(+) x y -- same as above
But, by default, you can also use them between expression (the word for this is 'infix'). You can also make a normal function work like an operator with backtics:
x + y
x `add` y -- same as above
What makes the first code example you gave (especially for new haskell coders) is that the !! operator is used as a function rather then in a typical operator (infix) position. Let me add some binding so it is clearer:
-- return the ith Fibonacci number
fib :: Int -> Int -- (actually more general than this but do't worry about it)
fib i = fibs !! i
where
fibs :: [Int]
fibs = map fib' [0 ..]
fib' :: Int -> Int
fib' 0 = 0
fib' 1 = 1
fib' n = fib (n - 1) + fib (n - 2)
You can now work your way back to example 1. Make sure you understand what map fib' [0 ..] means.
I'm sorry your question got down-voted because if you understood what was going on the answer would have been easy to look up, but if you don't know about operators as the exist in haskell it is very hard to mentally parse the code above.
(!!) :: [a] -> Int -> a
List index (subscript) operator, starting from 0. It is an instance of the more general genericIndex, which takes an index of any integral type.
See here: http://www.haskell.org/ghc/docs/latest/html/libraries/base/Data-List.html#g:16

Memoization with recursion

I am trying to understand Haskell realization of memoization , but I don't get how it works:
memoized_fib :: Int -> Integer
memoized_fib = (map fib [0..] !!)
where fib 0 = 0
fib 1 = 1
fib n = memoized_fib(n - 2) + memoized_fib(n - 1)
First of all I even don't understand why 'map'-function get three parameters (function - fib, list [0..], and ||), but not two how it must do.
Updated:
I have tried to rewrite the code, but get the different result:
f' :: (Int -> Int) -> Int -> Int
f' mf 0 = 0
f' mf 1 = 1
f' mf n = mf(n - 2) + mf(n - 1)
f'_list :: [Int]
f'_list = map (f' faster_f') [0..]
faster_f' :: Int -> Int
faster_f' n = f'_list !! n
Why? Is the any error in my reasoning?
First: Haskell supports operator sections. So (+ 2) is equal to \ x -> x + 2. This means the expression with map is equal to \ x -> map fib [0..] !! x.
Secondly, how this works: this is taking advantage of Haskell's call-by-need evaluation strategy (its laziness).
Initially, the list which results from the map is not evaluated. Then, when you need to get to some particular index, all the elements up to that point get evaluated. However, once an element is evaluated, it does not get evaluated again (as long as you're referring to the same element). This is what gives you memoization.
Basically, Haskell's lazy evaluation strategy involves memoizing forced values. This memoized fib function just relies on that behavior.
Here "forcing" a value means evaluating a deferred expression called a thunk. So the list is basically initially stored as a "promise" of a list, and forcing it turns that "promise" into an actual value, and a "promise" for more values. The "promises" are just thunks, but I hope calling it a promise makes more sense.
I'm simplifying a bit, but this should clarify how the actual memoization works.
map does not take three parameters here.
(map fib [0..] !!)
partially applies (slices) the function (!!) with map fib [0..], a list, as its first (left-hand) argument.
Maybe it's clearer written it as:
memoized_fib n = (map fib [0..]) !! n
so it's just taking the nth element from the list, and the list is evaluated lazily.
This operator section stuff is exactly the same as normal partial application, but for infix operators. In fact, if we write the same form with a regular function instead of the !! infix operator, see how it looks:
import Data.List (genericIndex)
memoized_fib :: Int -> Integer
memoized_fib = genericIndex (map fib [0..])
where fib 0 = 0
fib 1 = 1
fib n = memoized_fib(n - 2) + memoized_fib(n - 1)

Resources