是否有 OCaml 编译器利用 let ... 和绑定的未指定评估顺序?

问题描述 投票:0回答:1

根据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
1个回答
1
投票

首先。可能有很多编译器,有些可能不是开源和/或发布的,因此不可能按照所述方式回答您的问题。

一般来说,定义语言语义时的常见做法是使用最弱形式的语句,即为编译器留下尽可能多的操作空间。由于最少的约束是输入,因此编译器将有更多选项来生成更优化的程序。

说到主流 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)如果它们具有依赖关系,则意味着它们是不同的程序,因此编译器必须生成不同的代码。换句话说,不应该有任何优化。

© www.soinside.com 2019 - 2024. All rights reserved.