在F#中将递归函数标记为rec的原因是什么?

时间:2010-09-17 22:58:12

标签: function f# recursion

我不确定这是否是一个愚蠢的问题,但我正在阅读VS 2010附带的教程,并且有一个这样的函数:

let rec factorial n = if n=0 then 1 else n * factorial (n-1)

这个递归函数用rec关键字标记的原因是什么?

是否可以确保编译器具有递归性,因此可以进行某些优化?

如果排除它会怎样?

5 个答案:

答案 0 :(得分:28)

这可能很有启发性:

let Main() =
    let f(x) = 
        printfn "original f: %d" x
    let f(x) =
    //let rec f(x) =
        printfn "entered new f: %d" x
        if x > 0 then
            f(x-1)
        else
            printfn "done"
    f(3)
Main()

打印

entered new f: 3
original f: 2

现在,如果我们发表评论let并取消注释let rec,那么就打印

entered new f: 3
entered new f: 2
entered new f: 1
entered new f: 0
done

从这个角度来看,它只是名称绑定; let rec立即将标识符放在范围内(在此示例中,隐藏前一个f),而let仅在定义了主体后才将标识符放在范围内。

规则的动机源于与类型推断的互动。

答案 1 :(得分:11)

据Chris Smith说(在F#团队工作) -

通知类型推断系统允许该函数用作类型推断过程的一部分。 rec允许您在类型推断系统确定函数类型

之前调用该函数

答案 2 :(得分:8)

根据MSDN,它只是一种合成必需品:

  

递归函数,函数   称自己为自己   明确地用F#语言。这个   使正在形成的标识符   定义在范围内可用   功能

http://msdn.microsoft.com/en-us/library/dd233232.aspx

答案 3 :(得分:6)

  

这个递归函数用rec关键字标记的原因是什么?

告诉编译器函数体内函数名的任何使用都是递归引用它而不是先前定义的同名值。

  

是否可以确保编译器具有递归性,因此可以进行某些优化?

没有

  

如果排除它会怎样?

您失去了定义的函数在其函数体中引用自身的能力,并且能够引用先前定义的同名值。

答案 4 :(得分:5)

有必要使函数可以递归。非rec函数只知道它定义的地方的绑定,而不是之后(因此它不知道自己)。