0

Making this code in Haskell GHC:

lazee = 2:5:(zipWith (+) lazee (tail lazee))

And then

take 6 lazee

Gives you this:

[2,5,7,12,19,31]

I can see only how it gives 2 and 5, not sure why on the [7,12,19,31] part.

duplode
  • 31,361
  • 7
  • 69
  • 130

2 Answers2

4

As @Josep pointed out in the comment, this is a stereotypical Haskell way to define Fibonacci sequence x_n = x_n-1 + x_n-2 with initial conditions 2 and 5.

bipll
  • 11,131
  • 1
  • 15
  • 31
  • I understand the concept now, though is there any way you can elaborate? I can see how it gets [2, 5, 7] since tail lazee is 5 and adding 2 to 5 = 7, but then why does it shift to 5 + 7 = 12? – Jackie Doesque Apr 19 '17 at 05:36
  • Imagine that the list `lazee` is the list `5, 7, a, b, c, d, e ...`, then `tail lazee` is `7, a, b, c, d, e ...`. Now `zipwith (+)` will calculate the sums `a = 5+7, b = 7+a, c = a+b, d = b+c ...`. In each step, the necessary values to calculate the next value is available. – dvaergiller Apr 19 '17 at 13:25
1

This is a very idiomatic way to write fibonnacci in Haskell.

Basically, in Haskell, one tries to exploit laziness (the fact data is computed only when needed) as much as possible.

So for instance, if you write a = 1 : a, you'd get an infinite stream of 1s (1:1:1:1:...). Yet if you do take 100 a, you get exactly 100 1s, and the program doesn't hang. That is because Haskell doesn't care for the rest of the list execept if you ask for it.

So how does it tie with your question? Well let's run hand by hand this definition shall we?

lazee = 2:5:(zipWith (+) lazee (tail lazee))

So first, you put a 2 and a 5 in lazee. So right now we have

lazee = 2 : 5 : ...
tail lazee = 5 : ...

(I'm using ... to show non yet-computed values) Then you do something quite tricky: you define lazee in term of itself. I find this actually way more beautiful than mere recursion because you abstract the recursion away, leaving only the pattern very legillibly. ZipWith is a function that zips two list together with a function So

zipWith (+) lazee (tail lazee) = zipWith (+) (2 : 5 : ...) (5 : ...) = 7 ...

So now

lazee = 2, 5, 7, ...
tail lazee = 5, 7, ...
zipWith (+) lazee (tail lazee) = zipWith (+) (2, 5, 7, ...) (5, 7, ...) = 7 : zipWith (+) (5, 7, ...) (7, ...) = 7 : 12 : ...

And so on, Haskell creates lazee as it goes on, adding the term it currently reads on lazee with the next one.


This is a pattern you might often see in one liner: constant values that are expressed in terms of themselves. A common exemple that comes to mind is the very innefficient list of primes

primes = sieve [2..]
  where sieve (p:xs) = p : (filter ((== 1) . (gcd p)) $ sieve xs)

What does it do? Well filter (== 1) . (gcd p) takes only value that are coprime with p (ie. Those who have a greater gcd than 1 with p)

So first we have

sieve [2..] = 2 : (filter ((== 1) . (gcd 2) $ sieve [3..])

Which means that we take 2 as a prime, and every number in sieve [3..] that are not even. sieve [3..] is 3 followed with every number that are not coprime with 3.

If we run this on a little exemple:

sieve [4..] = 4 : ...
sieve [3..] = 3 : 4 : ...
sieve [2..] = 2 : 3 : ...

Notice how the 4 got erased.


I hope that helped a bit. If you want to see some interesting one-liners, consider looking at https://wiki.haskell.org/Blow_your_mind I personnally recommend you to avoid direct recursion (ie. One where you just call yourself with a changed parameters) in favor of such patterns and list manipulations as the one you posted. (For instance writing fact n = product [1..n] to define factorial)

Have a nice journey in Haskell ;)