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正则表达式的话,这对我来说会是一件令人费解的事情。规范中使用了这种风格。