“ let rec(-)x y = y-x in 1-2-3”在做什么?

时间:2019-09-26 12:19:36

标签: ocaml

在有关ocaml的书中,我看到了let (-) x y = y - x in 1 - 2 - 3let rec (-) x y = y - x in 1 - 2 - 3这两个例子。当我看到前一个功能时,我以为我理解了后面的技巧,直到看到了后者的功能。似乎后一个函数有一些堆栈溢出问题,但是为什么会这样呢? ocaml如何分别评估这两个表达式?

2 个答案:

答案 0 :(得分:1)

重命名let绑定函数可能会有所帮助。

let (-) x y = y - x
in 1 - 2 - 3

(* The above expression is equivalent to the following. *)

let f x y = y - x
in f (f 1 2) 3

(* Which reduces to the following. *)

let f x y = y - x
in 3 - (2 - 1)

请注意,我们定义的功能let (-) x y与我们在定义y - x中使用的功能不同。这是因为没有let的{​​{1}}不会在定义内绑定函数名称。因此,定义中的rec运算符是本机减号运算符。因此,结果为(-)

现在,考虑第二个示例。

2

现在,let rec (-) x y = y - x in 1 - 2 - 3 (* The above expression is equivalent to the following. *) let rec f x y = f y x in f (f 1 2) 3 减少到什么?它减少到f 1 2,减少到f 2 1,减少到f 1 2,依此类推。现在,在没有尾调用优化的语言中,这将导致堆栈溢出错误。但是,在OCaml中,它应该永远运行而不返回。

答案 1 :(得分:0)

let表达式的语法为let <name> = <expr1> in <expr2>,它定义<name>绑定到<expr1>中的<expr2><name>本身在<expr1>的范围内不可见,换句话说,默认情况下它不是递归的。并且该功能可以(经常使用)为相同的名称赋予新的含义,例如,这是规范的OCaml代码,

let example () = 
  let name = "Alice" in
  let name = "Professor " ^ name in
  print_endline name

let (-) x y = y - x in 1 - 2 - 3中使用了相同的技术方法,其中我们根据原始(-)运算符重新定义了(-),而该运算符在{{1 }}表达式。

但是,当我们在let定义中添加y - x关键字时,该名称会在当前定义的表达式的范围内立即显示,例如

rec

let rec (-) x y = y - x (* ^ | *) (* | | *) (* +----------+ *) 中的-是指当前定义的函数,因此我们有一个递归定义,表示x减y为y减x-虚假定义。