OCaml 模式匹配中,`when` 和 `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 风格指南:

代码的阅读次数比编写次数多 - 让读者的生活更轻松

代码越少越好,隐晦的代码越糟糕

第一个让我觉得带有多个 when 子句的版本是更好的选择,因为它可以根据条件在阅读代码时轻松预测或评估结果。第二个更进一步,反对 if-then-else 因为,即使更短,从远处看时也是神秘的。

此外,在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 语法看起来更吸引人,因为它完美排列并且对人类来说更容易大脑去寻找条件和它们对应的值在哪里,也就是说,它看起来更像是一个真值表。但是,如果我们会写

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

我们可以达到相同级别的可读性。

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

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

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

when

这与 (if (> x y) 3 (if (< x y) -1 1)) 函数的 if/then/else 版本生成的代码完全相同。

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