Recursion 如何表示阶乘n!使用F#函数,递归还是其他?

Recursion 如何表示阶乘n!使用F#函数,递归还是其他?,recursion,f#,tail-recursion,factorial,Recursion,F#,Tail Recursion,Factorial,自然数的阶乘(任何大于或等于0)是该数乘以自身的阶乘减1,其中0的阶乘定义为1 例如: 0! = 1 1! = 1 * 0! 2! = 2 * 1! 3! = 3 * 2! 4! = 4 * 3! 5! = 5 * 4! let factorial (num:int) = seq { for n in [1..num] -> n } |> Seq.reduce (fun acc n -> acc * n) 另一种编写方法是将1和n之间的所有自然数相乘n: 5

自然数的阶乘(任何大于或等于
0
)是该数乘以自身的阶乘减1,其中
0
的阶乘定义为
1

例如:

0! = 1
1! = 1 * 0!
2! = 2 * 1!
3! = 3 * 2!
4! = 4 * 3!
5! = 5 * 4!
let factorial (num:int) =
    seq { for n in [1..num] -> n }
    |> Seq.reduce (fun acc n -> acc * n)
另一种编写方法是将
1
n
之间的所有自然数相乘
n

5! = 1 * 2 * 3 * 4 * 5
我如何用F#中的递归函数来表示这一点?我应该用递归函数吗

//Factorials!
let factorial n = 
    result = ?
如何,备选方案1:

let rec factorial n =
    match n with
    | 0 | 1 -> 1
    | _ -> n * factorial(n-1)
如何,选项2(尾部递归,编译成循环):

应:否,请参见我对以下问题的回答:

我经常主张避免迭代和递归,而采用高阶函数。但如果你刚刚开始,也许还不必太担心这个建议。(但请参见例如@ChaosPandion的答案,或

let factorial n = [1..n] |> List.fold (*) 1

甚至:

let factorial n = [1..n] |> List.reduce (*) // doesn't require the 2nd parameter
我将如何为此声明递归函数

首先,要定义递归函数,可以使用
let rec
而不是
let
(因为
let
不允许引用递归定义的函数)

要递归定义阶乘函数,最简单(但不是最有效)的方法是使用阶乘函数的标准数学定义


更有效的方法是使用第二个参数定义尾部递归辅助函数,该参数存储迄今为止计算的结果。

这里是另一个示例:

0! = 1
1! = 1 * 0!
2! = 2 * 1!
3! = 3 * 2!
4! = 4 * 3!
5! = 5 * 4!
let factorial (num:int) =
    seq { for n in [1..num] -> n }
    |> Seq.reduce (fun acc n -> acc * n)
这个例子可能更清楚一些:

let factorial num =
    [1..num] |> Seq.fold (fun acc n -> acc * n) 1

Brian的答案最实用,但以下是延续传球风格的解决方案:

let rec factorial n = 
  let rec loopk i k = 
    match i with
    | 0 | 1 -> k i
    | _ -> loopk (i-1) (fun r -> k (i * r))
  in loopk n (fun r -> r)
我最喜欢的递归序列的F#解决方案是……无限、尾部递归序列!:

let factSeq =    
    let rec factSeq m n = 
        seq { let m = m * n
              yield m
              yield! factSeq m (n+1I) }
    seq { yield 1I ; yield 2I ; yield! (factSeq 2I 3I) }

let factTake n = factSeq |> Seq.take n //the first n terms
let fact n = factSeq |> Seq.nth (n-1) //the nth term
我在这里使用大整数,因为阶乘序列增长非常快(继续,试试第20000项)


我大体上同意Brian的建议,尽可能在迭代循环或递归循环(尾部递归+累加器)上使用高阶函数。但我认为在这种情况下,我所展示的无限序列更灵活,因为它生成的阶乘序列的所有项都达到了所需项(
factTake
),并且每个项只需要一个乘法步骤(n*(n-1))。然而,如果您希望使用折叠解决方案获得前n个项,则每个计算都将独立完成,并且不会受益于之前的计算。

这里是一个更简单的实现

let rec bfact (n):bigint = 
    match n with
        | i when i<0 -> bigint.Zero
        | 0 | 1 -> bigint(1)
        | _ -> ( bfact(n-1) * bigint(n) )

它是5!而不是!5。
!n
通常表示
n
对象的数量。根据我今天早上在浏览F标记时在StackOverflow上读到的内容,我实际上可以跳过'rec'关键字,但我必须明确声明数据类型。如果我错了,请纠正我。@Serg:你错了。如果你不使用
rec
您正在定义的函数在其定义中是不可访问的。您需要说“rec”来定义递归函数,否则函数的名称标识符将不在范围内。请参阅+1总结。Serg,第二个答案将更有效。感谢帮助。我不是复制粘贴程序编码的人,我想了解更多w第二位是这样的。特别是“匹配”关键字和“|”符号。还有,|符号是什么?非常感谢,希望这个问题能帮助其他人。“模式匹配”将需要一点时间来学习/理解所有内容,请参阅文档:或参阅了解更多关于模式匹配的信息。它在阶乘500上返回0。这是为什么?有什么原因不仅仅是“for i in 1..num->i”或列表[1..i]如果我没有实现ECMAScript正则表达式的话,这对我来说会是一件令人费解的事情。规范中使用了这种风格。