让我们考虑一下这个小功能
let f x =
match x with
0 -> 1 |
_ -> x ;;
这在逻辑上等同于
let f x =
if x = 0 then 1 else x ;;
如果我们可以使用if / else实现相同的模式匹配的目的是什么?
答案 0 :(得分:3)
在您的精确示例中,模式匹配并没有带来太多,因为您只有2个案例,更重要的是因为您的模式没有任何变量。只需用if / then / else编写这个例子,你就会明白:
let rec map f = function
[] -> []
| a::l -> let r = f a in r :: map f l
另请注意,如果您有多余的案例或忘记了某些案例,模式匹配会向您发出警告。
答案 1 :(得分:2)
检测到部分模式匹配:
type number = Zero | One | Two;
let f= function
Zero -> 0
| One -> 1 ;;
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
Two
val f : number -> int = <fun>
答案 2 :(得分:2)
通常,模式匹配允许编译器应用更积极的优化技术。在if / then / else表达式中,条件是任意表达式,可以包含副作用。例如,相等运算符可以执行任何操作,因此编译器通常不能依赖x=0
表示x等于零。在模式匹配中,子句总是常量,匹配意味着语法相等,不能重载,因此可以很容易地直接编译成程序集比较操作。在带有if
的示例中,比较将通常编译为函数调用(但在这种情况下afaik编译器足够聪明,并且生成的代码将是相同的)。
但if / then / else和模式匹配之间的主要区别在于后者是并行运行并编译成嵌入到程序集中的二叉搜索树,当if / then / else只是一个线性比较序列时(参见this了解更多信息)。
为了满足OP的好奇心,我添加了一些装配输出。不需要理解x86汇编程序,只需比较一些指令就可以得到一个基本的想法。你会看到。
正如我预测的那样,编译器发出了几乎相同的代码,具有与您相同的性能示例:
函数with_match
已编译成有效的代码(注意OCaml用语中的0
是1
)
with_match:
.L101:
cmpq $1, %rax
je .L100
ret
.L100:
movq $3, %rax
ret
对于函数with_if
,编译器也发出了最佳代码。唯一的区别是在with_if
函数中,跳转指令中的条件被反转。
with_if:
.L103:
cmpq $1, %rax
jne .L102
movq $3, %rax
ret
.L102:
ret
这是可能的,因为编译器使用了一个技巧,允许他对待
=
作为一种特殊功能,附有一些理论。但总的来说这是不可能的,因为=
可以是任意函数。我们可以通过在文件的开头添加以下行来轻松混淆编译器:
let (=) x y = x = y
现在所有技巧都被禁用,编译器会发出这个效率低下的代码。
with_if:
subq $8, %rsp
.L105:
movq %rax, 0(%rsp)
movq $1, %rsi
movq %rax, %rdi
movq _caml_equal, %rax
call _caml_c_call
.L106:
movq _caml_young_ptr, %r11
movq (%r11), %r15
cmpq $1, %rax
je .L104
movq $3, %rax
addq $8, %rsp
ret
.L104:
movq 0(%rsp), %rax
addq $8, %rsp
ret
尽管如此,我想强调一点,不管是反对还是反对,都不应该匹配。应该选择更干净且结果更易读的构造。并且ocaml编译器相当不错,并且会为您发出高效的代码。
我个人更倾向于比赛,因为这反映了我的思维方式。我很难用if / then / else结构来推理,每当我读到它们时,我都会在心理上将它们翻译成与子句匹配。但这是我个人的问题。随意使用适合您的任何构造。