解释模式匹配和开关

I have been trying to explain the difference between switch statements and pattern matching(F#) to a couple of people but I haven't really been able to explain it well..most of the time they just look at me and say "so why don't you just use if..then..else".

你要怎么跟他们解释?

编辑! 感谢每个人的伟大的答案,我真的希望我可以标记多个正确的答案。

17098 次浏览

也许你可以用字符串和正则表达式来做一个类比?您描述您正在寻找的 什么,并让编译器自己计算出 怎么做。它使您的代码更加简单和清晰。

顺便说一句,我发现模式匹配最有用的地方是它鼓励好习惯。我处理的角落案件 第一,很容易检查,我涵盖了每个案件。

我以前是“那些人”中的一员,我不知道有没有一种简洁的方式来总结为什么模式匹配是如此美味。这是经验之谈。

回想当初,我只看了一眼模式匹配,以为它是一个美化了的 switch 语句,我认为自己没有使用代数数据类型(元组和有区别的联合)编程的经验,也没有看到模式匹配既是一个控制结构 还有,又是一个绑定结构。现在我已经用 F # 编程,我终于“明白了”。模式匹配的酷是由于函数式编程语言中的一些特性的融合,因此对于外部观察者来说,欣赏这些特性是非常重要的。

在关于语言和 API 设计的两部分博客系列的第二部分中,我试图总结模式匹配为什么有用的一个方面; 请参阅 第一部分第二部分

我第一时间想到的是:

  1. 编译器可以判断您是否没有覆盖匹配中的所有可能性
  2. 您可以使用匹配作为赋值
  3. 如果您有一个受歧视的联合,那么每个匹配项可以有不同的“类型”

模式提供了一种小型语言来描述要匹配的值的结构。结构可以是任意深度的,您可以将变量绑定到结构化值的某些部分。

这使得你可以非常简洁地写东西。你可以用一个小例子来说明这一点,例如一个简单类型的数学表达式的导数函数:

type expr =
| Int of int
| Var of string
| Add of expr * expr
| Mul of expr * expr;;


let rec d(f, x) =
match f with
| Var y when x=y -> Int 1
| Int _ | Var _ -> Int 0
| Add(f, g) -> Add(d(f, x), d(g, x))
| Mul(f, g) -> Add(Mul(f, d(g, x)), Mul(g, d(f, x)));;

此外,由于模式匹配是静态类型的静态构造,编译器可以(i)验证是否覆盖了所有情况(ii)检测到永远无法匹配任何值的冗余分支(iii)提供非常有效的实现(包括跳转等)。

开关是两个前轮。

模式匹配就是整辆车。

节选自 这篇博客文章:

与 switch 语句和方法分派相比,模式匹配有几个优势:

  • 模式匹配可以作用于 int, 浮动、字符串和其他类型 以及物品。
  • 模式匹配可以作用于多个 同时拥有不同的价值观: 平行模式匹配方法 调度和切换仅限于单个 价值,例如「这个」。
  • 模式可以嵌套,允许 对任意树木进行调度 方法调度和切换受到限制 非嵌套的情况下。
  • 或者-模式允许子模式 方法分派只允许 当方法来自 碰巧共享一个基类的类 否则,您必须手动 把共性分解成 单独的函数(给它一个 名称) ,然后手动插入调用 从所有适当的地方到你的 不必要的功能。
  • 模式匹配提供了冗余 检查捕捉错误。
  • 嵌套和/或并行模式 属性为您优化匹配 F # 编译器。面向对象等价物必须 经常用手写 手工重新优化 发展,这是令人望而却步的 单调乏味,容易出错 产品质量的 OO 代码倾向于 相比之下非常慢。
  • 主动模式可以让你注射 自定义分派语义。

OCaml 中的模式匹配,除了在上面描述的几种方式中提到的更具表现力之外,还提供了一些非常重要的静态保证。编译器将为您证明您的模式匹配语句所包含的案例分析是:

  • 详尽无遗(不遗漏任何个案)
  • 非冗余(没有因为被前一个用例抢占而永远不能命中的用例)
  • 声音(没有给定有问题的数据类型的不可能的模式)

这可是件大事。当您第一次编写程序时,它非常有用,当您的程序正在进化时,它非常有用。如果使用得当,match 语句使可靠地更改代码中的类型变得更加容易,因为类型系统会将您指向损坏的 match 语句,这是一个很好的指示器,指示您需要修复代码的位置。

元组有“ ,”和变体有 Ctor 参数. . 这些是构造函数,它们创建东西。

模式是破坏者,它们将其撕裂。

它们是双重概念。

为了更有力地说明这一点: 元组或变量的概念不能仅仅由其构造函数来描述: 析构函数是必需的,或者您创建的值是无用的。正是这些双重描述定义了一个价值。

通常我们将构造函数视为数据,将析构函数视为控制流。变量析构函数是交替分支(许多分支中的一个) ,元组析构函数是并行线程(所有分支都是许多线程)。

这种并行性在以下操作中很明显

(f * g) . (h * k) = (f . h * g . k)

如果考虑通过函数流动的控制,元组提供了一种将计算拆分为并行控制线程的方法。

从这个角度来看,表达式是组合元组和变量以形成复杂数据结构的方法(想想 AST)。

模式匹配是组成析构函数的方法(再想想 AST)。

If-Else (或 switch)语句是关于根据手边值的 物业选择不同的方法来处理值(输入)。

模式匹配是定义如何处理一个给定的 结构值(还要注意,单一的大小写模式匹配是有意义的)。

因此,模式匹配更多的是解构值而不是做出选择,这使得它们成为定义归纳结构(递归联合类型)上的(递归)函数的一种非常方便的机制,这就解释了为什么它们在诸如 Ocaml 等语言中被如此广泛地使用。

PS: 您可能从模式匹配和 If-Else“模式”在数学中的特别使用中知道它们;

“ if x has property A then y else z”(If-Else)

“ p1. . pn 中的某个术语,其中... . 是 x. . 的素数分解”((单例)模式匹配)