我是F#的新手(今天才开始学习),我尝试递归实现斐波那契序列。我不确定这在语法上还是逻辑上是错误的
let rec fibonacciRecursive (x : int) = seq{
match x with
| 0 -> yield! [0]
| 1 -> yield! [0; 1]
| _ -> yield! fibonacciRecursive
|> Seq.pairwise
|> Seq.map (fun (prev, next) -> prev + next)
|> Seq.toList
}
答案 0 :(得分:1)
使用方便的Seq.unfold
函数(这种情况下设计的方法),您尝试创建斐波那契数列的方式会更好地工作。您向它传递一个函数和一个初始状态,然后反复调用该函数以根据当前状态计算序列的下一个值。它的参数以Seq.unfold function initialState
的顺序给出,但是我通常更喜欢将其写为initialState |> Seq.unfold function
。函数返回None
停止序列,或者返回Some (nextValue, nextState)
继续序列(可以创建无限序列)。
对于斐波那契数列,用Seq.unfold
创建它看起来像这样:
let fibUnfold = (0,1) |> Seq.unfold (fun (a, b) -> Some(b+a, (b, b+a)))
let fib = seq { yield 0; yield 1; yield! fibUnfold }
for i in fib |> Seq.take 10 do // Infinite sequence, so take a finite number
printf "%d, "
printfn ""
// Prints "0, 1, 1, 2, 3, 5, 8, 13, 21, 34, "
答案 1 :(得分:1)
递归创建序列的方法是创建一个函数,该函数从某个“状态”(此处的continueFrom
状态为a
和b
)开始创建序列。该函数应产生一个项目,然后以与下一个项目的顺序相对应的状态产生自身。
let fibonacciSequence =
let rec continueFrom a b =
seq {
yield a
yield! continueFrom b (a + b)
}
continueFrom 0 1
请注意,fibonacciSequence
是无限序列,这意味着您不能直接将其转换为列表。如果您想要第一个n
个项目,可以使用Seq.take n fibonacciSequence
。
答案 2 :(得分:0)
正如Fyodor所指出的那样,在第三种match
情况下存在语法错误,因为您实际上没有将参数传递给fibonacciRecursive
函数。但是,如果通过传递x
来解决此问题,则会发现函数在执行时抛出StackOverflowException
。这是因为您要递归地尝试为每个值生成整个序列。
F#中斐波那契的一个非常简单的解决方案如下:
let rec fib = function
| 0 -> 0
| 1 -> 1
| n -> fib (n - 1) + fib (n - 2)
但是,这最终也会导致堆栈溢出,因为调用不是“尾递归”,这意味着递归不是函数中最后发生的事情(在这种情况下,加法不是最后发生的事情) )。您可以通过延后每个元素的执行来制作尾部递归解决方案,以使递归调用位于尾部位置:
let fib n =
let rec fib cont = function
| n when n < 2 -> cont n
| n -> (n - 1) |> fib (fun x -> (n - 2) |> fib (fun y -> cont (x + y)))
fib id n
这有点难以理解,而且通常需要一段时间才能习惯于以这种方式编写函数。这是一个使用双反引号的版本,允许在参数中使用描述性名称作为延续,如果这有助于了解正在发生的情况:
let fib n =
let rec fib cont = function
| n when n < 2 ->
cont n
| n ->
(n - 1) |> fib (fun ``fib (n-1)`` ->
(n - 2) |> fib (fun ``fib (n-2)`` ->
cont (``fib (n-1)`` + ``fib (n-2)``)))
fib id n
答案 3 :(得分:0)
正如其他人已经指出的那样,您在递归调用中遇到了问题(您忘记了调用的参数)。
正如Aaron在他的回答中所指出的那样,希望您最终获得实现的尾部递归版本,以避免堆栈溢出。
这是我要怎么做:
只要您有一些不是尾递归的递归函数(它以f x (recCall y)
结尾,就会为自己创建一个看起来像let rec subF x y
的递归子函数,即,您将原始的尾部移动代码添加到您的助手子功能subf
中)。对于斐波那契,这就是我的情况:
let fib n =
let rec worker state x =
if x = 0 then state
else
match state with
| [] -> worker [0] (x-1)
| [0] -> worker [1;0] (x-1)
| (a::b::_) -> worker ((a+b)::state) (x-1)
worker [] n
而且-很容易看到,子功能worker
的递归调用是尾递归(在递归调用之后没有其他作用)。