OCaml中短路运算符和let的求值顺序

3
在OCaml中,当使用let为短路操作符(&&||)赋一个别名时,它不再短路操作数的求值。
这并不直观。这种行为的原因是什么?
考虑下面的代码:
let f() = Printf.printf "f"; false;;
let g() = Printf.printf "g"; true;;
let a = (&&);;

f() && g();; (* outputs 'f' *)

(&&) (f()) (g());; (* outputs 'f' *)

a (f()) (g());; (* outputs 'gf' *)

这也发生在使用let ... in时,因此let b = (&&) in b (f()) (g());;也会输出gf

2个回答

5
因为&&是一个原生操作符,其语义与普通函数相当不同。实际上,(&&)或多或少等价于fun x y -> x && y,即在应用它们之前评估其参数(以未指定的顺序,通常是从右到左,但您不应该依赖它)。
您可以通过使用ocaml -dlambda来查看这一点。这将启动一个解释器,输出每个输入命令的中间语言翻译。然后,您将获得以下结果:
# (&&);;
(function prim/1044 prim/1043 (&& prim/1044 prim/1043))
- : bool -> bool -> bool = <fun>
lambda格式没有文档说明,但是它应该足够清晰,以至于能够理解eta扩展正在发生。

1
我从未尝试过通过ocaml toplevel使用-dlambda,很不错。 - nlucaroni

2
评估是不懒惰的,因此在应用为函数参数之前将对fg进行评估。

但是手册(http://caml.inria.fr/pub/docs/manual-ocaml/expr.html#sec138)指出,运算符`&&`和`||`是特殊的(短路),并且在第一个示例中表现得像这样。手册还指出: 注意: 运算符&&、||和~-被特殊处理,不建议改变它们的含义。 但似乎不清楚为什么let会干扰这个。 - anol
因为 let 并不特殊。但是 external a : bool -> bool -> bool = "%sequand" 可以正常工作。 - nlucaroni
@nlucaroni,这个OCaml 4.01版本对我不起作用。实际上,我想说除了使用"bool Lazy, t"参数,很难定义一个能够展示这种行为的函数。"&&"是一种原始运算符,由运行时特殊处理。 - Virgile
我正在使用4.01.0版本并验证了其行为。也许你复制函数时出现了错误。是的,通常需要懒惰加载。 - nlucaroni
@nlucaroni,Virgile的回答对我来说更清晰,所以我选择了它,但是你的"%sequand"在我的OCaml 4.00.0上也可以工作。不过我希望我永远不需要使用这样黑暗的咒语。 - anol
@nlucaroni 是的,你说得对,我第一次检查时可能出了点问题。 - Virgile

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接