在 F# 中生成斐波那契数列

发布于 2024-09-01 09:57:49 字数 454 浏览 14 评论 0 原文

我刚刚开始使用 VS2010 学习 F#,下面是我第一次尝试生成斐波那契数列。我想做的是建立一个包含小于 400 的所有数字的列表。

let fabList = 
    let l =  [1;2;]
    let mutable a = 1
    let mutable b = 2
    while l.Tail < 400 do
        let c = a + b
        l.Add(c)
        let a = b
        let b = c

我的第一个问题是,在最后一个语句中,我在表达式中收到一条错误消息“在表达式中的这一点或之前的结构化构造不完整”最后一行。我不明白我在这里做错了什么。

虽然这似乎是一种以相当有效的方式构建列表的明显方法(来自 c++/C# 程序员),但根据我对 f# 的了解,这似乎不是执行该程序的正确方法。我的这种感觉正确吗?

I'm just starting to learn F# using VS2010 and below is my first attempt at generating the Fibonacci series. What I'm trying to do is to build a list of all numbers less than 400.

let fabList = 
    let l =  [1;2;]
    let mutable a = 1
    let mutable b = 2
    while l.Tail < 400 do
        let c = a + b
        l.Add(c)
        let a = b
        let b = c

My first problem is that on the last statement, I'm getting an error message "Incomplete structured construct at or before this point in expression" on the last line. I don't understand what I'm doing wrong here.

While this seems to be an obvious way to build the list in a fairly efficient way (from a c++/C# programmer), from what little I know of f#, this doesn't seem to feel to be the right way to do the program. Am I correct in this feeling?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

悲喜皆因你 2024-09-08 09:57:49

其他帖子告诉您如何使用递归函数编写 while 循环。这是使用 F# 中的 Seq 库的另一种方法:

// 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

作为解释,请参考 解决方案 2 中的 F# for Project Euler Problems,其中解决了前 50 个欧拉问题。我想您会对这些解决方案感兴趣。

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.

蝶舞 2024-09-08 09:57:49

首先,您使用 let 就好像它是一个改变变量的语句,但事实并非如此。在 F# 中,let 用于声明新值(这可能会隐藏任何先前的同名值)。如果您想使用突变来编写代码,那么您需要使用类似的内容:

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

代码的第二个问题是您尝试通过向 F# 列表添加元素来突变 F# 列表 - F# 列表是不可变的,因此一旦创建它们,您无法修改它们(特别是,没有 Add 成员!)。如果您想使用突变来编写此代码,您可以编写:

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

但是,正如其他人已经指出的那样,以这种方式编写代码并不是惯用的 F# 解决方案。在 F# 中,您将使用不可变列表和递归而不是循环(例如 while)。例如这样:

// 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)

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)
孤单情人 2024-09-08 09:57:49
let rec fibSeq p0 p1 = seq {
    yield p0
    yield! fibSeq p1 (p0+p1)
}
let rec fibSeq p0 p1 = seq {
    yield p0
    yield! fibSeq p1 (p0+p1)
}
负佳期 2024-09-08 09:57:49

这是使用序列表达式的无限尾递归解决方案。它非常高效,只需几秒钟即可生成第 100,000 个项。 “yield”运算符就像C#中的“yield return”,以及“yield!”运算符可能被理解为“yield all”,在 C# 中,您必须执行“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

此方法类似于 C# 中的以下方法(使用 while(true) 循环而不是递归):

在 C# 中查找斐波那契数列。 [欧拉计划练习]

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]

像你 2024-09-08 09:57:49

是的,可变变量和 while 循环通常是一个好兆头,表明您的代码功能不是很好。另外,斐波那契数列不是以 1,2 开头,而是以 0,1 或 1,1 开头,具体取决于您问的是谁。

我是这样做的:

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]*)

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]*)
等你爱我 2024-09-08 09:57:49

此函数“fib”将返回不大于 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;;

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;;
旧城空念 2024-09-08 09:57:49

一种使用聚合(折叠):

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

One using aggregation (fold):

let fib n = 
  [1..n] |> List.fold (fun ac _ -> (ac |> List.take 2 |> List.sum) :: ac) [1;1] |> List.rev
勿挽旧人 2024-09-08 09:57:49

一个带有数组:

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

One with an array:

let fibonacci n = [|1..n|] |> Array.fold (fun (a,b) _ -> b, a + b) (0,1) |> fst
甜是你 2024-09-08 09:57:49

这是 .Net 大师 Scott Hanselman 撰写的一篇关于在 F# 中生成斐波那契数列的好文章

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

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

还与其他语言进行比较作为参考

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

同尘 2024-09-08 09:57:49

另一种类似的方式:

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 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
情话墙 2024-09-08 09:57:49

Scott Hanselman 的伟大解决方案不报告斐波那契数列以 0 开头。

所以这里对他的解决方案做了一个小小的改变,也报告了 0。
我使用了一个从 0 到 10 的小列表来显示序列的前 11 项。

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

我是新手,对 f# 不称职,而且仍然没有完全完全理解它的需要。但发现这是一个有趣的测试。

只是为了好玩:如果找到一个计算第 n 个斐波那契数的比奈公式。
不幸的是,需要一些浮点函数才能最终返回整数结果:
[比奈斐波那契公式][1]

https://i.sstatic.net/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()

对 f# 的快速而肮脏的翻译将如上所示。我相信其他人可以在风格和效率方面对此进行改进。该示例计算第 10 个数字。结果将为 55。

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]

https://i.sstatic.net/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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文