解释模式匹配与开关
Posted
技术标签:
【中文标题】解释模式匹配与开关【英文标题】:Explaining pattern matching vs switch 【发布时间】:2010-09-17 00:39:50 【问题描述】:我一直试图向几个人解释 switch 语句和模式匹配 (F#) 之间的区别,但我真的无法很好地解释它..大多数时候他们只是看着我说“那你为什么不直接使用 if..then..else”。
你会如何向他们解释?
编辑!谢谢大家的好答案,我真的希望我能标记多个正确的答案。
【问题讨论】:
【参考方案1】:也许你可以用字符串和正则表达式来类比?您描述您要查找的什么,然后让编译器自己找出如何。它使您的代码更简单、更清晰。
顺便说一句:我发现模式匹配最有用的地方在于它鼓励了良好的习惯。我首先处理极端案例,很容易检查我是否涵盖了所有案例。
【讨论】:
【参考方案2】:我以前是“那些人”中的一员,但我不知道有没有一种简洁的方式来总结为什么模式匹配如此美味。这是体验。
当我刚刚看过模式匹配并认为它是一个美化的 switch 语句时,我认为我没有使用代数数据类型(元组和可区分联合)编程的经验,并且不太了解那种模式匹配既是一个控制结构和一个绑定结构。现在我一直在使用 F# 进行编程,我终于“明白了”。模式匹配的酷炫之处在于函数式编程语言中的各种特性的融合,因此对于外人而言,欣赏它并非易事。
我试图在关于语言和 API 设计的由两部分组成的简短博客系列的第二部分中总结模式匹配为何有用的一个方面;查看part one 和part two。
【讨论】:
“体验”。好话。 :-)【参考方案3】:在我的头顶:
-
编译器可以判断您是否没有涵盖匹配中的所有可能性
您可以将匹配项用作作业
如果您有一个有区别的联合,则每个匹配项都可以有不同的“类型”
【讨论】:
【参考方案4】:模式为您提供了一种小语言来描述您想要匹配的值的结构。结构可以任意深,您可以将变量绑定到结构化值的某些部分。
这使您可以非常简洁地编写内容。你可以用一个小例子来说明这一点,比如简单类型数学表达式的导函数:
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) 提供非常有效的实现(使用跳跃等)。
【讨论】:
很好的例子。在向“无功能的人”解释模式匹配时,我想提一下,使用 PM,您的条件可以检查数据的“形状”,而使用 if/switch 则更加困难、丑陋和低效。Add of expr * expr
我想你是想写+
No lukas,这是抽象语法,不是具体语法,所以+
无效,*
不应被解释为算术乘法。 Add of expr * expr
声明了一个非常量构造函数 Add
,其 2 个参数的类型均为 expr
。因此,您可以使用它来构造 2 个标记为 Add
的表达式的组合。【参考方案5】:
开关是两个前轮。
模式匹配是整辆车。
【讨论】:
【参考方案6】:摘自this blog article:
模式匹配比 switch 语句和方法调度有几个优点:
模式匹配可以作用于整数, 浮点数、字符串和其他类型为 以及对象。 模式匹配可以作用于多个 同时不同的值: 并行模式匹配。方法 dispatch 和 switch 仅限于单个 值,例如“这个”。 模式可以嵌套,允许 在任意树上调度 深度。方法调度和切换是有限的 到非嵌套的情况。 或模式允许子模式 共享。方法分派只允许 当方法来自时共享 碰巧共享一个基础的类 班级。否则你必须手动 将共性分解为 单独的功能(给它一个 名称),然后手动插入调用 从所有适当的地方到您的 不必要的功能。 模式匹配提供冗余 检查哪个捕获了错误。 嵌套和/或并行模式 匹配为您优化 F# 编译器。 OO 等效项必须 不断手写 期间手动重新优化 发展,这是令人望而却步的 繁琐且容易出错,所以 生产质量的 OO 代码倾向于 相比之下非常慢。 活动模式允许您注入 自定义调度语义。【讨论】:
【参考方案7】:OCaml 中的模式匹配,除了在上面描述的几种方式中提到的更具表现力之外,还提供了一些非常重要的静态保证。编译器将为您证明您的模式匹配语句所体现的案例分析是:
详尽无遗(没有遗漏案例) 非冗余(没有因为被前一个案例抢占而永远无法命中的案例) 声音(考虑到相关数据类型,没有不可能的模式)这真是一件大事。当您第一次编写程序时它很有帮助,并且在您的程序不断发展时非常有用。如果使用得当,匹配语句可以更轻松地可靠地更改代码中的类型,因为类型系统会将您指向损坏的匹配语句,这是您需要修复的代码的良好指标。
【讨论】:
【参考方案8】:元组有“,”,变体有 Ctor args .. 这些是构造函数,它们创造事物。
模式是析构函数,它们将它们撕裂。
它们是双重概念。
更强有力地说:元组或变体的概念不能仅仅由它的构造函数来描述:析构函数是必需的,或者你创建的值是无用的。正是这些双重描述定义了一个价值。
通常我们将构造函数视为数据,将析构函数视为控制流。变体析构函数是备用分支(许多之一),元组析构函数是并行线程(所有许多)。
并行性在诸如
之类的操作中很明显(f * g) . (h * k) = (f . h * g . k)
如果您认为控制流经函数,元组提供了一种将计算拆分为并行控制线程的方法。
这样看来,表达式是组合元组和变体以构成复杂数据结构的方法(想想 AST)。
模式匹配是组合析构函数的方法(再次想想 AST)。
【讨论】:
【参考方案9】:If-Else(或 switch)语句是关于根据手头值的属性选择不同的方式来处理值(输入)。
模式匹配是关于定义如何处理一个给定结构的值,(还要注意单例模式匹配是有意义的)。
因此,模式匹配更多的是解构值而不是做出选择,这使得它们成为在归纳结构(递归联合类型)上定义(递归)函数的一种非常方便的机制,这解释了为什么它们在 Ocaml 等语言中如此广泛地使用.
PS:您可能知道模式匹配和 If-Else “模式”从它们在数学中的特别使用中;
"如果 x 具有属性 A,则 y 否则 z" (If-Else)
“p1..pn 中的某个术语,其中 .... 是 x.. 的素数分解”((单例)模式匹配)
【讨论】:
以上是关于解释模式匹配与开关的主要内容,如果未能解决你的问题,请参考以下文章