Can fold be used to create infinite lists?

前端 未结 4 1117
执笔经年
执笔经年 2021-01-18 07:00

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)

相关标签:
4条回答
  • 2021-01-18 07:16

    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.

    0 讨论(0)
  • 2021-01-18 07:18

    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.

    0 讨论(0)
  • 2021-01-18 07:23

    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)
    
    0 讨论(0)
  • 2021-01-18 07:26

    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.

    0 讨论(0)
提交回复
热议问题