下面是一个简单的斐波那契函数:
let rec fib n =
match n with
| 0 -> 1
| 1 -> 1
| _ -> fib (n - 1) + fib (n - 2)
它的签名是 int -> int
。
可以重写为:
let rec fib =
fun n ->
match n with
| 0 -> 1
| 1 -> 1
| _ -> fib (n - 1) + fib (n - 2)
它的签名是 (int -> int)
(在 Visual Studio for Mac 中)。
那么这个与之前的有什么不同呢?
如果我再添加一行像这样的代码:
let rec fib =
printfn "fib" // <-- this line
fun n ->
match n with
| 0 -> 1
| 1 -> 1
| _ -> fib (n - 1) + fib (n - 2)
这个IDE为我提供了一个警告:
警告FS0040:通过使用延迟引用,将在运行时检查此对象及其他递归引用的初始化完整性。这是因为您正在定义一个或多个递归对象而不是递归函数。可以通过使用“#nowarn" 40“或”--nowarn:40“来抑制此警告。
这一行代码如何影响初始化?
“递归对象”是什么意思?我在文档中找不到相关解释。
更新
感谢您的回复,解释得非常好。
阅读了您的答案后,我对递归对象有了一些想法。
首先,我犯了一个错误,前两段代码片段具有相同的签名,int -> int
;但最后一个签名为(int -> int)
(请注意:在带有Ionide扩展的vscode中,这些签名具有不同的表示形式)。
我认为两个签名之间的差别在于,第一个签名表示它只是一个函数,而另一个签名表示它是一个函数的引用,即一个对象。
对于没有parameter-list
的每个let rec something
,它都是一个对象而不是函数,请参见definition函数,而第二个代码片段是一个例外,可能被编译器优化为函数。
一个例子:
let rec x = (fun () -> x + 1)() // same warning, says `x` is an recursive object
我能想到的唯一原因是编译器不够智能,它会因为递归对象而抛出警告,就像警告所示,
这是因为你定义了一个或多个递归对象,而不是递归函数
即使这种模式永远不会有任何问题。
let rec fib =
// do something here, if fib invoked here directly, it's definitely an error, not warning.
fun n ->
match n with
| 0 -> 1
| 1 -> 1
| _ -> fib (n - 1) + fib (n - 2)
你对此有何看法?