3

我这里有一些我不是 100% 理解的小问题:

let x = 1 in let x = x+2 in let x = x+3 in x

我知道这个表达式的结果是6,但只是想确定这个表达式的计算顺序;先计算哪一部分?

4

3 回答 3

11

您询问了表达式中的评估顺序let x=1 in let x=x+2 in ...。顺序是“从左到右”!当你有一个链时let a=b in let c=d in ...,评估的顺序总是从左到右。

但是,在您的示例中,有一个令人困惑的部分:您在每个构造中使用了相同的变量名 , 。这很令人困惑,因为您随后会看到类似的内容,这看起来像是在“重新定义”或“更改”的值。但是在 OCAML 中实际上并没有发生“x”的“改变”!正如上面已经指出的那样,这里发生的是每次都会引入一个新变量,因此您的示例完全等同于xletlet x=x+1xx

 let x = 1 in let y = x+2 in let z = y+3 in z;;

请注意,这里的评估顺序也是从左到右的。(在每个构造链中它总是从左到右let。)在您最初的问题中,您选择将所有这些新变量称为“x”而不是x,yz。这让大多数人感到困惑。最好避免这种编码风格。

但是我们如何检查我们是否正确地重命名了变量?为什么“让 x=1 在让 y=x+2 中”而不是“让 x=1 在让 x=y+2 中”?这x=x+2生意太混乱了!好吧,还有另一种理解评估的方式let x=aaa in bbb。构造

  let x=aaa in bbb

总是可以用以下适用于 的闭包代替aaa

  (fun x -> bbb) aaa

一旦你用这种方式重写它,你可以很容易地看到两件事:首先,OCAML 不会在闭包内评估“bbb”,直到“aaa”被评估。(出于这个原因,对的求值let x=aaa in bbb是先求值aaa然后再求值bbb,即“从左到右”。)其次,变量“x”被限制在闭包的主体中,因此“x”不可见在表达式“aaa”中。由于这个原因,如果“aaa”包含一个名为“x”的变量,它之前必须已经定义了一些值,并且它与闭包内的“x”无关。为清楚起见,最好用不同的名称调用此变量。

在您的示例中:

 let x=1 in let x=x+2 in let x=x+3 in x

被改写为

 (fun x -> let x=x+2 in let x=x+3 in x) 1

然后内部let构造也被重写:

 (fun x -> (fun x -> let x=x+3 in x) x+2 ) 1
 (fun x -> (fun x -> (fun x-> x) x+3) x+2 ) 1

现在让我们重命名每个函数内部的函数参数,我们可以随时在不改变代码含义的情况下这样做:

 (fun x -> (fun y -> (fun z -> z) y+3) x+2 ) 1

这与

 let x=1 in let y=x+2 in let z=y+3 in z

通过这种方式,您可以验证您是否已正确重命名变量。

于 2012-04-18T08:59:33.587 回答
4

想象一下括号:

let x = 1 in (let x = (x+2) in (let x = (x+3) in x))

然后替换 (x=1) 其中 x 它未被另一个 x 声明覆盖并消除 outermost let

let x = (1+2) in (let x = (x+3) in x)

评估:

let x = 3 in (let x = (x+3) in x)

代替:

let x = (3+3) in x

评估:

let x = 6 in x

代替:

6
于 2012-04-16T22:51:33.267 回答
4

(评论有点长,所以这里有一个小的额外答案。)

正如 Chuck 所指出的,这个表达式中没有闭包。唯一的复杂性是由于范围规则。OCaml 范围规则是常用的,即名称指的是最接近(最内层)的定义。在表达式中:

let v = e1 in e2

该变量ve1. 如果(偶然)该名称的变量出现在 中e1,则它必须引用 (a different) 的某些外部定义v。但是新v的(当然)可以命名为e2. 因此,您的表达式等效于以下内容:

let x = 1 in let y = x+2 in let z = y+3 in z

在我看来,这更清楚,但它具有完全相同的含义。

于 2012-04-16T23:25:07.433 回答