转载自并发编程网 – ifeve.com 原文:http://ifeve.com/pattern-matching-4/
先抛个问题,下面的语句是否都合理(编译通过),哪些会引起模式匹配?
scala> val a = 100 scala> val A = 100 scala> val a@b = 100 scala> val (a,b) = (100,200) scala> val (a,B) = (100,200) //第二个变量大写 scala> val Array(a,b) = Array(100,200) scala> val Array(a,B) = Array(100,200) scala> object Test { val 2 = 2 } scala> object Test { val 2 = 3 }
我们先看看其他语言(对scala有影响的)有关赋值语句的定义:
1) 在 ML 语言里,对赋值语句的定义:
val P = E
表示定义了模式P
中的变量,并赋予它们表达式E
中相应的值。
2) 在Erlang中等号 = 表示一个模式匹配操作
在这两种语言中,赋值语句都明确的定义为模式匹配,那么scala中,所有的赋值语句是否都是模式匹配呢?
尤其scala可以说在函数式风格上与ML(及其家族)语言有某种血缘,在这一点上是否也与ML完全一致呢?
先分析一下上面的每条赋值语句:val a = 100
和 val A = 100
是直观且没有歧义的。
val a@b = 100
是什么意思?回忆一下第一篇里讲过的“变量绑定模式”,当时的例子有点复杂,重新理解一下:
//给"hello"字符串对象用v1这个变量名 scala> "hello" match { case v1 => println(v1) } //变量绑定模式,把变量v2 绑定在v1这个模式上 scala> "hello" match { case v2@v1 => println(v2) }
上面的例子中,第一行中v1是个变量模式。 第二行中v2是一个新的变量,只有在v1这个模式匹配成功的情况下,才会把自己绑定到v1上,而v1因为是一个变量模式,它总能匹配成功,所以这里v2也会绑定到”hello”对象上。变量绑定模式通常不会这么使用,更多用在绑定到一个复合结构的模式上,如:
scala> List(1,List(2,3)) match { case List(_, x@List(2,_*)) => println(x.size) } 2
把变量x绑定到了嵌套的 List(2,3) 这个对象上
但赋值语句val a@b = 100
跟上面的有关系么?我们通过ToolBox看看它”脱糖”后的语法树:
scala> tb.parse("val a@b=100") res13: tb.u.Tree = { <synthetic> private[this] val x$3 = 100: @scala.unchecked match { case (a @ (b @ _)) => scala.Tuple2(a, b) //这一句 }; val a = x$3._1; val b = x$3._2 }
有注释的那一句里面把a,b两个局部变量绑定到通配符”_”上,而这个通配符模式case _ =>
可以匹配任何对象,所以相当于把a,b两个变量绑定到了100这个对象上,并产生了一个二元组记录这两个局部变量值。最终把二元组里的值分别赋给了我们定义的a,b两个变量。
接下来的val (a,b) = (100,200)
这个赋值也容易理解,把二元组里的值分别赋给a,b两个变量么,也是经过模式匹配的么?继续用ToolBox分析:
scala> tb.parse("val (a,b)=(100,200)") res14: tb.u.Tree = { <synthetic> private[this] val x$4 = scala.Tuple2(100, 200): @scala.unchecked match { case scala.Tuple2((a @ _), (b @ _)) => scala.Tuple2(a, b) }; val a = x$4._1; val b = x$4._2 }
看到了,是一个构造器模式与变量绑定模式的混合模式匹配。
再下一个val (a,B) = (100,200)
这个与上一个有区别么?回顾一下第一篇里讲到的“常量模式”:当变量大写时将被对待为常量模式,也就是说 大写B 和上面的 小写b 是两种不同的模式!!
scala> tb.parse("val (a,B)=(100,200)") res15: tb.u.Tree = val a = scala.Tuple2(100, 200): @scala.unchecked match { case scala.Tuple2((a @ _), B) => a }
大写B在这里当作常量来解析,但又找不到B这个变量(除非之前有定义过),就报错了:
scala> val (a,B) = (100,200) <console>:8: error: not found: value B val (a,B) = (100,200) ^
后边两个Array的赋值语句与这两个类似,小括号写法只是元组(Tuple)的语法糖而已。
最后,真正有趣,且会让新手崩溃的情况 object Test { val 2 = 2 }
为什么这个编译和初始化都没问题?
scala> object Test { val 2 = 2 } defined module Test scala> Test res16: Test.type = Test$@3042dc22
简直逆天,难道这个背后也与模式匹配有关系么?
scala> tb.parse(" object Test { val 2 = 2 }") res0: tb.u.Tree = object Test extends scala.AnyRef { def <init>() = { super.<init>(); () }; <synthetic> private[this] val x$1 = 2: @scala.unchecked match { case 2 => () } }
确实又是一个常量模式匹配,2匹配2,成功。
同理,下一个 object Test { val 2 = 3 }
也是个常量模式匹配,但为何明显不匹配,却可以编译时成功,而运行时时才报错呢?
scala> object Test { val 2 = 3 } defined module Test scala> Test scala.MatchError: 3 (of class java.lang.Integer) at Test$.<init>(<console>:8)
这是因为object 是惰性初始化的原因(lazy),如下:
// 对下面的单例 object Test { val a = 2 } $ scalac -Xprint:jvm A.scala package <empty> { object Test extends Object { private[this] val a: Int = _; <stable> <accessor> def a(): Int = Test.this.a; def <init>(): Test.type = { Test.super.<init>(); Test.this.a = 2; //在初始化时才对成员赋值 () } } }
在对多个变量赋值,或变量中有@符合,导致模式匹配还好理解,但”2=2″也引起模式匹配就会让我产生疑问:
是否所有的赋值语句都是模式匹配?
为了验证,通过编译选项查看val a=2
这样对单个变量的赋值却没有看到模式匹配。
另外,如果单个变量也是模式匹配,为何大写字母val A=2
没问题?假设对单个变量赋值也是模式匹配,那岂不无法定义大写的变量了;肯定是有区别的,但又怎么区分的?
我最初遇到这个困惑,在邮件列表里问了这个问题,得到了一些回复,并且有人给了一个老帖子链接说早就讨论过val 1=2
这个话题了:http://thread.gmane.org/gmane.comp.lang.scala.user/44036
在那个帖子里,martin也回复了为何 val 1=2
是模式匹配,并且为何不把这种情况作为错误给修复掉:
A value definition is of the form
val <pattern> = <expression> // 这个同ML和Erlang语言 1 is a <pattern>
There is one edge case:
If the pattern is a single variable (upper or lower case or backquoted), then it is always treated as a variable, not a constant. Otherwise, there would be no way to define such a value.只有一种边缘情况:如果模式是一个单独的变量(大写、小写、或用反引号引起来的),那么它总被当作变量,而非常量。否则就没法定义这样的一个值。
所以1=2
, "a"="b"
这样的赋值语句虽然是一个变量,但变量名称不符合上面的约束,产生了模式匹配。至于为何不修复这个问题(直接在编译时报错),也可以从这个帖子的线索中找到原因。
相关推荐
scala for循环中的模式匹配:1)变量模式匹配2)常量模式匹配3)变量绑定模式匹配4)类型模式匹配5)构造函数模式匹配6)序列模式匹配
scala正则表达式在模式匹配中的应用:提取模式的分组值,通过代码理解正则表达式与模式匹配的原理。
Scala模式匹配.md
scala:Scala和设计模式 疯狂代码 http://CrazyCoder.cn/ ĵ:http:/CrazyCoder.cn/Java/Article42687.html 在当前软件Software设计中最流行要算GoF这本书中提出各种设计模式很多人认为设计模式对于语言(特 别是c/Java...
响应式架构 消息模式Actor实现与Scala.Akka应用集成 响应式架构 消息模式Actor实现与Scala.Akka应用集成
scala连接redis哨兵模式 demo 使用scala的redis库(csdn)————程序
Scala设计模式,Scala高阶参考书。
Scala流程控制语句.md
scala中的模式匹配,还能用来匹配集合。 匹配数组 示例说明 依次修改代码定义以下三个数组 Array(1,x,y) // 以1开头,后续的两个元素不固定 Array(0) // 只匹配一个0元素的元素 Array(0, …) // 可以任意数量,但是...
scala 设计模式,详细讲述scala设计模式
Scala语言规范-Scala 是一门类 Java 的编程语言,它结合了面向对象编程和函数式编程。 Scala 是纯面向对象的,每个... Scala 也支持一种通用形式的模式匹配,模式匹配用来操作代数式类型,在很多函数式语言中都有实现。
主要介绍了Scala中正则表达式以及与模式匹配结合,本文给大家介绍了多种模式匹配方式,需要的朋友可以参考下
消息模式Actor实现与Scala、Akka应用集成
第4章 模式匹配 86 4.1 简单匹配 86 4.2 match中的值、变量和类型 87 4.3 序列的匹配 90 4.4 元组的匹配 94 4.5 case中的guard语句 94 4.6 case类的匹配 95 4.6.1 unapply方法 96 4.6.2 ...
大数据课程——Scala编程基础,教师版,提供教学大纲、教案、教学设计、实训文档等,课程内容包含教学准备环境、软件安装、作业、教学文档、演示视频,花费巨额时间亲自制作,下载后可私信提供上述所有教学资料,可...
Scala IDEA 最新插件,匹配 版本 2017.1, scala-intellij-bin-2017.1.15
Scala概述.pptx 变量.pptx 运算符.pptx 程序流程控制.pptx 函数式编程基础.pptx ...模式匹配.pptx 函数式编程高级.pptx 泛型 上下界 视图界定 上下文界定 协变逆变不变.pptx AKKA.pptx 韩顺平_Scala语言核心编程.pdf
响应式架构++消息模式Actor实现与Scala.Akka应用集成+,沃恩·弗农+