在这个例子中惰性求值是如何工作的?

sth*_*55 2 ocaml functional-programming

如果我有这个函数: let x = print_endline "foo" in x,x;; 它有这个签名:unit * unit = ((), ()) 并且评估为foo

我不明白为什么它打印 foo 一次。

另一方面:let x () = print_endline "foo" in x(),x();; 将打印 foo 两次并具有签名: unit * unit = ((), ())

有人可以解释一下这背后的原理以及它在这个具体示例中的工作原理吗?

ivg*_*ivg 7

OCaml is an eager language unless directed otherwise. To create lazy values we use the lazy keyword, e.g., lazy (print_endline "foo") will create a lazy value of type unit Lazy.t that will print foo, once it is forced with Lazy.force.

All other values in OCaml are eager and are evaluated immediately. You got confused with the let .. in ... syntactic form.

The syntactic form

let <var> = <expr1> in <expr2>
Run Code Online (Sandbox Code Playgroud)

first evaluates <expr1> and then evaluates <expr2> with the value of <expr1> bound to <var>. Finally, the result of <expr2> becomes the value of the whole form.

So when you say, let x = 2 in x + x you first evaluate 2 and bind it to x and then evaluate x + x with x=2, which is 4, so the value of the expression let x = 2 in x + x in 4. This is a pure expression, it doesn't define any functions and doesn't bind x in the global context.

Going back to your example,

let x = print_endline "foo" in x, x
Run Code Online (Sandbox Code Playgroud)

我们首先评估<expr1>,它print_endline "foo"具有打印的副作用并返回type 的foo值。接下来,绑定到并对进行求值,其中、 和 等于。()unit()x<expr2>x,x(),()

你的第二个例子,

let x () = print_endline "foo" in x (), x ()
Run Code Online (Sandbox Code Playgroud)

绑定x到函数,fun () -> print_endline "foo"因为

let f x = <expr>
Run Code Online (Sandbox Code Playgroud)

是语法糖

let f = fun x -> <expr>
Run Code Online (Sandbox Code Playgroud)

即函数的定义。

因此,您的表达式首先计算<expr1>,即 nowfun () -> print_endline "foo"并将其绑定到x。现在您评估x (), x ()并调用x ()两次,两次调用都返回()

最后,你在问题中说,

如果我有这个函数:let x = print_endline "foo" in x,x;;

你那里没有任何功能。与在 中非常相似let x = 2 in x + x,您没有定义任何函数,而只是x+x使用x绑定到进行计算2。评估后,没有任何残留,x不与任何东西绑定。在类似 C 的语法中,它与

{
   int x = 2;
   x + x;
}
Run Code Online (Sandbox Code Playgroud)

所以如果你想定义一个函数,你必须使用语法

let <name> <params> = 
   <expr>
Run Code Online (Sandbox Code Playgroud)

其中<expr>是在每个参数都替换为其实际值(参数)的上下文中计算的函数体。

例如,

let print_foo () = 
  print_endline "foo"
Run Code Online (Sandbox Code Playgroud)

或者,

let sum_twice x y = 
  let z = x + y in
  2 * z
Run Code Online (Sandbox Code Playgroud)

我建议阅读OCaml 教程以更好地了解 OCaml 语法。