Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# F中Fibonacci级数的生成#_F#_Fibonacci_Tail Recursion - Fatal编程技术网

F# F中Fibonacci级数的生成#

F# F中Fibonacci级数的生成#,f#,fibonacci,tail-recursion,F#,Fibonacci,Tail Recursion,我刚刚开始学习F#使用VS2010,下面是我第一次尝试生成斐波那契级数。我想做的是建立一个少于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 let fabLi

我刚刚开始学习F#使用VS2010,下面是我第一次尝试生成斐波那契级数。我想做的是建立一个少于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
let fabList=
设l=[1;2;]
设可变a=1
设可变b=2
而l.Tail<400 do
设c=a+b
l、 加(c)
设a=b
设b=c
我的第一个问题是,在最后一条语句中,我在最后一行得到了一条错误消息“表达式中此点或之前的不完整结构化构造”。我不明白我做错了什么


虽然这似乎是一种以相当有效的方式(从c++/c程序员那里)构建列表的明显方式,但就我对f#所知甚少,这似乎不是编写程序的正确方式。我的这种感觉正确吗?

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

设rec fib n=如果n<2,则1 else fib(n-2)+fib(n-1)


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

是的,可变变量和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]*)
let rec fabListHelper(a:int,b:int,n:int)=
如果a+bFablist400;;
val-it:int-list=[0;1;1;2;3;5;8;13;21;34;55;89;144;233;377]*)

首先,您使用的是
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#中,您将使用不可变列表和递归而不是循环(例如
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)
//实现循环的递归函数
//(需要前面两个元素,a和b)
让rec fibsRec a b=
如果a+b<400,则
//当前元素
让电流=a+b
//递归计算所有剩余元素
//使用“b”作为“a”,使用“current”作为“b”(在下一次迭代中)
let rest=光纤断路器b电流
//返回附加了“current”的其余元素
//结果列表的前面(这将构造新列表,
//所以这里没有突变!)
当前::rest
其他的
[]//生成所有元素-完成后返回空列表
//生成包含1、2和所有其他较大fibonaccis的列表
设fibs=1::2::(fibsrec12)

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

//生成一个无限Fibonacci序列
让fibSeq=Seq.展开(乐趣(a,b)->一些(a+b,(b,a+b))(0,1)
//取序列中的前几个数字,并将序列转换为列表
让fibList=fibSeq |>Seq.takeWhile(乐趣x->x Seq.toList

为了解释,请参考,前50个Euler问题是在哪里解决的。我想你会对这些解决方案感兴趣。

这是一个使用序列表达式的无限尾递归解决方案。它非常有效,只需几秒钟就产生了100000个项。“产量”操作符就像C#的“产量回报”,而“yield!”操作符可能被理解为“yield all”,在C#中,您必须执行“foreach item…yield return item”

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


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#。我相信其他人可以在风格和效率方面对此进行改进。示例计算了第10个数字。结果将是55。

另一个codata'ish方法:

let rec fibSeq p0 p1 = seq {
    yield p0
    yield! fibSeq p1 (p0+p1)
}
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
让rec fib=seq{
屈服!seq{0..1}
收益率!顺序图(乐趣(a,b)->a+b)顺序取10 |>顺序列表
一个带阵列的:

let fibonacci n = [|1..n|] |> Array.fold (fun (a,b) _ -> b, a + b) (0,1) |> fst
此函数“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;;
一个使用聚合(折叠):


是的,您做错了。您使用的是函数式编程语言,就像过程式编程语言一样。请尝试在不使用
while
或任何类似循环构造的情况下进行此操作。a)这是一种非常低效的计算斐波那契数的方法,而b)使用它来创建列表的效率更低,因为您必须为列表中的每个项目重新计算整个过程。感谢您的详细回答。作为一种正常编程模式,不可变性是我仍在尝试使用的东西,您的解释是c对我来说,这是一个非常清晰的概念。更不用说函数式编程了。@photo_tom:这就是我们所有第一次有必要的背景的人的感受:-)。不变性是一个基本概念,其他的函数式概念都是从这个概念开始的。答案非常好。值得一提的是,作为一种通用方法在创建递归函数时,对函数本身的调用应该始终是该特定分支中的最后一个操作,以便可以执行尾部调用优化。在这种特定情况下,以400为限制,堆栈溢出通常不是一个问题。对于链接-对于F#项目Euler问题,我正在处理一些问题我认为第一行应该是:让fibSeq=Seq.unfold(乐趣(a,b)->一些(a
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()
let rec fibSeq p0 p1 = seq {
    yield p0
    yield! fibSeq p1 (p0+p1)
}
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
let fibonacci n = [|1..n|] |> Array.fold (fun (a,b) _ -> b, a + b) (0,1) |> fst
let rec fib a b =
    let current = a + b
    match current with
    | _ when current >= 500 -> []
    | _ -> current :: fib b current 

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