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

Function 什么';在F#中将递归函数标记为rec的原因是什么?,function,f#,recursion,Function,F#,Recursion,我不确定这是否是一个愚蠢的问题,但我正在阅读VS 2010附带的教程,其中有一个函数如下: let rec factorial n = if n=0 then 1 else n * factorial (n-1) 这个递归函数被标记为rec关键字的原因是什么 这样编译器就可以保证它是递归的,这样就可以进行某些优化了吗 如果将其排除在外会发生什么?根据MSDN,这只是一种静态必要性: 递归函数 称自己为,被识别 用F#语言明确表示。这 生成正在使用的标识符 定义在的范围内可用 功能 根据克里斯·

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

let rec factorial n = if n=0 then 1 else n * factorial (n-1)
这个递归函数被标记为rec关键字的原因是什么

这样编译器就可以保证它是递归的,这样就可以进行某些优化了吗


如果将其排除在外会发生什么?

根据MSDN,这只是一种静态必要性:

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

根据克里斯·史密斯(在F#团队工作)的说法-


它是通知类型推理系统,允许将函数用作类型推理过程的一部分。rec允许您在类型推断系统确定函数的类型之前调用函数。这是必要的,以便函数可以递归。非rec函数只知道定义位置的绑定,而不知道后面的绑定(因此它不知道自己的情况)。

这可能是有指导意义的:

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
仅在定义了标识符的主体后才将标识符放入范围

规则的动机确实源于与类型推理的交互

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

告诉编译器函数体中函数名的任何用法都递归地引用它,而不是以前定义的同名值

这样编译器就可以保证它是递归的,这样就可以进行某些优化了吗

没有

如果排除它会发生什么


您将失去正在定义的函数在其函数体中引用自身的能力,并获得引用以前定义的同名值的能力。

是否可能重复?感谢他们看起来很相似,我也会读到。否则类型推断系统可能会进入无限循环?如果这就是原因,那就有意义了。真的,就这些吗?考虑到这一点,我想知道如果我显式地定义了所有类型,我是否可以编写一个递归函数而不使用
rec
。。。实验时间!如果没有
rec
,您还必须事先定义函数本身。但是您必须首先定义函数,因为它在自己的范围内不可用。但首先,您必须定义函数。定义函数之后。一旦定义了函数。这需要定义函数。赵:是的,这是我认为不可行的。实际上,它允许你在定义它的同时使用它。你说的绑定是什么意思?它像作用域吗?@Joan Venge:绑定基本上是一个变量名及其关联值。在本例中,这意味着您不能在函数本身内部使用该函数,因为它只绑定到
let
后面的名称
let rec
使绑定的名称在绑定表达式本身中可用。这是一个关于绑定、阴影和作用域的非常有教育意义的示例:-)。+1但“规则的动机确实源于与类型推断的交互”我需要澄清一下。你已经就这个话题写了好几个答案和评论,但我觉得这是最有启发性的。它的俳句般的简洁让它的内容可以立即被理解,这一点在你更详细的帖子中被部分地忽略了。