7

I have written the following code which creates an infinite list of Fibonacci numbers:

fibs = 1:1:fib 1 1
  where fib a b = a+b:fib b (a+b)

Can the above code be written using foldl or foldr to avoid recursion?

kiritsuku
  • 51,545
  • 18
  • 109
  • 133
Dragno
  • 2,669
  • 23
  • 38
  • Although this is interesting, there is actually a better way to do this which is to use the [closed form solution for finding the Fibonacci numbers](http://en.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding) – Andrew Walker Sep 06 '12 at 10:42
  • 2
    @AndrewWalker, with floating point arithmetic, that's not an exact solution. – huon Sep 06 '12 at 10:47
  • Note that in a non-strict functional language like Haskell, there's no cause to avoid recursion for efficiency reasons, but it's fine to avoid it for stylistic reasons. – AndrewC Sep 06 '12 at 14:51

4 Answers4

15

The foldl and foldr functions are list-consumers. As svenningsson's answer rightly points out, unfoldr is a list-producer which is suitable for capturing the co-recursive structure of fibs.

However, given that foldl and foldr are polymorphic in their return types, i.e. what they're producing by consuming a list, it is reasonable to ask whether they might be used to consume one list and produce another. Might any of these produced lists be infinite?

Looking at the definition of foldl

foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f a []        = a
foldl f a (b : bs)  = foldl f (f a b) bs

we see that for foldl to produce anything at all, the list it consumes must be finite. Thus if foldl f a produces infinite output, it is because a is infinite or because f sometimes performs infinite list generation.

It's a different story with foldr

foldr :: (b -> a -> a) -> a -> [b] -> a
foldr f a []        = a
foldr f a (b : bs)  = f b (foldr f a bs)

which admits the lazy possibility that f might generate some output for each b consumed from the input. Operations like

map g = foldr (\ b gbs -> g b : gbs) []   -- golfers prefer ((:) . g)
stutter = foldr (\ x xxs -> x : x : xxs) []

producing a little bit of output for each input, deliver infinite output from infinite input.

A cheeky person can thus express any infinitary recursion as a non-recursive foldr on an infinite list. E.g.,

foldr (\ _ fibs -> 1 : 1 : zipWith (+) fibs (tail fibs)) undefined [1..]

(Edit: or, for that matter

foldr (\_ fib a b -> a : fib b (a + b)) undefined [1..] 1 1

which is closer to the definition in the question.)

although this observation, whilst true, is hardly indicative of a healthy programming style.

Community
  • 1
  • 1
pigworker
  • 42,162
  • 18
  • 118
  • 213
  • Thanks for your answer which is very interesting. But using fib to call fib again uses **recursion** which we try to avoid using fold. – Dragno Sep 06 '12 at 19:24
  • 2
    The `fib` in my term is bound with a lambda, not defined by recursion. On the other hand, it's true that I'm using `foldr` to construct a general purpose fixpoint operator. – pigworker Sep 06 '12 at 19:44
  • 1
    In your foldl definition you omitted application of **f** so `foldl f a (b : bs) = foldl (f a b) bs` had to be `foldl f a (b : bs) = foldl f (f a b) bs` – David Unric Sep 07 '12 at 15:23
  • @DavidUnric Thanks, my bad. Fixed now. – pigworker Sep 07 '12 at 16:21
11

I don't know if it's possible to create infinite lists with foldl. You could perhaps solve this problem by using foldr, but then you would have to create another list to fold over. What would that list be? There is nothing with the fibonacci numbers that suggest they are generated from some other list.

What you want instead is to use unfoldr. It can be used to create lists instead of consuming them, as is the case for foldl and foldr. Here's how you would use unfoldr to generate the infinite list of fibonacci numbers.

fib = unfoldr (\(a,b) -> Just (a,(b,a+b))) (1,1)

You can find unfoldr in the module Data.List in the base package.

svenningsson
  • 3,961
  • 1
  • 21
  • 31
3

One way to avoid explicit recursion is to use fix to express the recursion as a fixed point.

import Data.Function (fix)

fibs = fix $ \l -> [1,1] ++ zipWith (+) l (tail l)

or in point-free style

import Data.Function (fix)
import Control.Monad.Instances

fibs = fix $ ([1,1] ++) . (zipWith (+) =<< tail)
shang
  • 23,876
  • 3
  • 55
  • 83
1

You can use zipWith to write your definition

fibonacci = 1:1:zipWith (+) fibonacci (tail fibonacci)

edit: Ok, I dont think you can use foldl or foldr to create infinite list. Not in any simple imaginable sense. If you look at the simple definition of foldl

foldl f z []     = z
foldl f z (x:xs) = foldl f (f z x) xs

foldl never returns until it has exhausted the whole list. So a simple example like

g = foldl f [] [1..]
 where 
  f xs a = xs ++ [a]

> take 10 g

will not work even and it will loop on forever.

Satvik
  • 11,140
  • 1
  • 35
  • 45