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