您询问了表达式let x=1 in let x=x+2 in ... 中评估的顺序。顺序是“从左到右”!当您有一个let a=b in let c=d in ... 链时,评估顺序始终是从左到右。
但是,在您的示例中,有一个令人困惑的部分:您在每个 let 构造中都使用了 same 变量名称 x。这很令人困惑,因为您会看到类似let x=x+1 的内容,这看起来像是在“重新定义”x 或“更改x 的值”。但是在 OCAML 中实际上并没有发生“x”的“改变”!正如上面已经指出的那样,这里发生的事情是每次都会引入一个 新变量,因此您的示例完全等同于
let x = 1 in let y = x+2 in let z = y+3 in z;;
请注意,这里的评估顺序也是从左到右的。 (在let 构造的每个链中,它始终是从左到右的。)在您最初的问题中,您选择将所有这些新变量称为“x”而不是x、y 和z。这让大多数人感到困惑。最好避免这种编码风格。
但是我们如何检查我们是否正确地重命名了变量?为什么“让 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
通过这种方式,您可以验证您是否正确地重命名了变量。