对于这个问题,这个问题是一个后续问题:如何在OCaml中进行协变可观察
接受的答案的作者(随便)指出,使用let rec绑定两个独立的值比两个单独的let绑定更"经济" .
let make x =
let queue = Queue.create () in
let obj = x in
let watch f = Queue.add f queue in
let notify () = Queue.iter (fun f -> f x) queue in
{ obj; watch; notify; }
Run Code Online (Sandbox Code Playgroud)
VS
let make x =
let queue = Queue.create () in
let obj = x in
let rec watch f = Queue.add f queue
and notify () = Queue.iter (fun f -> f x) queue in
{ obj; watch; notify; }
Run Code Online (Sandbox Code Playgroud)
他的陈述是否正确?如果是这样,为什么第二个版本更"经济"?
正如我在评论中所说,似乎通过使用let rec,您可以避免创建更多的闭包.为了检查这一点,我创建了两个略有不同的文件:
test1.ml有"通常"的方式没有let rec:
let test1 x =
let x = 5 in
let w () = x + 1 in
let n () = x + 1 in
w () + n ()
Run Code Online (Sandbox Code Playgroud)
另一方面,test2.ml用途let rec:
let test2 x =
let x = 5 in
let rec w () = x + 1
and n () = x + 1 in
w () + n ()
Run Code Online (Sandbox Code Playgroud)
然后我将ocamlc -dinstr两个文件(即我为两个文件生成字节码)并获得以下内容:
因为test1.ml,我得到了:
branch L2
L3: envacc 1
offsetint 1
return 1
L4: envacc 1
offsetint 1
return 1
L1: const 5
push
acc 0
closure L4, 1
push
acc 1
closure L3, 1
push
const 0a
push
acc 1
apply 1
push
const 0a
push
acc 3
apply 1
addint
return 4
L2: closure L1, 0
push
acc 0
makeblock 1, 0
pop 1
setglobal Closuretest!
Run Code Online (Sandbox Code Playgroud)
该文件test2.ml导致以下结果:
branch L2
L3: envacc 3
offsetint 1
return 1
L4: envacc 1
offsetint 1
return 1
L1: const 5
push
acc 0
closurerec 3 4, 1
const 0a
push
acc 1
apply 1
push
const 0a
push
acc 3
apply 1
addint
return 4
L2: closure L1, 0
push
acc 0
makeblock 1, 0
pop 1
setglobal Closuretest2!
Run Code Online (Sandbox Code Playgroud)
从一个Caml虚拟机 - 指令集文件(不幸的是,我不知道它是否是官方的,但看起来合理),似乎是指令closure并closurerec在堆栈上生成一个闭包.如您所见,字节码test1.ml总共生成3个闭包,而test2.ml只生成两个闭包(一个过孔closurerec).
我不是汇编大师,但你可以测试ocamlopt -S filename.ml,以便编译器离开(并且不删除)程序集(然后进入filename.s),在那里你也可以发现类似的差异.