速来报名!AICon北京站鸿蒙专场~ 了解详情
写点什么

Scala 模式匹配的亮点——Martin Odersky 访谈(四)

  • 2016-01-20
  • 本文字数:3601 字

    阅读完需:约 12 分钟

Martin Odersky 向 Bill Venners 和 Frank Sommers 谈论 Scala 模式匹配的机制和目的。

Scala 是一种新兴的通用用途、类型安全的 Java 平台语言,结合了面向对象和函数式编程。它是洛桑联邦理工大学教授 Martin Odersky 的心血结晶。本访谈系列由多部分组成,由 Artima 网站的 Frank Sommers 和 Bill Venners 向 Martin Odersky 讨教 Scala。在第一部分 Scala 起源中(点击查看《Scala 起源》中文翻译),Odersky 讲述了导致Scala 诞生的那些历史。在第二部分 Scala 的设计目标中,它讨论了 Scala 设计中的妥协、目标、创新和优势。在第三部分 Scala 类型系统的目的中,他挖掘了 Scala 的类型系统的设计动机。本期是第四部分,也是最后一部分,Odersky 讨论了模式匹配。

模式匹配是什么?

Bill Venners: Scala 支持 _ 模式匹配 _。这是一种函数式编程技术,过去尚未在主流语言中出现过。你能解释一下它是什么,以及我们为什么需要它?

Martin Odersky: 模式匹配并不很新,(上世纪)七十年代中期就已经有语言采用。据我所知,第一种语言是 ML,但可能也有更早的语言支持。它在许多函数式语言中都算是标准功能,包括 ML、Caml、Erlang、以及 Haskell。

那么什么是模式匹配呢?它可以让你给一个值匹配多种情况,有点像 Java 中的 switch 语句。但它不仅可以像 switch 语句一样用来匹配数字,还可以匹配对象的内在构建形式。

比如,Scala 中的 List 存在两种情况:要么是空 List,写做 Nil;要么由一个 _head_ 元素紧接着另一 List _tail_ 组成。有了模式匹配,你可以询问:给定的 List 是空 List 吗?只要编写 case Nil、箭头 (=>) 以及后续表达式即可:

case Nil => // 后续表达式你还可以询问:它是非空 List 吗?只要编写 case x :: xs、箭头、以及后续表达式即可:

case x :: xs => // 后续表达式双冒号(::)表示 _cons_ 操作符;x 表示 List 的首元素,xs 表示剩余部分。于是,模式匹配会首先区分 List 是否为空。而如果 List_ 非 _ 空,它会把 List 的首元素命名为 x 然后把 List 剩余部分命名为 xs。接下来,这些变量可以被箭头右侧表达式所用。(参见示例 1)

示例 1:match 表达式

复制代码
list match {
case Nil => "was an empty list"
case x :: xs => "head was " + x + ", tail was " + xs
}

如果 list 不为空,将匹配到第二种情况,List 首元素将赋值给 x,而列表剩余部分赋值给 xs。接下来,这些变量将被箭头符号右侧的字符串连接表达式所用。例如,如果 list 内容是 List(“hello”, “world”),那么匹配表达式的结果将是字符串"head was hello, tail was List(world)"。

上例的模式非常简单。但实际上模式还支持嵌套,类似表达式的嵌套,能让你编写层数很深的模式。总的来说,亮点在于,模式和表达式看起来很像。模式本质上和表达式属于完全一类东西,看上去就像构造表达式一样,可以用来构造复杂树状对象,但却不需要编写 new。事实上,在 Scala 中,该对象构造时一样不需要 new。然后你可以在某些位置填上占位变量,对应树对象中实际存在的值。(参见示例 2)

示例 2:嵌套模式的 match 表达式

复制代码
object match {
case Address(Name(first, last), street, city, state, zip) => println(last + ", " + zip)
case _ => println("not an address") // 默认情况
}

在第一种情况下,模式 Name(first, last) 嵌在模式 Address(…) 中。last 放在了 Name 构造函数内,可以“提取”出值,因而,可供箭头右边的表达式使用。

模式匹配的目的

那么,为什么你需要模式匹配?我们每个人都有复杂的数据。如果我们坚持严格的面向对象的风格,那么我们并不希望直接访问数据内部的树状结构。相反,我们希望调用方法,然后在方法中访问。如果我们能够这样做,那么我们就再也不需要模式匹配了,因为这些方法已经提供了我们需要的功能。但很多情况下,对象并不提供我们需要的方法,而且我们无法(或者不愿)向这些对象添加方法。

例如 XML。如果给你一棵 XML 树,那么树就只是单纯的数据。要么是节点,要么是节点的序列。XML 是一种非常通用的数据表现形式。例如,DOM 本质上只是节点的数组,其中每个节点的类型都未知。现在我们设想一下,如果把 XML 树转换到某种更强的框架中,可以给你一个列表,容纳各种不同类型的对象。组成列表的元素可能包括诸如电话号码、备忘录或地址等。如果你想以静态类型的方式获取所有这些东西,就会遇上一个问题:你不知道每个元素的类型。在传统面向对象的编程语言中,唯一可行方式是,编写一大堆 instanceof 检测,一一测试每个元素是 PhoneNumber 实例、Memo 实例,还是其他实例。一旦这些 instanceof 语句之一检测成功,你还需要进行类型转换。上述做法相当丑陋和笨拙,有了模式匹配就能避免了。模式匹配能以更安全、更自然的方式完成相同功能。

从本质上讲,当你从外部取得具有结构的对象图时,模式匹配就必不可少。你会在若干情况下遇到这种现象,XML 是其中之一。各种从文本解析而来的数据,都属于这一类。例如,有一种典型情况下模式匹配必不可少,即,处理编译器中的抽象语法树的情况。如果你要对表达式进行化简操作,表达式会被表示为树,你需要通过模式匹配对这些树进行提取操作。类似那样的情况还有许多。遇到这些情况时,模式匹配真的必不可少。

反向构造对象

Bill Venners: 你说模式像表达式,而且就像某种反向的表达式。正向的表达式向结果中插入值,反向的表达式却是给定结果,一旦匹配成功,就能反过来从结果中抽取出一大堆值。

Martin Odersky: 对。它还真就是反向的构造器。我可以通过嵌套构造器来构造对象,在构造时提供一些参数。比方说,我有一个方法,给它一些参数,就能从这些参数构造出复杂的对象结构。模式匹配正好相反——给定一个复杂数据结构,模式匹配就能抽取出先前用来构造该结构时所用的参数。

可扩展性的两个方向

Bill Venners: 听起来你好像在谈及一种面向对象的解决方案,解决的问题是:如何在现有对象中新增涉及内部数据的行为。理想情况下,你会把方法添加到子类型中,比如 Memo、Address 以及任何其他节点类型。你会在它们公共超类上调用这些方法,这些方法会通过动态绑定找到某个具体类。好比说,“我是 Memo,我干 Memo 该干的事。”但你说的问题是,往往你没办法轻易添加方法。

Martin Odersky: 是的,就是这样。关键问题是,你在什么时候添加方法?这个问题多半是在质问可扩展性。举个典型的面向对象例子:图形用户界面。你有很多不同的组件,都能做相同的事情。它们可以显示、可以隐藏、可以重绘……诸如此类。你与这些组件交互的协议是固定的,但你要打交道的组件数量却是无限的。用户时时刻刻都在发明新的图形用户界面组件。在这种情况下,面向对象的方法是正解。而且是唯一正解。事实上,有史以来第一种面向对象语言,理当是用于仿真领域的。该语言是 Simula-67 ,其主要用途就很类似图形用户界面领域。而第二种面向对象语言, Smalltalk ,则是在史上第一套实用图形用户界面开发的同时发明的。所以,这种语言真正回答了以下问题:怎样以可扩展的方式编写图形用户界面?

但这仅仅是可扩展性的观念之一。如果涉及一组相对固定的结构,则需要另一种观念。虽然你不想改变结构,但是你想要对这一组结构做的操作,却有无尽的可能。你会一直想要添加新的操作。典型的例子是编译器。编译器处理语法树,而语法树表示你写的程序。只要你不修改语言规范,语法树结构就会保持不变。这颗树一直都一个样。然而,编译器想要对语法树做的事情则天天都会变。明天你可能就会设想要新增一个优化阶段,需要遍历语法树。所以,你需要某种方式,把操作定义在树以外,因为,不然的话,每当你要向编译器添加新的优化阶段等行为时,你就必须为所有树节点类添加新方法。这显然非常昂贵、非常麻烦。

所以,完成工作的正确工具是什么?确实取决于你希望扩展的方向。如果你想要扩展新的数据,那么就该选择经典的面向对象途径,以及虚方法。如果你想保持数据类型固定,而只扩展新的操作,那么模式匹配更合适些。实际上面向对象编程中有一种设计模式(注意“设计模式”与“模式匹配”中的“模式”二字含义不同)叫做访问者模式。我们利用模式匹配能做的事,也可以用访问者模式来表达。但访问者模式利用了面向对象的虚方法分发机制。然而实践中,访问者模式非常笨重。很多用模式匹配很容易做到的事情,用访问者模式做不到。最终会导致访问者实现代码非常厚重。而且,最后我们发现,基于现代的虚拟机技术,访问者模式要比模式匹配性能更低。鉴于这两个原因,我认为模式匹配的确有用武之地。

阅读英文原文 The Point of Pattern Matching in Scala


感谢魏星对本文的策划和审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群(已满),InfoQ 读者交流群(#2))。

2016-01-20 16:253429

评论

发布
暂无评论
发现更多内容

博弈论(depu)与孙子兵法-02(46/100)

hackstoic

博弈论

MySQL系列——约束、存储引擎、事务

胖虎不秃头

MySQL 数据库· 9月月更

字节跳动A/B实验背后的秘密:样本量计算

字节跳动数据平台

数据分析 前端 ab测试 统计原理

iOS端如何实现带UI截屏分享

MobTech袤博科技

ios

【荣耀开发者服务平台—百亿曝光扶持等你来】智慧服务快应用卡片接入指南(上)

荣耀开发者服务平台

JavaScript 前端 UI 安卓 honor

数据可视化系列教程之组件通信

云智慧AIOps社区

前端 JavaScrip 可视化数据

软件测试 | 测试开发 | 智能遍历测试在回归测试与健壮性测试的应用

测吧(北京)科技有限公司

软件测试 测试

“数智化”时代 ,房企转型路径与挑战的一种技术思路

Speedoooo

小程序 前端开发 数字化转型 移动开发 小程序容器

Qt | 关于对象树和元对象的相关问题

YOLO.

c++ qt 9月月更

离谱了!京东T7手写「并发编程知识手册」,从原理到项目实战详解

了不起的程序猿

Java 并发编程 java程序员 java面试 java编程

基于Hudi的湖仓一体技术在Shopee的实践

Shopee技术团队

Hudi LakeHouse 湖仓一体

Qt | 关于容器类的一些总结

YOLO.

c++ qt 9月月更

直播预告 | PolarDB-X 动手实践系列——PolarDB-X 数据导入导出功能

阿里云数据库开源

MySQL 数据库 阿里云 云原生 PolarDB-X

Python 教程之数据分析(5)—— 使用 Python 进行数据分析和可视化 | 第 2 套

海拥(haiyong.site)

Python 9月月更

高并发下的网络 IO 模型设计

C++后台开发

后台开发 reactor 高并发 epoll 网络io模型

字节跳动 DanceCC 工具链系列之Xcode LLDB耗时监控统计方案

字节跳动终端技术

ios xcode swift LLVM 客户端

软件测试 | 测试开发 | 一文搞定 uiautomator2 自动化测试工具使用

测吧(北京)科技有限公司

测试 自动化测试

Qt | Qt中的一些使用在容器类上的算法

YOLO.

c++ qt 9月月更

博云 Kubernetes 开源榜单贡献度进入全球前十

BoCloud博云

云计算 开源 云原生

一加现在属于OPPO吗 资深“加油”来解答

Geek_8a195c

Python 教程之数据分析(6)—— 数据分析的数学运算

海拥(haiyong.site)

Python 9月月更

软件测试 | 测试开发 | 测试工程师用 Shell 定位 Bug 的正确姿势

测吧(北京)科技有限公司

测试 bug

关于QPalette的使用

YOLO.

c++ qt 9月月更

LED显示屏是否可以实现智能化控制

Dylan

LED显示屏 户外LED显示屏 led显示屏厂家

软件测试 | 测试开发 | AppCrawler 自动遍历测试实践(三):动手实操与常见问题汇总

测吧(北京)科技有限公司

软件测试 测试 软件测试和开发

软件测试 | 测试开发 | 测试人员必须掌握的测试用例

测吧(北京)科技有限公司

测试 测试用例

软件测试 | 测试开发 | 这些常用测试平台,你们公司在用的是哪些呢?

测吧(北京)科技有限公司

测试 bug

MySQL系列——表的创建、插入、修改、删除数据

胖虎不秃头

MySQL 数据库 9月月更

MySQL系列——索引、视图、DBA常用命令、数据库设计三范式

胖虎不秃头

MySQL 数据库· 9月月更

NFT艺术品交易平台:有哪些功能?

开源直播系统源码

NFT 数字藏品 数字藏品软件

测试平台解决了什么问题?

老张

测试平台

Scala模式匹配的亮点——Martin Odersky访谈(四)_Java_Bill Venners_InfoQ精选文章