引言

最近抱着《Programming in Scala》(英文第二版)在死啃Scala。在阅读第26章Extractor时,偶然在Stack Overflow上搜到一个帖子《Scala pattern matching variable binding》。其中Daniel C. Sobral的回答,让我产生了一系列的思考,深感其一语道破天机、顿觉醍醐灌顶,故得此文。

问题

Zapadlo问道:为什么这样会在第一行case出错:IsUpperCase错误的参数数量?

object IsUpperCase {
def unapply(s: String): Option[String] = {
if (s.toUpperCase() == s) {
Some(s)
} else {
None
}
}
} val s = "DuDu@qwadasd.ru"
s match {
case u @ IsUpperCase() => println("gotcha!")
case _ =>
}

而换成这样却又可以?

case IsUpperCase(u) => println("gotcha!")

答案

Daniel回答道:

A pattern match works the opposite of a function call. Where X(a, b) pass parameter "a" and "b" to "X" outside a pattern match, in a pattern match "a" and "b" are returned from "X".

意思指:

运用模式匹配的过程,其实是函数调用的逆过程。函数调用的过程,是给定参数a和b,然后得到x;而模式匹配,则是根据x,逆向得到a和b的过程。

并由此给出修改意见:在使用变量绑定方式进行模式匹配时,给IsUpperCase() 一个占位符用作其参数

case u @ IsUpperCase(_) => println("gotcha!")

思考

回想书中关于Extractor中apply()unapply()的关系:『前者是注入器,利用给定参数得到某个设定集合里的一个元素;后者则是把给定的元素分解为若干个组成部分。』于是,将Daniel答复中的function call换成apply,将pattern match用unapply代替之,也是可行的。

那么,apply的输入参数将是unapply的输出,反之亦然。Extractor的实质,就是利用unapply去还原可能的apply的参数。所以在模式匹配时,我们需要用变量名或者占位符_去绑定解析的结果,为匹配结果留出相应的坑。

反观上例,unapply()的输出是Option[String],那说明对应的必定是apply(s:String),所以在模式匹配时,我们需要给IsUpperCase()一个变量来接收匹配的结果。这就是为什么Zapadlo采取第二种方式可行的原因。

再来想想为什么采用『变量绑定』的方式时,需要使用占位符_?这是因为变量绑定,可以说是Scala为模式匹配中标准的变量匹配模式提供的一个语法糖。u @ IsUpperCase(_)这样的写法,相当于先匹配@后面的部分,然后把匹配的结果赋值给变量u。而从前面的分析得出,IsUpperCase需要至少一个参数,所以我们用占位符代替了这个参数。

最后,再想想为什么把Extractor中unapply的返回值改为Boolean后又可以了呢?这是因为,这时候apply是纯粹多余的、没有意义的,Extractor并不需要承担构造的任务,不需要绑定到某个或者某些变量上,它只是做个简单的判断。所以,在书中的第636页专门描述了这种情形,这也是Scala语言规范的一部分:

It’s also possible that an extractor pattern does not bind any variables. In that case the corresponding unapply method returns a boolean—true for success and false for failure.

这种情况下,类似这样的Extractor是没有办法象下面这个Email一样,利用匹配进行对象还原的。从另一个角度,其实我们也想得通,IsUpperCase.apply(true)能得到某个特定的字符串吗?

Email.unapply(obj) match {
case Some(user, domain) => Email.apply(user, domain)
}

理解了这些,理解Extractor多个变量时就会感觉容易多了,不外乎解析得到类似列表或者String*这样未知参数个数的结果了。

结语

Scala是一门夹杂了函数式编程的面向对象编程语言。在学习的过程中,val、equals与==、eq与ne,还有著名的akka框架等等等等,无一不让我心潮澎湃,让我隐约感受到了应用Scala实践DDD时将获得的那种顺畅感。希望能尽早完成基础语法的学习,再小试牛刀,哈哈。

理解Scala中的Extractor的更多相关文章

  1. Scala中的Extractor

    Scala中使用unapply方法可以实现三种extractor(另外使用unapplySeq也可以实现extractor) def unapply(object: S): Option[(T1, . ...

  2. 转载:理解scala中的Symbol

    相信很多人和我一样,在刚接触Scala时,会觉得Symbol类型很奇怪,既然Scala中字符串都是不可变的,那么Symbol类型到底有什么作用呢? 简单来说,相比较于String类型,Symbol类型 ...

  3. Scala中的协变,逆变,上界,下界等

    Scala中的协变,逆变,上界,下界等 目录 [−] Java中的协变和逆变 Scala的协变 Scala的逆变 下界lower bounds 上界upper bounds 综合协变,逆变,上界,下界 ...

  4. Scala 中的函数式编程基础(一)

    主要来自 Scala 语言发明人 Martin Odersky 教授的 Coursera 课程 <Functional Programming Principles in Scala>. ...

  5. scala调用java的方法,返回了一个对象链表List<Student>,在scala中遍历该链表获取指定Student的名字name

    假设Student类如下: class Student { private int no; private String name; public int getNo() { return no; } ...

  6. scala中常用但其他语言不常见的符号含义

    本文旨在介绍Scala在其他语言中不太常见的符号含义,帮助理解Scala Code. 随着我对Scala学习的深入,我会不断增加该篇博文的内容. 修改记录 ----2016.11.23  新增scal ...

  7. Scala中的override

    Scala中的override override是覆盖的意思,在很多语言中都有,在scala中,override是非常常见的,在类继承方面,它和java不一样,不是可写可不写的了,而是必须写的.如果不 ...

  8. Programming In Scala笔记-第七章、Scala中的控制结构

    所谓的内建控制结构是指编程语言中可以使用的一些代码控制语法,如Scala中的if, while, for, try, match, 以及函数调用等.需要注意的是,Scala几乎所有的内建控制结构都会返 ...

  9. Programming In Scala笔记-第五章、Scala中的变量类型和操作

    这一章的一些基础性的东西,主要包括Scala中的基本变量类型,以及相关的一些操作符. 一.简单类型 下表中列出Scala语言中的基本类型,以及其字节长度,其中Byte, Short, Int, Lon ...

随机推荐

  1. iOS开发进阶 - 项目的本地化处理(多语言开发)

    移动端访问不佳,请访问我的个人博客 最近项目本地化,需要支持多国语言,下面将本地化的步骤记录下来,方便查找使用,步骤很简单,有些地方也有坑,希望大家看后少走弯路~~ 什么是本地化 本地化说直白点就是多 ...

  2. mybatis映射文件select_resultMap_关联查询_collection定义关联集合

    知识点:查询一个实体类,并查出这个类下面的集合 Employee.java实体类 package com.hand.mybatis.bean;public class Employee {    pr ...

  3. 【Semantic Segmentation】Segmentation综述

    部分转自:https://zhuanlan.zhihu.com/p/37618829 一.语义分割基本介绍 1.1 概念 语义分割(semantic segmentation) : 就是按照" ...

  4. static、final和finalize详解

    一.static 修饰符 数据共享 成员变量(实例变量)和静态变量(类变量)的区别 两个变量的生命周期不同 成员变量随对象的创建而存在,随对象被回收而释放 静态变量随类的加载而存在,随类的消失而消失 ...

  5. PHPCMSV9的CKEDITOR编辑器增加行距

    lineheight插件,下载地址:http://files.cnblogs.com/ysfng/ckeditor-lineheight.zip 第一步,下载lineheight插件,并解压到\cke ...

  6. Python面向过程和面向对象基础

    总结一下: 面向过程编程:根据业务逻辑从上到下的写代码-----就是一个project写到底,重复利用性比较差 函数式:将某些特定功能代码封装到函数中------方便日后调用 面向对象:对函数进行分类 ...

  7. python中enumerate()函数用法

    python中enumerate()函数用法 先出一个题目:1.有一 list= [1, 2, 3, 4, 5, 6]  请打印输出:0, 1 1, 2 2, 3 3, 4 4, 5 5, 6 打印输 ...

  8. 明确出需求 然后开会评审 要什么接口 接口参数、返回json内容、格式 协定好 在做

     明确出需求 然后开会评审 要什么接口 接口参数.返回json内容.格式 协定好 在做 

  9. 自已开发完美的触摸屏网页版仿app弹窗型滚动列表选择器/日期选择器

    手机端网页版app在使用下拉列表时,传统的下拉列表使用起来体验非常不好,一般做的稍好一点的交互功能界面都不会直接使用下拉列表,所以app的原生下拉列表都是弹窗列表选择,网页型app从使用体验上来当然也 ...

  10. 启用/禁用以太网的批处理,用于一个网卡切换本地网络和wifi使用(Win10)

    注意下面时英文版上默认网络使用,同时接入了网线和wifi时,本地网络优先wifi. 所以禁用本地网络就会自动连接到wifi,启用本地网络,就会禁用wifi. 批处理支持 -y 参数,跳过用户输入y,代 ...