书名:Scala 学习手册/(美)Swartz,J. 著;苏金国等译。–北京:中国电力出版社,2016.2
第1章 概述
- 输出:
println("hello world!")
,String
字符为双引号,Char
字符为单引号 - REPL:Read,Evaluate,Print,Loop.可以跨会话存储
- up 箭头:上一行命令
- 每一行的输出
result
REPL 会用一个常量变量保存:res0,res1,...
- 运行
scala
文件hello.scala
- 交互环境中输入
scala> :load hello.scala
- 命令行:
scala hello.scala
- 交互环境中输入
- 退出:
:q
第2章 数据类型(数值类型:Int,Double
,非数值类型:String,Char
)
- 字面量值(不可重新赋值):
scala> val x:Int=10
,不可变有类型的存储文件,用关键字val
(value),类型可省略scala> val x = 10
- 变量:
scala> var a:Double=2.73
,关键字var
(variable),类型可省略scala> var a=2.73
- 字符串
val approx = 355/113f println("Pi,using 355/113,is about "+approx+".") println(s"Pi,using 355/113,is about $approx.") //内插法
- 字符串内插(替代格式
printf
,难读,输出可格式化)val item = "apple" println(s"How do you like them ${item}s?") println(s"Fish n chips n ,${"pepper "*3}salt") // 字符串乘法 println(f"I wrote a new $item%.3s today") println(f"Pi,using 355/113,is about ${355/113.0}%.5f.") //内插法
- 正则表达式
"Froggy went a' courting" matches ".* courting" //Ture or False "milk,tea,muck" replaceAll ("m[^ ]+k","coffee") "milk,tea,muck" replaceAll ("m[^ ].k","coffee") "milk,tea,muck" replaceFirst ("m[^ ]+k","coffee") "milk,tea,muck" replaceFirst ("m[^ ].k","coffee") // 捕获组:小括号定义,输入至少包括一个匹配值 val input = "Enjoy this apple 3.14159 times today" val pattern = """.* apple ([\d.]+) times .*""".r // r 为字符串操作符 val pattern(amountText) = input val amount = amountText.toDouble
- 类型操作(每个类型都属于一个对象)
scala> 5.asInstanceOf[Double]
:val/var 类型转换(尽量用第五条)scala> nada.getClass
:val/var 查看类型- val/var 类型确认:
scala> nada.isInstanceOf[Unit]
- val/var 散列码:
scala> "A".hashCode
- val/var 转换函数:
toString/toByte/toFloat/...
- 元组:包含两个以上值得有序容器,值类型可不同,不可迭代但可通过下标访问。类似关系型数据库中得一行
// 元组,下标从1开始 val info = (5,"Korben",true) val name = info._2 val red = "red"->"0xff0000" // 利用关系操作符创建2元组 val reversed = red._2 -> red._1
注:
1. 值优先于变量,因为值可以为代码带来稳定性和可预测性。
2. 类型为Double 的变量可以赋 Int 值,因为Int数可以自动转换为Double 数。
3. 变量命名建议:`camel case`驼峰命名
4. 字符串支持三重双引号
5. Scala 不支持其他类型到Boolean 的自动转换,非Null 字符不会计算为True,0也不等于False
6. &&与||很懒,第一个参数可以出结论,不在计算第二个参数。&与| 会对两个参数都做检查
7. Unit 字面量:`scala> val nada=()`,通常用来定义函数和表达式
第3章 表达式 和 条件式
表达式
-
表达式:
scala> val x=5*20;val amount = x+10
,字面量val/var 也是一种表达式 -
表达式块:
scala> val amount = {val x=5*20;x+10}
块中最后一个表达式最为块得返回值。- 表达式块分号,可用换行替代
- 表达式块可以嵌套
scala> {val a=1;{val b=a*2;{val c=b+4;c}}}
// 结果可以赋值给变量 val x = -3 if (x>0) { println("This is a positive number!") } else if (x==0){ println("This is zero") } else { println("This is a negative number !") }
-
if…else 表达式块:
scala> val max = if(5>3) {val x=4*2;x+2} else 2
-
匹配表达式(
match expressions
):类似 switch// 匹配表达式 val status=500 val message = status match { case 200 => "ok" case 400 => println("ERROR - we called the service incorrectly");"error" case 500 => { println("ERROR - the service encountered an error");"error" } } // 模式替换式 pattern alternative val day = "MON" val kind = day match { case "MON"|"tue"|"wed"|"thu"|"fri" => "weekday" case "sat"|"sun" => "weekend" case unexpected => unexpected + " is not expected!" // 除了通配符,匹配值还可以获取 }
-
通配模式匹配
//值绑定 val message="ok" val status = message match { case "ok" => 200 case other => println(s"could not parse $other");-1 }//other 为case 块定义,并赋值为 message //通配符 val message="Tom" val status = message match { case "ok" => 200 case _ => println(s"could not parse $message");-1 }//_ 通配符,可以匹配任意值,右侧不可访问.若需要访问,可直接访问输入 // 类型模式 for (elem <- List(9,12.3,"spark","hadoop",'hello)){ println("current value is:",elem) val result = elem match { case i:Int => i + " is an int value" case d:Double => d + " is an double value" case "spark" => "spark is find" case s:String => s + " is a string value" case _ => "this is an unexpected value" } println(result) }
-
模式哨卫匹配(case 后面 + if 表达式,可不加括号)
val response:String ="ok1" response match { case s if s!= null => println(s"Received '$s'") case s => println("ERROR! Received a null response") }
-
模式变量匹配类型(应该不常用)
循环(表达式的控制结构)
- for 循环
<-
类似python 中的in
。(x <- 1 to 7 by 1)
为生成器for (x <- 1 to 7 by 1) {println(s"Day $x")} println(s"${"*"*3}等价于${"*"*3}") val week = for (x <- 1 until 8 by 1) yield {s"Day $x:"} //返回值将作为一个集合返回,可迭代,for 推导式 for (day <- week) print(day+",") // print 不换行
- 迭代器哨卫(
iterator guard/filter
,增加一个 if表达式)val threes = for(i<-1 to 20 if i%3==0) yield i val quote = "Faith,Hope,,Charity,null" for {t<-quote.split(",");if t!=null;if t.size>0}{println(t)} //迭代器与迭代器哨卫 分开
- 嵌套迭代器(多层循环):
scala> for {x<-1 to 2;y<-3 to 5}{print(s"($x,$y)")}
- 值绑定 for 循环:
val powersOf2 = for (i<-0 to 8;pow=1<<i) yield pow // 1 左移 i 位
- while/do…while… 循环
- while:
scala> var x=10;while (x>=0) x -= 1
- do…while:
val x=0;do println(s"Here I am,x=$x") while (x>0)
- while:
第4章 函数
定义:def multiplier(x:Int,y:Int): Int = {x*y}
函数返回值:一般为表达式块最后一行,也可以使用 return 关键字返回并退出
- 过程 procedure:没有返回值的函数
- 隐式返回类型:
def log(d:Double) = println(f"Got value $d%.2f")
- 显示返回类型:
def log(d:Double):Unit = println(f"Got value $d%.2f")
- 隐式返回类型:
- 无参函数:
def hi():String ="hi"
- 块表达式函数:参数为块表达式,可赋 默认值
def formatEuro(amt:Double = 10) = f" $amt%.2f" formatEuro{val rate = 0.011;1500*rate}
- 递归函数
- 递归缺点:栈溢出,调用递归函数太多,耗尽分配的内存
- 尾递归特点:不使用额外的栈空间
// 计算 x^n 幂 def powerN(x:Int,n:Int):Double = { n match { case n if n<0 => 1.0/x * powerN(x,n+1); case n if n>=1 => x * powerN(x,n-1); case n => 1 } } // tail-recursion 递归调用函数本身作为返回值,才能由Scala 编译器完成尾递归优化 @annotation.tailrec def power(x:Int,n:Int,t:Int=1):Int = { if (n<1) 1 else power(x,n,x*t) }
- 嵌套函数
def max(a:Int,b:Int,c:Int):Int = { def max(x:Int,y:Int) = if(x>y) x else y max(a,max(b,c)) // 局部函数优先于外部函数 }
- Vararg 参数
def mysum(item:Int*):Int = { var total = 0 for (i<- item) total += i total } mysum();mysum(20,30)
- 参数组:
def max(x:Int,y:Int) = if(x>y) x else y
- 方法和操作符
- 函数常存在于对象中,函数更适合的说法通常是方法。
- Scala 没有任何四则运算符,Scala 使用的算术运算符其实都是实例的方法。这里不使用传统点记法,而是使用空格来分割对象(2)、操作符方法(+)、和方法的参数(3)。例如:
2 + 3
scala 的操作符记法为2.+(3)
val d= 65.642 d.compare(18.0) d compare 18.0 1 + 2 + 3 (1.+(2)).+(3)
第六章 常用数据类型
-
List:可迭代
- filter:
List(23,8) filter (_ > 18)
- 合并:
List(List(1,2),List(3,4)).flatten
- 拆分:
List(1,2,3,4,5) partition (_<3)
- 切片:
List(1,2,3,4,5) slice (1,3)
- top n:
List(1,2,3,4,5) take 3
- zip:
List(1,2) zip List("a","b")
val colors = List("red","green","blue") println(s"The colors value's size head remain is ${colors.size},${colors.head},${colors.tail}") colors(1) // List 下标从 0 开始 // 高阶函数 colors.foreach((c:String)=>println(c)) val numbers = colors.map((c:String)=> c.size)\ val total_str = colors.reduce((a:String,b:String)=> a+" "+b)
- filter:
-
Set
var myset = Set("hadoop","spark") // myset 可以指向不同的地址。Set 默认不可变 myset += "scala" // myset 指向另一个不可变 地址 import Scala.collection.mutable.Set // 可变集合 val myset1 = Set("hadoop","spark") myset1 += "cloud computing" // myset1 指向地址不变,Set 内容增加
-
Cons 操作符
-
映射(Map)
val uni = Map("xmu"->"xiamen uin","thu"->"Tsinghua uin") // 默认不可变 val xmu = if(uni.contains("xmu")) uni("xmu") else 0 println(xmu) import scala.collection.mutable.Map val uni = Map("xmu"->"xiamen uin","thu"->"Tsinghua uin") // 可变 uni("xmu") = "xiamen university" // update uni("fzu") = "fuzhou uni" uni += ("tju"->"tianjin uni") uni += ("tju"->"tianjin uni","whu"->"wuhan uni") for ((k,v) <- uni) printf("code is: %s and name is: %s\n",k,v) // 遍历 for (k <- uni.keys) println(k) for (k <- uni.values) println(v)
-
迭代器(Iterator):基本操作:
next(),hasNext
val iter = Iterator("hadoop","spark") while (iter.hasNext){println(iter.next())} for (ele <- iter) println(ele)
-
类
-
else
基本语法
写入文件:Scala 需要java.io.PrintWriter 实现把数据写入文件
import java.io.PrintWriter
val out = new PrintWriter("/usr/local/output.txt")
for (i <- 0.8f to 1.6f by 0.2f) out.println(i)
out.close()
读取文件:可以使用 Scala.io.Source 的 getLines 方法 实现对文件中所有行读取
import scala.io.Source
val inputFile = Source.fromFile("output.txt")
val lines = inputFile.getLines
for (line <- lines) println(line)
异常处理:Scala 将所有异常都当作 不受检异常,使用 try-catch 结构捕获异常
// 暂不能运行
import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
try {
val f = new FileReader("input.txt")
} catch {
case ex:FileNotFoundException => println("file not found")
case ex:IOException => println("IO error")
case _ => println("else error")
}
finally{
file.close()
}
类
class Counter{
private var value = 0 // 外部不可访问
def increment():Unit = {value += 1}
def current():Int = value
}
val mycounter = new Counter // 无参数可不加括号,下同
println(mycounter.current)
mycounter.increment
println(mycounter.current)
class Counter{
var value = 0 // 外部可访问
def increment(step:Int):Unit = {value += step} // 含参函数
def current():Int = value
}
object Mycounter{
def main(args:Array[String]){
val mycounter = new Counter // 无参数可不加括号,下同
println(mycounter.current)
mycounter.increment(5)
println(mycounter.current)
}
}
getter 与 setter 方法
class Counter{
private var privateValue = 0 // 外部可访问
def value = privateValue // 类似 Java 的 getter 方法
def value_= (newValue:Int){// 类似 Java 的 setter 方法
if (newValue > 0) privateValue = newValue
}
def increment(step:Int):Unit = {value += step} // 含参函数
def current():Int = value
}
object Mycounter{
def main(args:Array[String]){
val mycounter = new Counter
println(mycounter.value)
mycounter.value = 3
println(mycounter.value)
mycounter.increment(5)
println(mycounter.current)
}
}
辅助构造器
- scala 构造器包含一个主构造器和若干个(>=0)辅助构造器
- 辅助构造器名称为 this,每个辅助构造器都必须调用已经定义辅助构造器或主构造器
class Counter{ // 类 主构造器
private var value = 0 // 计数器起始值
private var name = "" // 计数器名称
private var mode = 1 // 计数器类型
def this(name:String){ // 第一个 辅助构造器,只能调用 主构造器
this() // 调用主构造器
this.name = name
}
def this(name:String,mode:Int){
this(name)
this.mode = mode
}
def increment(step:Int):Unit = {value += step} // 含参函数
def current():Int = value
def info():Unit = {printf("Name:%s and mode is %d\n",name,mode)}
}
object Mycounter{
def main(args:Array[String]){
val mycounter1 = new Counter // 主构造器
val mycounter2 = new Counter("Runner")
val mycounter3 = new Counter("Timer",2)
mycounter1.info
mycounter1.increment(1)
printf("current value is:%d\n",mycounter1.current)
mycounter2.info
mycounter2.increment(2)
printf("current value is:%d\n",mycounter2.current)
mycounter3.info
mycounter3.increment(3)
printf("current value is:%d\n",mycounter3.current)
}
}
class Counter(val name:String,val mode:Int){ // 类:主构造器
private var value = 0 // 计数器起始值
def increment(step:Int):Unit = {value += step} // 含参函数
def current():Int = value
def info():Unit = {printf("Name:%s and mode is %d\n",name,mode)}
}
单例对象 和 伴生对象
- 单例对象:对象定义不需要出现class,而是用 object。静态变量,每次调用都累加
- 伴生对象
- 同时包含实例方法 和 静态方法的类
- 当单例对象 与某类具有相同名称时,它被称为这个类的伴生对象
- 类及其伴生对象 必须存在于同一文件,可以相互访问
- 定义在伴生对象的方法都属于静态方法(static method)
class Person {
private val id = Person.newPersonId() // 调用伴生对象(静态 static方法)
private var name = ""
def this(name:String){
this()
this.name = name
}
def info(){printf("The id of %s is %d.\n",name,id)}
}
object Person { // 单例对象:类Person 的伴生对象
private var lastId = 0
private def newPersonId() = {
lastId += 1
lastId
}
def main(args:Array[String]){ // main 函数入口
val person1 = new Person("ziyu")
val person2 = new Person("minxing")
person1.info
person2.info
}
}
scalac hello.scala
scala -classpath . Person
javap Person // Java 反编译
继承
- 子类重写超类的抽象方法不需要使用 override 关键字
- 只有 主构造器 可以调用 超类中的 主构造器
- 可以重写超类中的字段
- 重写一个非抽象方法必须使用 override 修饰符
- Scala 和 Java 一样,不允许从多个超类中继承
- abstract 抽象类关键字,extends 继承关键字
abstract class Car{ // 抽象类:不能被直接实例化
val carBrand :String // 抽象字段:没初始化,必须声明类型
def info() // 抽象方法:没有定义任何方法的方法(空着)
def greeting() {println("welcome to my car!")} // 具体方法
}
class BMWCar extends Car{
override val carBrand = "BMW" // 重写超类字段,必须用 override
def info(){printf("This is a %s car.It is on sale.\n",carBrand)} // 重写超类抽象方法, override 可忽略
override def greeting(){println("welcome to my BMWCar car!")} // 重写超类具体方法, override 不可忽略
}
object Mycar{
def main(args:Array[String]){
val mycar = new BMWCar
mycar.greeting
mycar.info
}
}
特质(trait):继承多个父类
trait CarId{
var id:Int
def currentId():Int
}
trait CarGreeting{
def greeting(msg:String) {println(msg)}
}
class BMWCarId extends CarId with CarGreeting{ // 可以使用多个 with 混入多个特质
override var id:Int = 20000
def currentId():Int = {id += 1;id} // 重写超类抽象方法, override 可忽略
}
object Mycar{
def main(args:Array[String]){
val mycarid = new BMWCarId
mycarid.greeting("welcome my first car!")
printf("my first carid is %d\n",mycarid.currentId)
}
}
函数式编程
// 函数式编程实例 WordCount:对当前目录下文本文件进行词频统计
import java.io.File
import scala.io.Source
object WordCount{
def main(args:Array[String]){
val dirfile = new File("D:/documents/3_linzi_work/com_tools/bailing")
val files = dirfile.listFiles
for (file <- files) println(file)
val listFiles = files.toList
val wordsMap = scala.collection.mutable.Map[String,Int]() // 声明可变映射
listFiles.foreach(
file=>Source.fromFile(file).getLines().foreach(
line=>line.split(" ").foreach(
word=> {
if (wordsMap.contains(word)){
wordsMap(word) += 1
}
else{
wordsMap += (word->1)
}
}
)
)
)
println(wordsMap)
for ((k,v)<- wordsMap) println(k+":"+v)
}
}