OCaml 模式匹配中哪个更好,`when` 或 `if-then-else`?

Which is better in OCaml pattern matching, `when` or `if-then-else`?

假设我们有一个名为 d:

的类型
type d = D of int * int

我们想对它做一些模式匹配,这样做更好吗:

let dcmp = function 
  | D (x, y) when x > y -> 1 
  | D (x, y) when x < y -> -1
  | _ -> 0

let dcmp = function 
  | D (x, y) -> 
    if x > y then 1 else if x < y then -1 else 0

一般来说,匹配具有许多“when”情况的模式或匹配一个模式并在其中放入“if-then-else”更好吗?

我在哪里可以获得有关此类问题的更多信息,例如 OCaml 中的良好做法和语法糖等?

引用 OCaml Towards Clarity and Grace 风格指南:

Code is more often read than written - make the life of the reader easy

Less code is better, cryptic code is worse

第一个让我觉得有多个 when 子句的版本是更好的选择,因为它可以很容易地根据条件阅读代码时预测或评估结果。第二个更进一步,反对 if-then-else 因为,即使更短,从 afar.

看时也是神秘的

此外,在 Functions 部分,我们发现 “模式匹配是定义函数的首选方式”

从 Haskell 函数式程序员的角度来看。

这两种方法各有利弊,因此应根据上下文使用它们。

when子句比if更容易理解,因为它只有一个分支,所以你一次可以消化一个分支。它的代价是,当我们分析一个子句以了解其路径条件时,我们必须分析它之前的所有分支(并否定它们),例如,将您的变体与以下定义进行比较,这是等价的,

  let dcmp = function 
  | D (x, y) when x > y -> 1 
  | D (x, y) when x = y -> 0
  | _ -> -1

当然,if/then/else 构造也是如此,只是更难意外地重新排列 if/then/else 表达式中的分支(例如,在重构期间)并完全改变表达式的逻辑。

此外,when守卫可能会阻止编译器执行决策树优化1并混淆2反驳机制。

鉴于此,在此特定示例中使用 when 而不是 if 的唯一优势是 when 语法看起来更吸引人,因为它完美排列并且更容易人脑去寻找条件和它们对应的值在哪里,也就是说,它看起来更像一个真理-table。但是,如果我们写

let dcmp (D (x,y)) = 
  if x = y then 0 else
  if x > y then 1 else -1

我们可以达到相同水平的可读性。

总而言之,当不可能或几乎不可能用if/then/else表达相同的代码时,最好使用when。为了提高可读性,最好将您的逻辑分解为具有可读名称的辅助函数。例如,对于 dcmp,最好的解决方案是既不使用 if 也不使用 when,例如,

let dcmp (D (x,y)) = compare x y

1)在这种特殊情况下,编译器将为 whenif/then/else 生成相同的代码。但在更一般的情况下,守卫可能会阻止匹配的编译器生成高效代码,尤其是当分支不相交时。在我们的例子中,编译器只是注意到我们在重复同一个分支,并将它们合并为一个分支,然后将其转回 if/then/else 表达式,例如,这里是带有 [= 的函数的 cmm 输出14=] 守卫,

(if (> x y) 3 (if (< x y) -1 1))

这与 if/then/else 版本的 dcmp 函数生成的代码完全相同。

2) 当然,不是到它不会注意到丢失的分支的状态,而是到它报告丢失的分支不太准确或者会要求你的状态添加不必要的分支。