Functional programming 为什么在OCaml中使用模式匹配 让我们考虑这个小函数 let f x = match x with 0 -> 1 | _ -> x ;;
这在逻辑上等同于Functional programming 为什么在OCaml中使用模式匹配 让我们考虑这个小函数 let f x = match x with 0 -> 1 | _ -> x ;;,functional-programming,pattern-matching,ocaml,Functional Programming,Pattern Matching,Ocaml,这在逻辑上等同于 let f x = if x = 0 then 1 else x ;; 如果我们可以使用if/else实现相同的功能,那么模式匹配的目的是什么 检测到部分模式匹配: type number = Zero | One | Two; let f= function Zero -> 0 | One -> 1 ;; Warning 8: this pattern-matching is not exhaustive. Here is an exa
let f x =
if x = 0 then 1 else x ;;
如果我们可以使用if/else实现相同的功能,那么模式匹配的目的是什么 检测到部分模式匹配:
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>
类型编号=零|一|二;
设f=函数
零->0
|一->一;;
警告8:此模式匹配并非详尽无遗。
以下是一个不匹配值的示例:
两个
val f:number->int=
在您的精确示例中,模式匹配不会带来太多,因为您只有两个案例,更重要的是,您的模式没有任何变量。只要用if/then/else写下这个例子,你就会明白:
let rec map f = function
[] -> []
| a::l -> let r = f a in r :: map f l
另外请注意,如果您有多余的案例或忘记了某些案例,模式匹配会向您发出警告。通常模式匹配允许编译器应用更积极的优化技术。在if/then/else表达式中,条件是一个包含副作用的任意表达式。例如,相等运算符可以执行任何操作,因此编译器通常不能依赖
x=0
表示x等于零。在模式中,匹配子句总是常量,匹配意味着语法相等,不能重载,因此可以轻松地直接编译为程序集比较操作。在使用if
的示例中,比较通常会编译为函数调用(但在本例中,afaik编译器足够聪明,生成的代码将是相同的)
但是if/then/else和模式匹配之间的主要区别在于后者是并行运行的,并编译成嵌入到程序集中的二进制搜索树,而if/then/else只是一个线性比较序列(有关更多信息,请参阅)
更新
为了满足好奇心,我添加了一些汇编输出。不需要理解x86汇编程序,只需比较一些指令,就可以得到一个基本的想法。你会看到的
正如我所预测的,事实上,编译器发出了几乎相同的代码,对您来说具有相同的性能,例如:
带有_match的函数已编译成有效的代码(注意,OCaml术语中的0
是1
)
对于带有_if的函数,编译器也会发出最佳代码。唯一的区别是,在使用_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
尽管如此,我想强调的是,一个人不应该更喜欢匹配而不是if或if。应该选择一个更干净的结构,并产生更可读的代码。ocaml编译器相当不错,可以为您提供高效的代码
我个人更倾向于比赛,因为这反映了我的思维方式。我更难根据if/then/else结构进行推理,每当我阅读它们时,我都会在心里把它们翻译成与从句匹配的句子。但这是我个人的问题。请随意使用任何更适合您的结构 但是请注意,防护装置可能会引起副作用。@antron:防护装置是什么?它们和C++一样吗?(#ifdef MYHEADER_H…#endif
)我想用实验证明一下编译此文件(以及另一个变体的类似文件)并运行time./exec
会是可靠的基准吗?不会。更好的方法是查看汇编并比较一些指令。但我敢打赌,对于您的特定示例,编译器会聪明地生成相同的代码,因为它使用了一些启发式方法。@marmistrz:是这样的:。防护装置不是模式的一部分,但是,正如您所看到的,您可以使用它们在match
案例中设置任意条件,包括其评估会导致副作用的条件。
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