在OCaml中,当使用let
为短路运算符(&&
或||
)分配别名时,它不再对操作数进行短路评估。 / p>
这不直观。这种行为的原因是什么?
请考虑以下代码:
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
。
答案 0 :(得分:5)
这是因为&&
是一个原始运算符,其语义与普通函数完全不同。实际上,(&&)
或多或少等同于fun x y -> x && y
,正如nlucaroni所解释的那样,在它们被应用之前会评估它的参数(以未指定的顺序,通常是从右到左,但你不应该依赖它)。
您可以使用ocaml -dlambda
查看。这将启动一个解释器,它以您输入的每个命令的一种中间语言输出翻译。然后,您将得到以下结果:
# (&&);;
(function prim/1044 prim/1043 (&& prim/1044 prim/1043))
- : bool -> bool -> bool = <fun>
lambda
格式没有记录,但应该很清楚,eta扩展正在发生。
答案 1 :(得分:2)
评估不是懒惰的,因此{<1}}和f
将在之前评估,它们将作为参数应用于您的函数。