对于这个问题,这个问题是一个后续问题:How to make a covariant observable in 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; }
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; }
他的陈述是否正确?如果是这样,为什么第二个版本更“经济”?
答案 0 :(得分:5)
正如我在评论中所说,似乎通过使用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 ()
另一方面,test2.ml
使用let rec
:
let test2 x =
let x = 5 in
let rec w () = x + 1
and n () = x + 1 in
w () + n ()
我然后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!
文件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!
来自Caml Virtual Machine — Instruction set Document(不幸的是,我不知道它是否是正式的,但看起来很合理),似乎closure
和closurerec
指令生成了关闭堆栈。如您所见,test1.ml
的字节码总共生成了3个闭包,而test2.ml
只生成了两个闭包(一个来自closurerec
)。
我不是汇编大师,但你可以测试ocamlopt -S filename.ml
,以便编译器离开(并且不删除)程序集(然后在filename.s
中),在那里你可以发现类似的差异,同样。