① 话说模式匹配 scala里是怎么实现的
一、简单例子:
1 abstract class Expr
2 case class Var(name:String) extends Expr
3 case class Number(num:Double) extends Expr
4 case class UnOp(operator : String , arg : Expr) extends Expr
5 case class BinOp(operator : String , left : Expr , right : Expr) extends Expr
样本类:添加了case的类便是样本类。这种修饰符可以让Scala编译器自动为这个类添加一些语法上的便捷设定。如下:
添加与类名一致的工厂方法。也就是说,可以写成Var("x")来构造Var对象。
样本类参数列表中的所有参数隐式获得了val前缀,因此它被当作字段维护。
编译器为这个类添加了方法toString,hashCode和equals等方法。
模式匹配:
复制代码
1 def simplifyTop(expr : Expr) : Expr = expr match{
2 case UnOp("-" , UnOp("-" , e)) => e
3 case BinOp("+" , e , Number(0)) => e
4 case BinOp("*" , e , Number(1)) => e
5 case _ => expr
6 }
清单 15.1
复制代码
match对应java里的switch,但是写在选择器表达式之后。即: 选择器 match {备选项}。
一个模式匹配包含了一系列备选项,每个都开始于关键字case。每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。
match表达式通过以代码编写的先后次序尝试每个模式来完成计算。类似于UnOp("-" , UnOp("-" , e))这种形式的,是构造器模式匹配。
match与switch的比较:匹配表达式可以被看作Java风格Switch的泛化。但有三点不同:
match是Scala的表达式,始终以值作为结果;
Scala的备选项表达式永远不会“掉到”下一个case;
如果没有模式匹配,MatchError异常会被抛出。这意味着必须始终确信所有的情况都考虑到了,或者至少添加一个默认情况什么都不做。如 case _ =>
二、模式的种类
通配模式:case _ => 。表示默认的全匹配备选项。通配模式还可以用来忽略对象中不关心的部分。如:case BinOp(_,_,_) => XXX,则表示不关心二元操作符的元素是什么,只是检查是否为二元操作符
常量模式 :仅匹配自身。任何字面量都可以用作常量。包括String类型。另外,任何的val或单例对象也可以被用作常量。如,单例对象Nil是只匹配空列表的模式。
变量模式 :变量模式类似于通配符,可以匹配任何对象。不同点在于,Scala把变量绑定在匹配的对象上。之后就可以使用这个变量操作对象。如:
expr match{
case 0 => "zero"
case somethingElse => "not zero" + somethingElse
}
清单 15.6
有一个注意点:Scala使用了一个简单的文字规则来区分是一个常量还是一个变量:用小写字母开始的简单名被当作是模式变量。
构造器模式:它的存在使得模式匹配真正变得强大。它由名称及若干括号之内的模式构成。如BinOp("+" , e , Number(0))。
序列模式:可以像匹配样本类那样匹配List或Array这样的序列类型。同样的语法现在可以指定模式内任意数量的元素。如:
1 expr match{
2 case List(0 , _ , _ ) => println("found it")
3 case _ =>
4 }
清单 15.8
如果想匹配一个不指定长度的序列,可以指定_*作为模式的最后元素。它能匹配序列中0到任意数量的元素。
元组模式:例子:
复制代码
1 def tupleDemo(expr : Any) =
2 expr match {
3 case (a , b, c) => println("matched " + a + b + c)
4 case _ =>
5 }
清单 15.9
复制代码
类型模式:可以把它当做类型测试和类型转换的简易替代。例如:
复制代码
1 def generalSize(x : Any) = x match{
2 case s : String => s.length
3 case m : Map[_ , _] => m.size
4 case _ => 1
5 }
清单 15.11
复制代码
使用: scala> generalSize(Map(1 -> 'a' , 2 -> 'b'))
res15 : Int = 2
另:在Scala里类型测试和转换的代码很冗长,因为不建议这么做。
模式守卫:模式变量仅允许在模式中出现一次。如:
1 case BinOp("+" , x , x ) => BinOp("*" , x , Number(2))
这种写法就会出错。可以改成: case BinOp("+" , x , y ) if x == y => BinOp("*" , x , Number(2))
模式守卫接在模式之后,开始于if。守卫可以是任意的引用模式中变量的布尔表达式。如果存在模式守卫,那么只有在守卫返回true的时候匹配才成功。
② idea scala工程怎么找不到简单的scala工程,只有基于sbt的
创建一个Scala – sbt项目sbtFirst 虽然这个项目采用sbt格局,但少了sbt-idea插件,IDEA并不能对这个项目进行操作,只能打开sbt console。 首先我们需要修改项目的build.sbt文件,添加需要的Scala版本和libraryDependencies,注意sbt文件每行之间.
③ scala命令是怎么编译运行scala程序的
scala命令是怎么编译运行scala程序的
所以既要使用到Unicode的大的字库表,又要节省存储空间,就需要对Unicode再编码,且是根据(Unicode编码.n)内容 不定长编码 --- UTF-8是一种对(Unicode编码.n)的不定长字符编码方案。
UTF-8字符编码方案决定了(Unicode编码.n)在计算机内的存储方式。
(Unicode编码.n)经过UTF-8字符编码方案编码之后也可以看做是一个新的二进制数字,(通常用十六进制数字字符表示这个新的二进制的值,它们直接的关系是这个十六进制字符表示的值 等于 这个二进制数字的值)。
④ idea scala工程怎么找不到简单的scala工程,只有基于sbt
创建一个Scala – sbt项目sbtFirst
虽然这个项目采用sbt格局,但少了sbt-idea插件,IDEA并不能对这个项目进行操作,只能打开sbt console。
首先我们需要修改项目的build.sbt文件,添加需要的Scala版本和libraryDependencies,注意sbt文件每行之间必须有空行。
name := “sbtTest”
⑤ 如何配置sbt的build.sbt使得编译时将依赖包也打包进去
首先问题解决了,就是sbt-assembly插件的配置问题。这个文档自己前两次读表示看不懂意思。过2天又仔细看了一遍大致明白了,敢动手操作了。
assembly插件的目的是:
The goal is simple: Create a fat JAR of your project with all of its dependencies.
即将项目依赖的大文件也打包到生成的jar中。我的报错Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/spark/streaming/kafka/KafkaUtils$就是因为包含KafkaUtil的jar包没打包到jar中。
配置使用插件
下面是我的scala项目目录结构:
.
├── assembly.sbt
├── build.sbt
├── project
├── README.md
├── run-assembly.sh
├── run.sh
├── src
└── target
插件的配置取决于sbt的版本,详情见这里
我的是sbt 0.13.8,所以在project/assembly.sbt添加(assembly.sbt)要自己创建:
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.0")
配置assembly的参数
参数在项目根目录下新建assembly.sbt。
直接引入插件就可以用
sbt assembly
编译了,但是由于我的sbt下载了大量的依赖包,在编译的时候遇到了编译包冲突的问题。这个时候需要配置Merge Strategy(合并策略)
⑥ 话说模式匹配 scala里的赋值语句都是模式匹配吗
先抛个问题,下面的语句是否都合理(编译通过),哪些会引起模式匹配?
scala>vala=100
scala>valA=100
scala>vala@b=100
scala>val(a,b)=(100,200)
scala>val(a,B)=(100,200)//第二个变量大写
scala>valArray(a,b)=Array(100,200)
scala>valArray(a,B)=Array(100,200)
scala>objectTest{val2=2}
scala>objectTest{val2=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{casev1=>println(v1)}
//变量绑定模式,把变量v2绑定在v1这个模式上
scala>"hello"match{casev2@v1=>println(v2)}
上面的例子中,第一行中v1是个变量模式。 第二行中v2是一个新的变量,只有在v1这个模式匹配成功的情况下,才会把自己绑定到v1上,而v1因为是一个变量模式,它总能匹配成功,所以这里v2也会绑定到”hello”对象上。变量绑定模式通常不会这么使用,更多用在绑定到一个复合结构的模式上,如:
scala>List(1,List(2,3))match{caseList(_,x@List(2,_*))=>println(x.size)}
2
把变量x绑定到了嵌套的 List(2,3) 这个对象上
但赋值语句val a@b = 100跟上面的有关系么?我们通过ToolBox看看它”脱糖”后的语法树:
scala>tb.parse("vala@b=100")
res13:tb.u.Tree=
{
<synthetic>private[this]valx$3=100:@scala.uncheckedmatch{
case(a@(b@_))=>scala.Tuple2(a,b)//这一句
};
vala=x$3._1;
valb=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]valx$4=scala.Tuple2(100,200):@scala.uncheckedmatch{
casescala.Tuple2((a@_),(b@_))=>scala.Tuple2(a,b)
};
vala=x$4._1;
valb=x$4._2
}
看到了,是一个构造器模式与变量绑定模式的混合模式匹配。
再下一个val (a,B) = (100,200)这个与上一个有区别么?回顾一下第一篇里讲到的“常量模式”:当变量大写时将被对待为常量模式,也就是说大写B和上面的小写b是两种不同的模式!!
scala>tb.parse("val(a,B)=(100,200)")
res15:tb.u.Tree=
vala=scala.Tuple2(100,200):@scala.uncheckedmatch{
casescala.Tuple2((a@_),B)=>a
}
大写B在这里当作常量来解析,但又找不到B这个变量(除非之前有定义过),就报错了:
scala>val(a,B)=(100,200)
<console>:8:error:notfound:valueB
val(a,B)=(100,200)
^
后边两个Array的赋值语句与这两个类似,小括号写法只是元组(Tuple)的语法糖而已。
最后,真正有趣,且会让新手崩溃的情况object Test { val 2 = 2 }为什么这个编译和初始化都没问题?
scala>objectTest{val2=2}
definedmoleTest
scala>Test
res16:Test.type=Test$@3042dc22
简直逆天,难道这个背后也与模式匹配有关系么?
scala>tb.parse("objectTest{val2=2}")
res0:tb.u.Tree=
objectTestextendsscala.AnyRef{
def<init>()={
super.<init>();
()
};
<synthetic>private[this]valx$1=2:@scala.uncheckedmatch{
case2=>()
}
}
确实又是一个常量模式匹配,2匹配2,成功。
同理,下一个object Test { val 2 = 3 }也是个常量模式匹配,但为何明显不匹配,却可以编译时成功,而运行时时才报错呢?
scala>objectTest{val2=3}
definedmoleTest
scala>Test
scala.MatchError:3(ofclassjava.lang.Integer)
atTest$.<init>(<console>:8)
这是因为object 是惰性初始化的原因(lazy),如下:
//对下面的单例
objectTest{vala=2}
$scalac-Xprint:jvmA.scala
package<empty>{
objectTestextendsObject{
private[this]vala:Int=_;
<stable><accessor>defa():Int=Test.this.a;
def<init>():Test.type={
Test.super.<init>();
Test.this.a=2;//在初始化时才对成员赋值
()
}
}
}
在对多个变量赋值,或变量中有@符合,导致模式匹配还好理解,但”2=2″也引起模式匹配就会让我产生疑问:
是否所有的赋值语句都是模式匹配?
为了验证,通过编译选项查看val a=2这样对单个变量的赋值却没有看到模式匹配。
另外,如果单个变量也是模式匹配,为何大写字母val A=2没问题?假设对单个变量赋值也是模式匹配,那岂不无法定义大写的变量了;肯定是有区别的,但又怎么区分的?
在那个帖子里,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"这样的赋值语句虽然是一个变量,但变量名称不符合上面的约束,产生了模式匹配。至于为何不修复这个问题(直接在编译时报错),也可以从这个帖子的线索中找到原因。
⑦ linux下用sbt编译scala需要安装哪些工具
1、下载sbt通用平台压缩包:sbt-0.13.5.tgz2、建立目录,解压文件到所建立目录 $ sudo mkdir /opt/scala/sbt$ sudo tar zxvf sbt-0.13.5.tgz -C /opt/scala/3、建立启动sbt的脚本文件/*选定一个位置,建立启动sbt的脚本文本文件,如/opt/scala/s...
⑧ 使用scala 必须安装sbt吗
不需要。sbt是一个构建工具,而Scala可以直接从官网上下载到开发工具包
⑨ sbt编译时怎么匹配scala版本
build.sbt中写入:
scalaVersion := "2.10.5"
就会使用对应的版本的库进行编译