在F#序列中需要帮助

时间:2018-09-24 00:36:26

标签: f#

我是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
        }

4 个答案:

答案 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状态为ab)开始创建序列。该函数应产生一个项目,然后以与下一个项目的顺序相对应的状态产生自身。

 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 的递归调用是尾递归(在递归调用之后没有其他作用)。