假设我们有一个类型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
一般来说,使用多个"当"case或匹配一个模式,然后加上&;if-then-else&;在吗?
我在哪里可以获得更多关于这些事情的信息,比如OCaml和语法糖等的良好实践?
这两种方法都有各自的优缺点,因此应该根据上下文使用它们。
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
可以达到同样的可读性。
总而言之,当不可能或几乎不可能用if/then/else
表示相同的代码时,最好使用when
。为了提高可读性,最好将逻辑分解为具有可读名称的辅助函数。例如,对于dcmp
,最好的解决方案是既不使用if
也不使用when
,例如
let dcmp (D (x,y)) = compare x y
1)在这种特殊情况下,编译器将为when
和if/then/else
生成相同的代码。但是在更一般的情况下,守卫可能会阻止匹配的编译器生成有效的代码,特别是在分支不连接的情况下。在我们的例子中,编译器只是注意到我们在重复相同的分支,并将它们合并成一个分支,并将其转换回if/then/else表达式,例如,下面是带有when
保护的函数的cmm输出,
(if (> x y) 3 (if (< x y) -1 1))
与dcmp
函数的if/then/else版本生成的代码完全相同。
2)当然不是到它不会注意到缺失分支的状态,而是到它会不精确地报告缺失分支或要求您添加不必要分支的状态。
引用OCaml走向清晰和优雅的风格指南:
代码读的比写的多——让读者的生活更轻松
和
代码越少越好,代码越晦涩越糟
第一个让我认为具有多个when
子句的版本是更好的选择,因为它可以在根据条件读取代码时轻松预测或评估结果。第二个更深入,与if-then-else
相反,因为即使较短,当从远处看时也是模糊的。
同样,在函数一节中,我们发现& '模式匹配是定义函数的首选方式& ';
从一个Haskell函数式程序员的角度来看