根据OCaml手册
.... 以某种未指定的顺序评估let pattern_1 = expr_1 and … and pattern_n = expr_n in expr
....`expr_1 … expr_n
OCaml 的任何编译器或 rutnime 是否利用未指定的顺序? 例如,
ocamlopt
和 ocamlc
+ocamlrun
在实践中对函数参数使用不同的求值顺序,我想知道 let ... and
是否会出现类似的情况。
我找不到任何评估顺序不是从左到右的情况,这里有一些代码,无论我使用
123
还是ocaml
,都会打印ocamlopt
:
let p = Printf.printf "%d%!"
let rec spin = function
| 0 -> ()
| n -> spin (n - 1)
let () =
let
x0 = spin 99999999;
and x1 = p 2
and x2 = p 3
in
let _ = x0, x1, x2 in
()
首先。可能有很多编译器,有些可能不是开源和/或发布的,因此不可能按照所述方式回答您的问题。
一般来说,定义语言语义时的常见做法是使用最弱形式的语句,即为编译器留下尽可能多的操作空间。由于最少的约束是输入,因此编译器将有更多选项来生成更优化的程序。
说到主流 OCaml 发行版,let p1 and p2 .. and pN in e
和
let p1 in let p2 in ... let pN in e
之间的编译代码没有区别1,前提是
p1,...,pN
与数据无关2。编译器将为两个表达式生成相同的 lambda 代码,这意味着甚至字节码也是相同的。
更具体地说,两个表达式都将被转换为 lambda 形式
(let (p1 p2 ... pN) e)
,即使模式之间存在数据依赖关系(编译器将重命名所有变量,确保所有变量都是新鲜的)。由于转换为 lambda 是编译的第一步(除了解析和输入,这严格来说不是编译过程的一部分),因此有关源代码中使用的 let 表达式的形式的信息会丢失,因此编译的后期阶段,尤其是指令调度,将无法使用此提示。
说实话,编译器并不真正需要开发者的这个提示。 OCaml 编译器足够聪明,可以执行数据和副作用分析,以根据需要重新排列术语。因此,
let p1 and ... pN in e
形式的主要用途是向代码的读者(即我们的程序员同事)传达,模式p1
和pN
是独立的,可以独立阅读。由于这极大地减轻了读者的认知负担(每个表达式现在具有更小的上下文),因此它使您的程序更具可读性。即使有副作用的表达,当你写作时
let x = f () and y = g () in k x y
您明确地告诉读者,
f
和g
的执行顺序没有区别,因此它们是独立的函数。
1)您可以通过将
-dlambda
选项添加到 ocamlopt
(或 ocamlc
)来检查这一点。
2)如果它们具有依赖关系,则意味着它们是不同的程序,因此编译器必须生成不同的代码。换句话说,不应该有任何优化。