Generating Fibonacci series in F#

梦想的初衷 提交于 2019-11-30 08:30:51

First of all, you're using let as if it was a statement to mutate a variable, but that's not the case. In F#, let is used to declare a new value (which may hide any previous values of the same name). If you want to write code using mutation, then you need to use something like:

let c = a + b  // declare new local value
l.Add(c)  
a <- b   // mutate value marked as 'mutable'
b <- c   // .. mutate the second value

The second issue with your code is that you're trying to mutate F# list by adding elements to it - F# lists are immutable, so once you create them, you cannot modify them (in particular, there is no Add member!). If you wanted to write this using mutation, you could write:

let fabList = 
  // Create a mutable list, so that we can add elements 
  // (this corresponds to standard .NET 'List<T>' type)
  let l = new ResizeArray<_>([1;2])
  let mutable a = 1
  let mutable b = 2
  while l.[l.Count - 1] < 400 do
    let c = a + b
    l.Add(c) // Add element to the mutable list
    a <- b
    b <- c
  l |> List.ofSeq // Convert any collection type to standard F# list

But, as others already noted, writing the code in this way isn't the idiomatic F# solution. In F#, you would use immutable lists and recursion instead of loops (such as while). For example like this:

// Recursive function that implements the looping
// (it takes previous two elements, a and b)
let rec fibsRec a b =
  if a + b < 400 then
    // The current element
    let current = a + b
    // Calculate all remaining elements recursively 
    // using 'b' as 'a' and 'current' as 'b' (in the next iteration)
    let rest = fibsRec b current  
    // Return the remaining elements with 'current' appended to the 
    // front of the resulting list (this constructs new list, 
    // so there is no mutation here!)
    current :: rest
  else 
    [] // generated all elements - return empty list once we're done

// generate list with 1, 2 and all other larger fibonaccis
let fibs = 1::2::(fibsRec 1 2)

Other posts tell you how to write the while loop using recursive functions. This is another way by using the Seq library in F#:

// generate an infinite Fibonacci sequence
let fibSeq = Seq.unfold (fun (a,b) -> Some( a+b, (b, a+b) ) ) (0,1)
// take the first few numbers in the sequence and convert the sequence to a list
let fibList = fibSeq |> Seq.takeWhile (fun x -> x<=400 ) |> Seq.toList

for explanation, please ref solution 2 in F# for Project Euler Problems, where the first 50 Euler problems are solved. I think you will be interested in these solutions.

let rec fibSeq p0 p1 = seq {
    yield p0
    yield! fibSeq p1 (p0+p1)
}
Stephen Swensen

Here's an infinite tail-recursive solution using sequence expressions. It's quite efficient, producing the 100,000th term in just a few seconds. The "yield" operator is just like C#'s "yield return", and the "yield!" operator may be read as "yield all", where in C# you would have to do "foreach item ... yield return item".

https://stackoverflow.com/questions/2296664/code-chess-fibonacci-sequence/2892670#2892670

let fibseq =    
    let rec fibseq n1 n2 = 
        seq { let n0 = n1 + n2 
              yield n0
              yield! fibseq n0 n1 }
    seq { yield 1I ; yield 1I ; yield! (fibseq 1I 1I) }

let fibTake n = fibseq |> Seq.take n //the first n Fibonacci numbers
let fib n = fibseq |> Seq.nth (n-1) //the nth Fibonacci number

This approach is similar to the following in C# (which uses a while(true) loop instead of recursion):

Finding Fibonacci sequence in C#. [Project Euler Exercise]

Yes, mutable variables and while loops are usually a good sign that your code is not very functional. Also the fibonacci series, doesn't start with 1,2 - it starts with 0,1 or 1,1 depending on who you ask.

Here's how I'd do it:

let rec fabListHelper (a:int,b:int,n:int) =
  if a+b < n then
    a+b :: fabListHelper (b, a+b, n)
  else
    [];;

let fabList (n:int) = 0 :: 1 :: fabListHelper (0,1, n);;

(*> fabList 400;;
val it : int list = [0; 1; 1; 2; 3; 5; 8; 13; 21; 34; 55; 89; 144; 233; 377]*)

This function "fib" will return a list of Fibonacci numbers that are not greater than 500

let rec fib a b =
    let current = a + b
    match current with
    | _ when current >= 500 -> []
    | _ -> current :: fib b current 

let testFib = fib 1 2;;

One using aggregation (fold):

let fib n = 
  [1..n] |> List.fold (fun ac _ -> (ac |> List.take 2 |> List.sum) :: ac) [1;1] |> List.rev

Here's a good article by .Net guru Scott Hanselman on generating fibonacci series in F#

let rec fib n = if n < 2 then 1 else fib (n-2) + fib(n-1)

http://www.hanselman.com/blog/TheWeeklySourceCode13FibonacciEdition.aspx

It also compares with other languages as a reference

One more codata'ish way:

let rec fib = seq {
  yield! seq {0..1}
  yield! Seq.map (fun(a,b)->a+b) <| Seq.zip fib (Seq.skip 1 fib)
}
let a = fib |> Seq.take 10 |> Seq.toList

One with an array:

let fibonacci n = [|1..n|] |> Array.fold (fun (a,b) _ -> b, a + b) (0,1) |> fst

The great solution of Scott Hanselman does not report the 0 the fibonacci sequence starts with.

So here is a minor change to his solution to also report the 0. I used a small list from 0 to 10 to display the first 11 items of the sequence.

let nums=[0..10]
let rec fib n = if n < 1 then 0 else if n < 2 then 1 else fib (n-2) + fib(n-1)
let finres = List.map fib nums
printfn "%A" finres

I'm new and incompetent in relation to f# and still not totally fully understanding the need of it. But found this an interesting test.

Just for fun : If found a formula of Binet that calculates the n-th Fibonacci number. Unfortunately some floating point functions are needed to get the integer result back in the end : [Fibonacci formula of Binet][1]

http://i.stack.imgur.com/nMkxf.png

let fib2 n = (1.0 / sqrt(5.0)) * ( (((1.0 + sqrt(5.0)) /2.0)**n)  -  (((1.0 -  sqrt(5.0)) /2.0)**n) )
let fib2res = fib2 10.0
System.Console.WriteLine(fib2res)
let strLine = System.Console.ReadLine()

A quick and dirty translation to f# would be as shown above. I'm sure others can improve on this in matter of style and efficiency. The example calculates the 10th number. The result will be 55.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!