通常模式匹配允许编译器应用更积极的优化技术。在 if/then/else 表达式中,条件是任意表达式,可能包含副作用。例如,相等运算符可以做任何事情,因此编译器通常不能相信x=0 意味着x 等于0。模式匹配中的子句始终是常量,匹配意味着语法相等,不能重载,所以可以很方便的直接编译成汇编比较操作。在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
话虽如此,我想强调的是,人们不应该更喜欢 match 而不是 if 或反之亦然。应该选择更干净且代码更易读的结构。而且 ocaml 编译器相当不错,并且会为您生成高效的代码。
我个人更倾向于比赛,因为这反映了我的思维方式。我很难根据 if/then/else 结构进行推理,每当我阅读它们时,我都会在脑海中将它们翻译成与子句匹配的内容。但这是我个人的问题。随意使用更适合您的任何构造。