-1

I have searched the blog for a Fibonacci implementation similar the the one I desire. Nothing came up although there were a lot of questions about Haskell's Fibonacci implementation.

I want a recursive Fibonacci function (speed doesn't matter) that appends each Fibonacci number to a global list on each call to the fib function. I am new to Haskell and coming from an Imperative background. Here is what I thought would work but did'n to the trick.

fibos = []

fib 0 = 1
fib 1 = 1
fib n = (fib(n-1) + fib(n-2)):fibos


main = do 
    putStrLn "Please enter a number:"
    n <- readLn
    fib n
    print fibos

I know about the already existing Fibonacci list in Haskell but my assignment specification wants me to do it in this way. I'm trying to cons each Fibonacci number up to user defined number n into the empty global fibos list. Any help or pointers will be much appreciated.

EDIT:

I now have a working function that returns a list of fibonacci numbers with n number of elements where n is user input, here is the code:

fib::Int->Int
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
fibList n = map fib [0..n]

main :: IO ()
main = do 
    putStrLn "Please enter a positive integer:"
    n <- readLn 
    print (fibList n)

What I need is a function that prints [0,1,1,2] if the user enters 2 and [0,1,1,2,3,5,8] if the user enters 8 or anything between 8 and 12(because 13 is a fib number).

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
  • You cannot mutate `fibos`, it always is the empty list. – Bergi Oct 26 '15 at 12:03
  • 2
    That parse error is probably due to indentation. Move the `putStrLn` in the next line - all statements inside a `do` must have the same indentation. – Bergi Oct 26 '15 at 12:05
  • What exactly is that "*global list*" good for? Why do you need it, what do you want to do with it? In which order should it contain the numbers? – Bergi Oct 26 '15 at 12:06
  • 1
    I don't think you are interpreting your assignment correctly. Can you share it verbatim? – n. m. could be an AI Oct 26 '15 at 12:12
  • @Bergi the list will be used later on to calculate sum and product of all elements in the list. The specification of my assignment states: "As each number in the sequence is generated, that number is appended to a list containing the numbers generated" it must also be a recursive function. – Ronnie Van Dyk Oct 26 '15 at 12:13
  • @RonnieVanDyk: So the function you're looking for is `fib :: Int -> [Int]` (or similar), not `fib :: Int -> Int`. – Zeta Oct 26 '15 at 12:14
  • @RonnieVanDyk: I think what the assignment text was actually trying to say is "*As each number in the sequence is generated, it is prepended to the list of the numbers that are yet to be generated*". In purely functional programming, the list that you're trying to generate is the functions result. – Bergi Oct 26 '15 at 12:45
  • @Bergi that makes sense! Thank you. I'll try and implement something like that. – Ronnie Van Dyk Oct 26 '15 at 12:58

4 Answers4

2
import Data.List

fibo :: Int -> Int
fibo 0 = 0
fibo 1 = 1
fibo n = fibo (n-1)+fibo(n-2)


fiboList x =map fibo [1..x]

main = do
    print ( fiboList 35 )
1

EDIT:

if all you need is to see the sequence of values that led to fib n, you should just use something like:

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
fib n = (take n fibs, fibs !! n)

which works like this:

*Main> fib 0
([],0)
*Main> fib 1
([0],1)
*Main> fib 10
([0,1,1,2,3,5,8,13,21,34],55)

If you want, you can use the State monad to separate out the list aspect of the computation but it would only mean a list that is "global" in a limited scope (e.g. the main function). That's the closest you can get to whay you want while staying in proper Haskell land.

The Right Way

I'm assuming you're attempting this for purposes of momoization? If yes, basically there already is a global list under the hood of any list-based implementation of fib — any callers of fib use the same lazily evaluated list that the Haskell (e.g. GHC) runtime uses under the hood; any elements of that list that have already been computed are not going to be re-computed.

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

— here, fibs is a global list, and whereas you can't explicitly/manually mutate it, by accessing individual elements in it (fibs !! n), you are indirectly causing the underlying list to be mutated as the runtime adds new values to it.


The Wrong Way (but go ahead if you're not going to use this in real code)

If, on the other hand, you need the list for the sake of just having that list, e.g. for debugging or some other weird reason, you can use a really ugly and unsafe hack such as the following IORef based solution, but please remember to stick to function names that end in Unsafe, in the spirit of how the stdlib is marking all of the unsafe calls so that people who use them are aware of the dangers:

import Data.IORef
import System.IO.Unsafe (unsafePerformIO)

fibsStorageUnsafe :: IORef [Integer]
fibsStorageUnsafe = unsafePerformIO $ newIORef []

-- this is one one that uses the pure definition
-- above to compute the value and then store it
-- in a global, mutable IORef
fibUnsafe n = unsafePerformIO (updateFibs ret) `seq` ret
  where
    ret = fib' n

    updateFibs x = do
      old <- readIORef fibsStorageUnsafe
      writeIORef fibsStorageUnsafe (x:old)

    fib' :: Int -> Integer
    fib' 0 = 0
    fib' 1 = 1
    -- you can recursively call fib' instead of fibUnsafe
    -- to only log the "top level" (non-recursive)
    -- calls to fibUnsafe and not all recursive calls
    fib' n = fibUnsafe (n-1) + fibUnsafe (n-2)

which works like this:

*Main> readIORef fibsStorageUnsafe
[]
*Main> fibUnsafe 0
0
*Main> readIORef fibsStorageUnsafe
[0]
*Main> fibUnsafe 1
1
*Main> readIORef fibsStorageUnsafe
[1,0]
*Main> fibUnsafe 3
2
*Main> readIORef fibsStorageUnsafe
[1,0,1,1,2,1,0]
*Main> fibUnsafe  10
55
*Main> readIORef fibsStorageUnsafe
[1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,13,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,21,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,13,34,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,13,1,0,1,1,2,1,0,1,3,1,0,1,1,2,5,1,0,1,1,2,1,0,1,3,8,21,55,1,0,1,1,2,1,0]

You can adjust the definitions of fibUnsafe, fib' and other components to fit your goals, but again: I'd strongly recommend against using an approach like this, unless it's solely for learning or experimentation purposes.

For example, if you modify fib' to call itself not fibUnsafe like this:

fib' n = fib' (n-1) + fib' (n-2)

then calling fibUnsafe 10 will yield the following contents for fibStorageUnsafe instead of what you're seeing above:

*Main> fibUnsafe  10
55
*Main> readIORef fibsStorageUnsafe
[55]

DISCLAIMER: I don't even know if the IORef based code above will work correctly under all circumstances — I admit I'm not too familiar with IORefs and how unsafePerformIO works to give any guarantees as to the semantics of the code above.

Erik Kaplun
  • 37,128
  • 15
  • 99
  • 111
  • My implementation of fibonacci function is only for educational purposes, secondly when a user enters 8, it must print [0,1,1,2,3,5,8] and not [0,1,1,2,3,5,8,13] if the user enters 9, it should also print [0,1,1,2,3,5,8] because there's no fib number 9, and not [0,1,1,2,3,5,8,13,21] which has 9 number of elements. How would one implement such a function – Ronnie Van Dyk Oct 26 '15 at 13:54
  • 1
    Look, man, this is not a web site for a "do my homework"-attitude — I've shown you how to modify a global list; it's your code and your goal so you can modify the implementation to fit your needs. – Erik Kaplun Oct 26 '15 at 13:59
  • I don't have a "do my homework attitude", I'm just not getting answers that can help me proceed with the assignment. It could be because I'm not describing my problem in the right way, this was the purpose of my previous comment. – Ronnie Van Dyk Oct 26 '15 at 14:13
  • "I'm just not getting answers that can help me proceed with the assignment." is sort of a "do my homework" attitude. :) – sshine Oct 26 '15 at 15:17
  • Ronnie, my answer clearly provided a means to create and modify a global list, as you requested. If that doesn't help you proceed, I don't know what does, shy of providing a full working solution (but your post doesn't contain a detailed enough a spec to do that, even if one was willing to) – Erik Kaplun Oct 26 '15 at 15:55
1

You can do something similar as you want if it's okay to get a list back:

fibs 0 = [0]
fibs 1 = [0,1]
fibs n = let fs@(f1:f2:_) = fibs (n-1) 
         in (f1+f2) : fs

Then fibs 10 would return [55,34,21,13,8,5,3,2,1,1,0]. Note that it would be either wasteful or more complicated to build the list in the opposite order, as Haskell lists operate only on the "front". For the strange fs@(f1:f2:_), please consult https://en.wikibooks.org/wiki/Haskell/Pattern_matching (@ is the "as"-pattern)

For a "real" Fibonacci function you just need to return the head:

fib n = head (fibs n)

I think this version comes your intentions quite close, and its performance is okay, too. However, this code isn't very idiomatic. The typical approach here are infinite lists (like some other answers show).

If you allow me a personal remark: I found that "imperative" knowledge was more irritating than helpful when learning Haskell. I think you should consciously try to "forget" or "unlearn" what you know about (imperative) programming in order to "grok" functional programming.

Landei
  • 54,104
  • 13
  • 100
  • 195
  • This backwards list is very strange. In fact, in a lazy language, it is likely to be much more efficient to build the list forwards using something like the `zipWith` approach Erik Allik suggested. – dfeuer Oct 26 '15 at 15:05
  • @dfeuer It just *feels* strange, as functions like `zipWith` give you the "normal" order, and that for a good reason. However, here the reversed order is quite useful, as you don't want to traverse the whole list just to get to the "interesting" bits. Of course, such an approach is not possible for infinite lists. And if you actually *read* my answer and not just glace over the code, you will find that I already pointed that out. But I consciously avoided infinite lists, as they are difficult to grasp for beginners. The `zipWith` magic might be impressive, but need to be understood, too. – Landei Oct 27 '15 at 08:06
  • It is strange in Haskell, and a bit peculiar elsewhere too. Without laziness, you can use a streaming generator of some sort. For fast access, it's much better to use the matrix exponentiation technique. – dfeuer Oct 27 '15 at 15:51
  • Do you even read what I write? I *agree*, the infinite list generator is the better solution, but this doesn't help the TO to *understand*. That's why I tried to mimic *his* solution as close as possible. He struggles already with the concept of immutability, so it makes no sense at all to throw an example of magical self-referential laziness mumbo-jumbo at him. This is about learning and understanding, not about the "best" way to do it. – Landei Oct 27 '15 at 18:34
0

In purely functional programming, one does not operate with global, mutable variables. Thus, if you define a global value fibos = [], then that list will never be anything but an empty list. If you instead want a top-level definition that is immutable, fixing it to the n first Fibonacci numbers, where n is determined at runtime and fibs has no parameters, becomes difficult.

An alternative is Haskell's lazy evaluation which enables you to create a top-level definition for all Fibonacci numbers, but they will only be computed to the extent they are requested.

fibs :: [Integer]
fibs = 1:1:zipWith (+) fibs (tail fibs)

Or less fancy:

fibs :: [Integer]
fibs = fib 0 1
  where fib a b = b : fib b (a+b)

You can then extract as many Fibonacci numbers as you need:

main :: IO ()
main = do
  putStr "n: "
  n <- readLn
  putStrLn $ "Fibs: " ++ show (take n fibs)
sshine
  • 15,635
  • 1
  • 41
  • 66
  • Edit: I answered a comment of Ronny von Dyk which just disappeared (the comment, not Ronny)... Original text: You cannot "append" something to a list, because a list (as any variable) is *immutable* in Haskell. The closest thing to appending an element is getting back a *new* list consisting on the pöd list and an "appended" element. But you are never ever able to *modify* an existing list in Haskell. – Landei Oct 26 '15 at 14:26