<<Scala编程>>学习记录
记录阅读笔记,以备回顾学习
http://www.tutorialspoint.com/scala/scala_tuples.htm
word版本下载地址:点击打开链接
第2章_入门初探
1:val常量
2:var变量
3:变量定义
val msg3:String = "hello" 或者利用类型推断 val msg = hello
4:函数入门
备注: (1)返回值在递归函数中必须明确给出 (2)函数返回值:为最后一个表达式 (3)函数只有一句也可以不带括号
def max(x:Int, y:Int):Int{ if(x>y) x else y
}
def max(x:Int,y:Int) = if(x>y) x else y
5:while
var i =0while(i<args.length){ println(args(i)) i +=1}
6:foreach
args.foreach(arg=>println(arg))其中括号内部是函数字面量 完全形式为: args.foreach((arg:String)=>println(arg)) 更简单形式: args.foreach(println)
7:for
for(arg<-args) println(arg)
第3章_入门再探
[1]数组
可变的同类对象序列
1-数据定义
(1)数组定义
val arr: Array[String] =newArray[String](3)
通过类型推断简写为: val arr =newArray[String](3)
(2)伴生对象apply工厂相当于静态方法构造ARRAY
val number0 = Array.apply("zero","one")
简化为 val number1 = Array("one","two")
(3)通过圆括号,赋值访问,数组也是类实例,通过()---> 传递给update()
arr(0) = "Hello"
arr(1) =","
arr(2) ="world"
arr.update(0,"Hello")
arr.update(1, ",")
arr.update(2,"world")
(4)访问()--->传递给apply()
arr(0) -->apply(0)
[2]List
方法没有副作用是函数式,计算并返回值应该是唯一目的
List不同于java,是不可变同类序列
前缀拼接:: , 列表拼接:::
val List1 = List(1, 2)
val List2 = List(3,4)
val list_0 = 1 ::2 :: Nil
val ListCons = List1 ::: List2
//前缀
val ListCons_1 = 1 ::List1
相当于: List1.::(1)
不支持append,用::和reverse代替
//后缀
val list_2 = (3::list_0.reverse).reverse
方法名 |
作用 |
List() 或 Nil |
空List |
List(“Cool”,“tool”) |
创建有2个值得新List[String](2) |
val thirll = "cool"::"tool"::Nil |
创建有2个值得新List[String] |
List("a","b"):::List("c","d") |
叠加2个列表,返回一个新列表 |
thirll.count(s=>s.length==4) |
计算长度为4的string元素的个数 |
thirll.drop(1) |
返回去掉前一个元素的thirll列表List(“tool”) |
thirll.dropRight(1) |
返回去掉后一个元素的thirll列表 |
thirll.exists(s=>s=="cool") |
是否存在值为"cool"的元素,true |
thirll.filter(s=>s.length == 4) |
过滤出元素长度为4的元素,返回组成的新列表 |
thirll.forall(s=>s.endsWith("l")) |
判断thirll中所有元素是否都以"l"结尾 |
thirll.foreach(s=>print(s)) thirll.foreach(print) |
对每个元素执行字面函数
备注:返回值为Util
|
thirll.head |
第一个元素 |
thirll.init |
除最后一个元素外的其他元素组成的新列表 |
thirll.isEmpty |
是否为空 |
thirll.last |
最后一个元素 |
thirll.tail |
除第一个元素外的其他元素组成的列表 |
thirll.map(s=>s+"y") |
对每一个元素按字面函数操作
res4: List[String] = List(cooly, tooly)
|
thirll.mkString(", ") |
按指定分割符,返回元素组成的新列表
res6: String = cool, tool
|
thirll.reverse |
翻转 |
thirll.sortWith((s,v)=>s.charAt(0)>v.charAt(0)) |
按照第一个字母的顺序降序排列 |
|
|
[3]Tuple
l 不可变,非同类,类型取决于元素个数与类型
l 作为函数返回值,返回多个值
l ._序号 ,访问元素
val tuple = (1,"hello",List(2,3,4)) tuple._1 tuple._2 tuple._3 |
tuple: (Int, String, List[Int]) = (1,hello,List(2, 3, 4)) //类型取决于元素个数与类型
res0: Int = 1
res1: String = hello
res2: List[Int] = List(2, 3, 4)
|
http://www.tutorialspoint.com/scala/scala_tuples.htm
Iterate over the Tuple
You can use Tuple.productIterator() method to iterate over all the elements of a Tuple.
Try the following example program to iterate over tuples.
Example
object Demo {
def main(args: Array[String]) {
val t = (4,3,2,1)
t.productIterator.foreach{
i =>println("Value = " + i )
}
}
}
|
[4]set与HashSet
set特质分为可变与不可变,分别存于不同包中
scala.collection.immutable.Set (set) ß(继承) scala.collection.immutable.Set (HashSet)
scala.collection.mmutable.Set (set) ß(继承) scala.collection.mmutable.Set (HashSet)
import scala.collection.mutable.Set val set = Set("h","i") set += "j" // 对于mutable.Set 才是真正的 += 而对于immutable 是返回了新值 |
[5]Map与HashMap
特质分为可变与不可变,分别存于不同包中
scala.collection.immutable.Map (Map) ß(继承) scala.collection.immutable.HashMap (HashMap)
scala.collection.mmutable.Map (Map) ß(继承) scala.collection.mmutable. HashMap (HashMap)
import scala.collection.mutable.Map val map = Map[Int,String]() map += (1->"h") map +=(2->"j") map(2) |
import scala.collection.mutable.Map
map: scala.collection.mutable.Map[Int,String] = Map()
res0: scala.collection.mutable.Map[Int,String] = Map(1 -> h)
res1: scala.collection.mutable.Map[Int,String] = Map(2 -> j, 1 -> h)
res2: String = j
|
[6]函数式编程风格转换
l 函数式编程少用var,崇尚val
l 不可变对象
l 没有副作用的方法
* 判断有无副作用,看返回值为Unit,即没有返回任何有用的值,唯一表现只能通过副作用
* 减少副作用,有利于测试
object VarAndVal { val args: Array[String] //使用var,有副作用(输出控制台) def printArgs(args: Array[String]): Unit = { var i = 0 while (i < args.length) { println(args(i)) i += 1 } } //去掉var,有副作用(输出控制台) def printArgs_2(args: Array[String]): Unit = { for (arg <- args) println(arg) }
////去掉var,有副作用(输出控制台) def printArgs_2(args: Array[String]): Unit = { args.foreach(println) }
//无副作用 def formatArgs(args: Array[String]) = args.mkString("\n") println(formatArgs(args)) //有利于测试 assert(formatArgs(Array("h","i","j")) =="h\ni\nj\n") }
|
[7]从文件中读取文本
package com.scalaPrograming.chapter2 import scala.io.Source object FileOps { //行长度数值的宽度如"28" 宽度= 2 ; "1" 宽度 = 1 def widthOfLength(s:String) = s.length.toString.length def longestLineFun(ls : List[String]) : String = { ls.reduceLeft( (a,b) => if(a.length > b.length) a else b ) } def main(args: Array[String]) { //toList 因为 getLines 返回枚举器Iterator[String],一旦遍历完成就失效了 val lines = Source.fromFile("./src/com/scalaPrograming/chapter2/FileOps.scala").getLines().toList
val longestLine = longestLineFun(lines)
val maxlength = widthOfLength(longestLine) for(line<- lines){ val numSpace = maxlength - widthOfLength(line) val pading = " " * numSpace println(pading + line.length + "|" + line) } } } |
第4章_类和对象
[1]类和方法简介
l 类中成员默认public
l 函数特点
函数的参数是val的
返回最后一个表达式的值
只有一句可省去 = {} 简化为一行
省略 = 号,返回值强制转化为Unit
对于副作用函数可以用省略= 号的方式
package com.scalaPrograming.chapter4 class classLearn { //默认为public var num_1:Int =0
private var num_2:Int =1
def getNum_2():Int = { return num_2 }
//==========函数特点================ // 函数的参数是val的 // 返回最后一个表达式的值 //只有一句可省去 = {} 简化为一行 //省略 = 号,返回值强制转化为Unit //对于副作用函数可以用省略 =号的方式 def setNum_2(value:Int) = { // value = 2 函数的参数是val的 this.num_2= value this.num_2+1//返回值= value + 1 ,返回最后一个表达式的值 }
def add1(value :Int): Unit= { this.num_2+ value } //简化为一行 def add2(value :Int) =this.num_2+ value //对于副作用函数(返回值为Unit)这里只为了num_2加value,可省略 = 号 def add3(value :Int) {this.num_2+= value}
//省略 =号,返回值强制转化为Unit def tostring() {"h"} }
object classLearn_1{ //这里不是伴生对象,伴生对象可以访问对象值中所有 def main(args: Array[String]) { val cls = newclassLearn() println(cls.num_1)
//不可访问 //Error:(19, 17) variable num_2 in class classLearn cannot be accessed in com.scalaPrograming.chapter4.classLearn //println(cls.num_2)
// println(cls.num_2)
println(cls.getNum_2) println(cls.setNum_2(3)) println(cls.getNum_2) println(cls.tostring()) //输出: // 0 // 1 // 4 // 3 // () } } |
[2]单例对象
[1]作为伴生对象
1. 同类相互访问私有成员
2. 对应apply方法使得类可以省略new 操作符使用
3. 必须和类在同一个源文件
package com.scalaPrograming.chapter4 class Singleton { private var a:Int =0
def setValue():Int = { //相互访问私有成员 this.a = Singleton.init this.a } }
object Singleton {
private val init:Int =1
def apply():Singleton = { new Singleton } def main(args: Array[String]) { //在伴生对象中定义了apply方法可以,val sgl = Singleton()省略new val sgl = Singleton() //伴生对象中可以访问类的私有成员 println(sgl.a) println(sgl.setValue()) //结果: // 0 // 1 }
} |
[2]单例对象与类的区别
l 单例对象不带参数
l 第一次访问时才被初始化
l 不与伴生类共享名称的单例对象为独立对象,可以作为相关功能方法的工具类或定义scala应用的接口
第5章_基本类型和操作
[1]基本类型
字符串:string
整数类型:Int、Short、Byte、Char、Boolean
数类型:Float、Doubel
类型 |
包 |
位数 |
数值范围 |
Byte |
scala |
8 |
有符号补码整数(-2^7至-2^7-1) |
Short |
16 |
有符号补码整数(-2^15至-2^15-1) |
Int |
32 |
有符号补码整数 |
Long |
64 |
有符号补码整数 |
Char |
16 |
无符号Unicode字符(0至2^16-1) |
Float |
32 |
IEEE754单精度浮点数 |
Double |
64 |
IEEE754单精度浮点数 |
Boolean |
|
true或false |
String |
java.lang |
|
Char序列 |
[2]字面常量
整数:十进制、十六进制(0X)、八进制(0)
浮点数:1.23、1.23e1=12.3(10为底的幂级数)、1.23F(f)
字符:‘A’
字符串:“hello”
符号字面量:’<符号> 符号可为:字母或数字
[3]任何方法都可以是操作符
[1]方法调用后的括号
对于方法调用后边括号,通常带有副作用的加括号:println()
无副作用,去掉括号:s.toLowerCase
[4]数学运算
+ - * / %
[5]关系|逻辑|位操作
关系操作符 |
> |
< |
>= |
<= |
! |
逻辑 |
&& |
|| |
|
|
|
位 |
& |
| |
^ |
~ |
<< |
>> |
>>>(无符号) |
[6]对象相等
[1]== 和 !=
按内容比较、可以比较不同对象(实际调用左操作数的equals)
[2]和java中一样比较对象的 == 是用eq和 ne
[7]富包装器
0 max 5
0 min 5
-2.7 abs
-2.7 round
4 to 6
“bob” capitalize -> Bob
“robert” drop 2 ->bert
|
第6章_函数式对象
函数式对象:不具有任何可改变状态的对象的类
1:类参数
2:构造函数
3:方法
4:操作符
5:私有成员
6:子类方法重载
7:先决条件检查
8:同类方法重载和自指向
以有理数为例:
[1]创建Rational
[1]类初识
class Rational(n:Int,d:Int)
n,d :为类参数(分子和分母),类可以直接带参数与java不同
主构造器:编译器会自动创建带同样参数的主构造器
[2]主构造器
1: 类的参数
2:类中既不是参数也是不方法的部分
class MyRational (n:Int, d:Int){ println(n+"/"+d)//在主构造器中,每次新建类都会打印信息 } object MyRational { def main(args: Array[String]) { val r1 = newMyRational(1,2) val r2 = newMyRational(2,3) } } |
[3]重写toString方法
class MyRational (n:Int, d:Int){ override def toString = n + "/" + d } object MyRational { def main(args: Array[String]) { val r1 = newMyRational(1,2) print(r1) } } |
[4]检查先决条件
require(true)
require(false) 会抛出IllegalArgumentException
class MyRational (n:Int, d:Int){ require(d != 0) override def toString = n + "/" + d } |
[5]添加字段承接类参数
[0]类的列表为类私有列表
以便类外使用,类的列表为类私有列表
that.d和that.n提示找不到
class MyRational (n:Int, d:Int){ // 条件判断 require(d != 0) def add(that: Rational): Rational = new Rational(n * that.d + that.n * d,d * that.d) } |
[1]方法1_添加字段承接类参数
class MyRational (n:Int, d:Int){ // 条件判断 require(d != 0) // 构造列表的量,只对对象本省可见,所以这里转接,public valnumber: Int = n val denom: Int = d def add(that: Rational): Rational = new Rational(number*that.denom+that.number*denom,denom*that.denom) } |
[2]方法2_定义参数+赋值
class MyRational (val n:Int, val d:Int){ // 条件判断 require(d != 0) def add(that: Rational): Rational = new Rational(n * that.d + that.n * d,d * that.d) } |
[6]辅助构造器
1:5/1 不写成MyRational(5,1) 而是写成MyRational(5)
2:辅助构造器都是从this(…)开始,调用其他辅助构造器或主构造器,最终都会调用主构造器
class MyRational (n:Int, d:Int){ // 条件判断 require(d != 0) // 构造列表的量,只对对象本省可见,所以这里转接 val number: Int = n val denom: Int = d def this(n:Int) = this(n,1) //辅助构造器 def add(that: Rational): Rational = new Rational( number * that.denom+ that.number*denom, denom * that.denom ) } |
[7]私有成员和方法
[1]class 成员默认是public
2/4 --> 1/2 , 除掉最大公约数
class MyRational (n:Int, d:Int){ // 条件判断 require(d != 0) private val g= gcd(n.abs, d.abs) //私有成员 // 构造列表的量,只对对象本省可见,所以这里转接 val number: Int = n/g val denom: Int = d/g def this(n:Int) = this(n,1)//辅助构造器 def add(that: Rational): Rational = new Rational( number * that.denom+ that.number*denom, denom * that.denom ) private def gcd(a:Int, b:Int):Int={ //私有方法 if(b == 0) a else gcd(b,a%b) } } |
[8]定义操作符
[1]定义操作符: + - * /
class MyRational (n:Int, d:Int){ // 条件判断 require(d != 0)
private val g= gcd(n.abs, d.abs)
// 构造列表的量,只对对象本省可见,所以这里转接 val number: Int = n val denom: Int = d
def this(n:Int) = this(n,1)//辅助构造器
def add(that: Rational): Rational = new Rational( number * that.denom+ that.number*denom, denom * that.denom )
//定义操作符 def +(that: Rational): Rational =newRational( number * that.denom+ that.number*denom, denom * that.denom ) def -(that: Rational): Rational =newRational( number * that.denom- that.number*denom, denom * that.denom ) def *(that: Rational): Rational =newRational( number * that.number,denom* that.denom )
def /(that: Rational): Rational =newRational( number * that.denom,denom* that.number ) private def gcd(a:Int, b:Int):Int={ if(b == 0) a else gcd(b,a%b) } } |
[7]标识符
[1]字母数字下划线
1:不建议用下划线
2:和java一样
类:ClassName
方法和变量:funOne() ,varOne
3:常量:不同于java,第一个字母大写的驼峰
4:字面量标识符`…`:`x` ,`yeild`
[8]方法重载和隐式转换
描述:
1:有理数r*2 只能写成 r * new Rational(2),重载方法* 为只带有一个Int参数的形式
2:左操作数为整数时,报错;这里用隐式转换在必要的时候将整数转换为有理数;
3:隐式转换 用implicit 告诉编译器在一些情况下自动调用,隐式转化只能定义在作用范围,如果把隐式转化定在类中它不会在解释器的作用范围中起作用。这里我们定义在object中随main方法在同一作用域
class Rational(n: Int, d: Int) { // 条件判断 require(d != 0)
// 私有成员 private val g= gcd(n.abs, d.abs)//可以质数化 2/4 = 1/2
// 构造列表的量,只对对象本省可见,所以这里转接 val number: Int = n /g val denom: Int = d /g
def this(n: Int) =this(n,1)//辅助构造函数,处理 n/1这种情况
override def toString =if(denom==1)""+numberelsenumber+"/"+denom
def add(that: Rational): Rational =newRational( number * that.denom+ that.number*denom, denom * that.denom ) //定义操作符 def +(that: Rational): Rational =newRational( number * that.denom+ that.number*denom, denom * that.denom ) def -(that: Rational): Rational =newRational( number * that.denom- that.number*denom, denom * that.denom )
def *(that: Rational): Rational =newRational( number * that.number,denom* that.denom )
def /(that: Rational): Rational =newRational( number * that.denom,denom* that.number ) //----方法的重载 def +(i: Int): Rational = new Rational(number+ i *number,denom) def -(i: Int): Rational = new Rational(number- i *denom,denom) def *(i: Int): Rational = new Rational(number* i,denom) def /(i: Int): Rational = new Rational(number,denom* i)
//私有方法,注意递归函数必须有明确的类型 private def gcd(a: Int, b: Int): Int = { if (b == 0) a else gcd(b, a % b) } }
object RationalTest { /* 隐式转换,定义在作用范围内 */ implicit defintToRational (x: Int) = new Rational (x) def main(args: Array[String]) { val test_0 = newRational(3) val test_1 = newRational(2,3) println("test_0 = " + test_0 +"\tand\t"+"test_1 = " + test_1) val test_2 = newRational(2,4) println("test_2 = " + test_2) val test_3 = test_0.add(test_1) println("test_3 = " + test_3) val test_4 = test_0 + test_1 println("test_4 = " + test_4) val test_5 = test_0 * test_1 println("test_5 = " + test_5) val test_6 = 2 * test_1 println("test_6 = " + test_6) } } |
第7章_内建控制结构
1:内建控制结构有限:if\while\for\try\match\函数调用
2:原因:语法层面上支持函数字面量,控制结构在函数库中实现
3:几乎所有内建控制结构都会产生值,利用这一特性可以缩短代码
[1]IF
[1]利用控制本身返回值_缩短代码_最重要是引入val 而非var
var filename = "default.txt" if(!args.isEmpty) filename = args(0) //利用控制本身返回值,缩短代码,最重要是引入val而非var val filename2 = if(!args.isEmpty) args(0) else "default" |
[2]While循环
称为while循环是因为:返回值为Unit 、()
很多函数式语言弃之,scala保留,实际多用迭代代替;不得已不用while
[1]while
//--------while def gcbLoop(x: Long, y: Long): Long = { var a = x var b = y while(a != 0){ val temp = a a = b % a b = temp } b } |
[2]do-while
// var line = ""//这里初识化,有了类型 do{ line = readLine() println("Read: " + line) }while(line != " ") |
[3]多用迭代代替
//-----------上面只是为了用while实际中 def gcb(x:Long, y:Long):Long = { if(y == 0) x else gcb(y,x%y) } |
[3]表达式的Uint陷阱_表达式有值
var 再赋值等式本身也是unit 一切表达是都有值
//------注意对var再赋值等式本身也是unit 一切表达是都有值 //------ unit != " " 导致死循环 var line_2 = "" while ((line_2 = readLine()) != " "){ println(line_2) } |
[3]For
[1]枚举集合类
[1]数组
1:建立指向当前目录的对象File,调用listFiles()返回Array[File], println(file),调用File的toString方法打印文件名
2:发生器语法:file <- fileHere
//建立指向当前目录的对象File,调用listFiles()返回Array[File], println(file),调用File的toString方法打印文件名 val fileHere = (newFile(".")).listFiles() for (file <- fileHere) // for加发生器语法 println(file) |
[2]Range_to_until
//to for (i <- 1 to 4) // 1,2,3,4 print(i) //until for (i <- 1 until 4) // 1,2,3, print(i) |
[3]不常用
for(a<-array.lenght-1)
[2]过滤_For中的If守卫
//for中的 if过滤 val filesHere = (newFile(".")).listFiles() for (file <- filesHere iffile.getName.endsWith(".iml")) println(file)
//多个过滤器用";"隔开,打印是文件目录 for ( file <- filesHere if file.isDirectory; if file.getName.endsWith(".git") ) println(file)
|
[3]多个发生器_嵌套枚举
//嵌套枚举,加入多个<- def fileLines(file: java.io.File) = scala.io.Source.fromFile(file).getLines().toList def grep(pattern: String) = for ( file1<- (new File(".")).listFiles() if file1.getName.endsWith(".scala") line_1 <- fileLines(file1) if line_1.trim.match(pattern) )println(file1 + ": " + line_1.trim) |
[4]流间Val变量绑定
//流间变量 def grep(pattern:String) = for ( file<- (new File(".")).listFiles() if file.getName.endsWith(".scala") line0 <- fileLines(file) trimmed = line.trim //相当于val temp:String if line0.trim.match(pattern) )println(file + ": " + trimmed) |
[5]制造新集合for_yield_[二次处理]
产生一个被迭代项的新集合,这里产生Array[Int],类型同arr
//制造新集合 val arr = Array(1,2,3,4) val a = for { i <- arr if i % 2 == 0 } yield i + 100 // 还可以二次处理
//Array(102 , 104)
|
[4]Try处理异常
[1] Try的返回值
else 部分返回值为Nothing
整个if 的类型为最终执行的分支
valhalf=if(n%2==0)n/2elsethrow new RuntimeException("n must be even") |
try{ val file = newFileReader("input.txt") }catch{ case ex: FileNotFoundException=>println("处理丢失文件") case ex: IOException => println("处理其他I/O") } |
try{ // 使用文件 } finally{ //关闭文件 } |
[2]finally返回值
java中finally 的返回值凌驾于之前try和catch,而scala不会
java返回2
def f():Int = try{return1}finally{return2}
scala返回1
def f():Int = try{1} finally {2 }
[5]Match匹配表达式
相当于switch,break是隐含的
// 有返回值值
val val_1 = "chips" val friend = val_1 match{ case "salt"=>"salt" case "chips"=>"chips" case _ => "huh?" }
|
[6]无break和continue_可用标识量代替
[7]小结_重构指令式风格
注意改变编码风格
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100
|
object MultiTab { def main(args: Array[String]) { //以序列形式返回一行乘法表 def makeRowSeq(row:Int) = for(col<-1to10)yield{ val prod = (row*col).toString val padding = " "*(4-prod.length) padding + prod } //以字符串形式返回一行乘法表 def makeRow(row:Int) = makeRowSeq(row).mkString //字符串形式返回一行乘法表 def mutiTbale() = { val tableSeq = for (row <- 1 to 10) yield makeRow(row) tableSeq.mkString("\n") } print(mutiTbale()) } } |
第8章_函数和闭包
[1]本地函数
java私有方法,在scala中通过将函数定义在其他函数中实现
def loaclFun(input:Int): Int = { val v = input * 2 //本地函数可以用外部函数的成员如input def localFunction(num: Int): Int ={ num + input } localFunction(v) } |
[2]头等函数(first-class Function)
函数不仅可以定义、调用、匿名字面量亦可作为值传递
函数字面量:存在于源代码
函数值:作为对象存在于运行期
object FirstClassFun { def main(args: Array[String]) { //函数值 =函数字面量 var funVar = (x: Int) => x + 1 val num = funVar(10) println(num) println("---------")
//函数字面量应用举例 foreach filter val List_1 = List(1, 2, 3, 4) List_1.foreach((x: Int) => { x * 10 println(x) } ) println("---------") println(List_1.filter((x:Int) => x % 2 == 0 )) //List(2, 4),注意返回值类型 } |
[3]函数字面量的短格式
得益于类型推断,函数字面量可以简化
例如:
List_1.filter((x:Int) => x % 2 ==0))
List_1.filter((x) => x % 2 ==0))
List_1.filter(x => x % 2 ==0))
[4]占位符语法
1:当每个参数在函数字面量中仅出现一次时,可用下划线作为一个或多个参数,甚至整个参数列表的占位符,函数每次调用时将参数填入
2:当用占位符,不能判断类型时可以,: xx 明确类型
//占位符,当做一个或更多的占位符只要每个参数在函数字面量内仅出现一次 val list = List(1, -2,0,4) list.filter(_ > 0) //当用占位符,不能判断类型时可以,: xx明确类型 val f = (_: Int) + (_: Int) val sum_1 = f(5,10) println(sum_1) println("---------") |
[5]部分应用函数
//部分应用函数 //用占位符待整个函数列表 list.foreach(print _) println("\n---------")
def sum(a: Int, b: Int, c: Int) = a + b + c val fun_2 = sum _ val fun_3 = sum(1, _: Int,4)
println(fun_2(1, 2, 4)) println(fun_3(2))
|
[6]闭包
1:闭包:函数依赖定义在其他地方的变量,在创建时捕获此时活跃的这个变量
注意是变量,而不仅是变量值
val addMore = (x: Int) => x + more
x是绑定项,more为*项
//闭包,函数依赖定义在其他地方的变量,在创建时捕获此时活跃的这个变量 def main(args: Array[String]) { //方式一 val more = 1 val addMore = (x: Int) => x + more println(addMore(2))
//方式二 def addMore_2(more : Int) = (x: Int) => x + more
//方式二-调用方式-1 addMore_2(1)(2)
//方式二-调用方式-2 val fun_addMore_2 = addMore_2(1)// more = 1 val fun_addMore_3 = addMore_2(2)// more = 2 println("结果: "+ fun_addMore_2(1) +"--"+ fun_addMore_3(1)) def Fun(in:Int) = in + 1 + (_ : Int) val FunVar = Fun _ println( FunVar(1)(2) ) } |
[7]可变长参数
1:可变长参数格式: var*
2:实际上是存成一个Array[具体类型]
3:注意数组或集合不能多做多参数,要通过特殊符号转化 [: _*]
object MoreArgs { def main(args: Array[String]) { //可变参数-最后一个加* def echo(args: String*) = { for (arg <- args) println(arg) } echo("hello", "world!") //注意数组或集合不能多做多参数,要通过特殊符号转化 val arr = Array("h","j","w") val arr_2 = List("h","j","w") echo(arr: _*) echo(arr_2: _*) } } |
[8]尾递归
待续
第9章_控制抽象
前面说明了控制结构少,但提供了创建自定义控制抽象的能力
本章展示:把函数应用到创建新的控制抽象、柯里化、传名参数
[1]函数值参数_减少代码重复
函数分为:通用部分、非通用部分(由参数提供)
函数值作为参数的高阶函数,简化代码
scala不支持函数名作为参数
这里有个例子,编写文件浏览器(根据文件名过滤)
[1]特殊结尾的文件
import java.io.File object FileMatcher { private def fileHere = { new File(".").listFiles } def fileEnding(query: String) = for (file <-fileHere;iffile.getName.endsWith(query)) yield file } |
[2]包含某字符串的文件
import java.io.File object FileContains { private def fileHere = { new File(".").listFiles } def fileEnding(query:String) = for (file <- fileHere;iffile.getName.contains(query)) yield file } |
[3]匹配某字符串的文件
import java.io.File object FileMatches{ private def fileHere = { new File(".").listFiles } def fileEnding(query: String) = for (file <-fileHere;iffile.getName.matches(query)) yield file } |
[4]函数值参数优化
这里用到:函数为头等值、函数字面量、闭包
def Fun(in:Int) = in + 1 + (_ :Int)
val FunVar = Fun _
println( FunVar(1)(2) )
arg为参数,query为*待闭包量
object HigherOrderFun { private val list=List("hj.scala","j.java","w.md") private def my_match(matcher:String=> Boolean) = { for(arg<-list;ifmatcher(arg))//arg为String yield arg } def fileEnding(query:String) = {//为*量query,待闭包 my_match(_.endsWith(query)) } def fileContains(query:String)= { my_match(_.contains(query)) } def fileMatch(query:String)= { my_match(_.matches(query)) } def main(args: Array[String]) { println(fileEnding(".scala")) println(fileContains("j")) println(fileMatch("w.md")) } } |
[2]API里的高阶函数_简化代码
http://www.scala-lang.org/api/2.12.0-RC1/
API中有很多循环方法,应用到可简化代码
object API { def containOdd(list:List[Int]) :Boolean ={ var exists = false for(num <- list if !exists) if(num % 2 ==1) exists = true exists } def APIContainOdd(list:List[Int]) :Boolean = list.exists(_%2==1) def main(args: Array[String]) { println(containOdd(List(2,3,4))) println(containOdd(List(2,6,4)))
println( APIContainOdd(List(2,3,4)) ) println( APIContainOdd(List(2,6,4)) ) } } |
[3]柯里化(curing)
柯里化的函数被应用于多个参数列表
例子:
def add(x:Int,y:Int) :Int = { x+y } |
应用柯里化:
object Curing { //柯里应用多个参数列表 def curriedSum(x: Int)(y: Int) = { x + y } def main(args: Array[String]) { val s1 = curriedSum(1)(2) //柯里化调用 println(s1) //返回函数值 val fun1 =curriedSum(1)_ //返回第2个函数的函数值 val s2 = fun1(2) //调用第2个函数 println(s2) } } |
[4]编写新的控制结构
1:创建新的控制结构= 创建带有函数做参数的方法
2:当函数只有一个参数时可以将小括号写成花花括号,看起来更像控制结构
3:当发现代码中多个地方有重复的控制模式时,应该考虑将其实现为新的控制结构
例1:双倍控制结构
object ControlStructure { // 1:创建新的控制结构=创建带有函数做参数的方法 // 2:单函数只有一个参数时可以将小括号写成花花括号,看起来更像控制结构 // 3:当发现代码中多个地方有重复的控制模式时,应该考虑将其实现为新的控制结构 //例1:双倍控制结构,将参数x按照op函数字面量操作两次 def twice(op: Double => Double, x: Double) = op(op(x)) def main(args: Array[String]) { val num = twice(_ +1,5) println(num) } } |
当发现代码中多个地方有重复的控制模式时,应该考虑将其实现为新的控制结构,例如:打开一个资源,对他进行操作,然后关闭资源,可以写成如下方式作为控制结构使用
由withPrintWriter而不是客户端程序确认文件是否关闭;
借贷模式:控制抽象函数将资源借贷给op函数,最后由控制抽象函数负责收尾工作
//例2:打开一个资源,对他进行操作,然后关闭资源,可以写成如下方式作为控制结构使用 def withPrintWriter(file:File, op:PrintWriter => Unit): Unit ={ val writer = newPrintWriter(file) try{ op(writer) }finally { writer.close() } } //调用 withPrintWriter( new File("date.txt"), writer => writer.println(new Date()) ) |
当只有一个参数时,可将小括号写成花括号,看上去更像控制抽象:
print(“hello”) ==> print{“hello”}
或者通过柯里化将必要的参数写成花括号,类似于if控制抽象了:
def withPrintWriter(file:File)( op:PrintWriter => Unit):Unit ={ val writer = newPrintWriter(file) try{ op(writer) }finally { writer.close() } } //调用类似于if控制抽象了 withPrintWriter(new File("date.txt")) {
writer => writer.println(new Date())
}
|
[5]传名参数(by-name parameter)
=> xxx
withPrintWriter(file) {
writer => writer.println(new Date())
}
中与if、while区别,参数已=>显示
如果定义一个参数字面量的myAssert,则为:
var assertionsEnable=true def myAssert(predicte: ( )=> Boolean)= { if(assertionsEnable&& !predicte()) throw new AssertionError } myAssert(()=>5>3) |
定义传名参数:
def myAssert(predicte: => Boolean)= { if(assertionsEnable&& !predicte()) throw new AssertionError } myAssert( 5>3) |
第10章_组合和继承
组合、继承、抽象类、无参数方法、扩展类、重写方法和字段、参数化字段、调用超类构造器、多态和动态绑定、final成员和类、工厂对象和方法
[1]引子_二维布局库实例
建立二维布局元素的库为本章实例,每个元素将显示一个由文字填充的矩形
提供一个elem工厂方法通过传入的数据构造新元素
elem(s:String):Element
还有拼接操作如above或beside
val column1= elem("hello") above elem("***")
val column2= elem("***") above elem("world")
column1 besidecolumn2
hello ***
*** world
|
[2]抽象类和抽象方法
1:抽象类: abstract class Element …
2:抽象方法:只要无实现就是抽象方法,不用显示abstract修饰(不同于java)
定义布局元素类型,以字符数组代表一行,代表二维的字符矩形:
abstract class Element{ def contents:Array[String] } |
[3]定义无参数方法
abstract class Element{ //抽象方法,无参数的情况 def contents: Array[String] def height: Int = contents.length //无参数方法的调用 = contents().length def width: Int = if(height ==0)0elsecontents(0).length // 当高为0时,长度为0,否则返回第1行的长度 } |
无参方法:只要方法中无参数 && 方法不能改变可变状态(方法仅能通过读取所包含对象的属性访问可变状态);客户端代码不受属性由方法实现OR 字段实现影响,可以直接将height\ width 改为字段 val。
abstract class Element{ //抽象方法,无参数的情况 def contents: Array[String] val height: Int = contents.length //无参数方法的调用 = contents().length valwidth: Int =if(height ==0)0elsecontents(0).length // 当高为0时,长度为0,否则返回第1行的长度 } |
字段实现与方法实现比较:
(1)字段比方法快,字段在类初识化时预计算,方法在每次调用时计算
(2)字段在每个对象中占用内存
当不改变对象、无副作用时推荐省略括号(虽然对于空括号方法均可省略括号)
“hello”.lenght//无副作用
println()//最好不省略()
[4]继承
extends
1:任何类的基类为scala.AnyRef
2:私有成员不继承
3:成员名称和参数相同时为重写
4:泛型同样支持
//子类 class ArrayElement(conts:Array[String])extendsElement{ def contents: Array[String] = conts //重写方法 }
//泛型
vale:Element=newArrayElement(Array(“hello“) )
|
[5]重写字段和方法
scala中只有两个命名空间:
1:值(字段、方法、包、单例对象)
2:类型(类和特质名)
3:由于字段、方法在同一命名空间,字段可以重写无参方法
4:字段、方法不能同名
class ArrayElement(conts:Array[String])extendsElement{ valcontents: Array[String] = conts //重写方法 } |
[6]定义参数化字段
参数化字段:实在类列表中,将成员定义和参数合并,参数化字段可以在类外部访问(对于public),同时参数化字段可以有用修饰方符修饰(访问权限修饰和override)
class ArrayElement(conts:Array[String])extendsElement{ valcontents: Array[String] = conts //重写方法 } |
class ArrayElement(val contents:Array[String])extendsElement{ } |
可以同时添加修饰符 |
Class Cat{
val dangerous = false
}
|
Class Tiger(
override val dangerous :Boolean,
private var age:Int
)extends Cat
|
[7]调用超类的构造器与override修饰符
1:调用超类的构造器,只需要在超类圆括号内传值,实际类的加参数的圆括号就相当于构造器也称为主构造器
2:若子类重写了父类的具体成员必须带override,实现同名抽象成员可以不带
abstract class Element{ //抽象方法,无参数的情况 def contents: Array[String] def height: Int = contents.length def width: Int = if(height ==0)0elsecontents(0).length } |
class ArrayElement(contents:Array[String])extendsElement{ def contents: Array[String] = conts } |
class LineElement(s:String)extendsArrayElement (Array(s)) { override def width = s.length override def height = 1 } |
[8]支持多态和动态绑定
[9]final修饰词
修饰成员:不可以被重写
修饰类:不能被继承
[10]实现above和beside
[11]工厂方法
实现工厂方法,而不是直接new构造对象,直接在伴生对象中构建工厂方法,唯一暴露给用户的是类/对象的组合隐藏子类的实现(子类也在伴生对象中实现并私有化)
这里默认this和that对象等长等高形式的实现above和beside
object MyElement { //====单例对象张私有化子类,提供工厂方法来对外提供服务 private class ArrayElement(conts:Array[String])extendsMyElement{ def contents: Array[String] = conts } private class UniformElement( ch:Char, override val width: Int, override val height: Int )extends MyElement{ private val line= ch.toString*width def contents = Array.make(height,line) } private class LineElement(s:String)extendsMyElement{ val contents=Array(s) override def width = s.length override def height = 1 } //====工厂方法 def elem(contents:Array[String]):MyElement =newArrayElement(contents) def elem(chr :Char,width:Int, height:Int):MyElement =newUniformElement(chr,width,height) def elem(line:String):MyElement =newLineElement(line) } abstract class MyElement{ //抽象方法,无参数的情况 def contents: Array[String] def height: Int = contents.length def width: Int = if(height ==0)0elsecontents(0).length def above(that:MyElement):MyElement = MyElement.elem(this.contents ++ that.contents) def beside(that:MyElement):MyElement = MyElement.elem( for( (line1,line2)<-this.contents zip that.contents )yield line1+line2 ) override def toString = contents mkString "\n" } |
[12]变宽边长兼容处理
兼容不等长对象的above操作和不等宽对象的beside操作
私有方法widen返回指定宽度的Element(左右侧留空格)
私有方法heighten竖直方向执行同样的操作
package com.scalaPrograming.MyElement object MyElement { //====单例对象张私有化子类,提供工厂方法来对外提供服务 private class ArrayElement(conts:Array[String])extendsMyElement{ def contents: Array[String] = conts } private class UniformElement( ch:Char, override val width: Int, override val height: Int )extends MyElement{ private val line= ch.toString*width def contents = Array(line) }
private class LineElement(s:String)extendsMyElement{ val contents=Array(s) override def width = s.length override def height = 1 } //====工厂方法 def elem(contents:Array[String]):MyElement =newArrayElement(contents)
def elem(chr :Char,width:Int, height:Int):MyElement =newUniformElement(chr,width,height)
def elem(line:String):MyElement =newLineElement(line) } abstract class MyElement{ //抽象方法,无参数的情况 def contents: Array[String] def height: Int = contents.length def width: Int = if(height ==0)0elsecontents(0).length def above(that:MyElement):MyElement = { val this1 = thiswiden that.width val that1 = that widen this.width MyElement.elem(this1.contents ++ that1.contents) } def beside(that:MyElement):MyElement = { val this1 = thisheighten that.height val that1 = that heighten this.height MyElement.elem( for( (line1,line2)<-this.contents zip that.contents )yield line1+line2 ) } def widen(w:Int):MyElement ={ if(w <= width) this else{ val left = MyElement.elem(' ',(w-width)/2,height) val right = MyElement.elem(' ',w-width -left.width ,height) left beside this beside right } } def heighten(h:Int):MyElement={ if(h<=height) this else{ val top = MyElement.elem(' ',width,(h-height)/2) val bot = MyElement.elem(' ', h - height - top.height ,height) top above this above bot } } override def toString = contents mkString "\n" } |
[13] 疑问
Array.make()
第11章_Scala的层级关系
[1]层级关系
Any是所有类的超类,Nothing是所有类的子类,下面是Any中定义的方法。
final def == (that:Any):Boolean final def !=(that:Any):Boolean def equals(that:Any):Boolean def hashCode:Int def toString:String |
== 与!=为final, 实际上equals总与==相同,所有可以通过重写equals改变== 与!=
AnyRef 是java.lang.object的别名
第12章_特质
1:特质是Scala中代码复用的基础单元(类单继承,但可混入任意多个特质
),特质类似java中的接口,但是特质比java接口丰富,可封装非抽象方法和字段,除了无类参数(主构造器)和类外观定义一样。
2:特质是含有具体方法的接口
3:最常用的两种方式:拓宽瘦接口、定义可堆叠的改变
4:与类的区别:(1)除了无类参数(主构造器)(2)类中super是静态绑定的(在类中super.funxx()是明确哪个方法将被调用),在特质中是动态绑定的,super调用的方法尚未被定义,将在每一次特质被混入到具体类的时候才被决定
[1]特质初识
1-初识
trait A{ def fun(): Unit ={ println("可以为完整的函数") } } class B extendsA{ override def toString = "混入特质A的B类" } val b = newB() val a:A = b //特质也是类型和java中的接口一样
|
2-使用(当成接口应用即可)
trait Eat{ def eat(): Unit ={ println("可以为完整的函数") } } trait Sleep{ def sleep(): Unit ={ println("可以为完整的函数") } } class Animal { override def toString = "this is animal" } class Dog extendsAnimalwithEatwithSleep{ override def eat()={ println("dog 想睡觉") } //重写特质的方法 } |
[2]拓宽瘦接口
像java接口interface1,若有N个方法,实现类必须实现所有方法;
特质是含有具体方法的接口,有些公共部分的方法可以统一给出,这样在实现类中就不用全部实现一遍了
[3]应用实例Ordered特质
特质Ordered可以让你仅仅实现一个compare方法获得比较操作;
class Rational(n: Int, d: Int)extendsOrdered[Rational] {
<<省略>>
//=================实现compare===========
def compare(that:Rational) = {
(this.number*that.denom) - (that.number*this.denom)
}
}
package com.scalaPrograming.chapter12 /* 注意构造列表的量,只对对象本省可见 */ class Rational(n: Int, d: Int)extendsOrdered[Rational] { // 条件判断 require(d != 0)
// 私有成员 private val g= gcd(n.abs, d.abs)//可以质数化 2/4 = 1/2 // 构造列表的量,只对对象本省可见,所以这里转接 val number: Int = n /g val denom: Int = d /g
def this(n: Int) =this(n,1)//辅助构造函数,处理 n/1这种情况
override def toString =if(denom==1)""+numberelsenumber+"/"+denom
def add(that: Rational): Rational =newRational( number * that.denom+ that.number*denom, denom * that.denom ) //定义操作符 def +(that: Rational): Rational =newRational( number * that.denom+ that.number*denom, denom * that.denom )
def -(that: Rational): Rational =newRational( number * that.denom- that.number*denom, denom * that.denom ) def *(that: Rational): Rational =newRational( number * that.number,denom* that.denom ) def /(that: Rational): Rational =newRational( number * that.denom,denom* that.number ) //----方法的重载 def +(i: Int): Rational = new Rational(number+ i *number,denom)
def -(i: Int): Rational = new Rational(number- i *denom,denom)
def *(i: Int): Rational = new Rational(number* i,denom)
def /(i: Int): Rational = new Rational(number,denom* i) //私有方法,注意递归函数必须有明确的类型 private def gcd(a: Int, b: Int): Int = { if (b == 0) a else gcd(b, a % b) } //=================实现compare=========== def compare(that:Rational) = { (this.number*that.denom) - (that.number*this.denom) } } object Ordered { def main(args: Array[String]) {
val half = newRational(1,2) val third = newRational(1,3) println(half < third) println(half > third) } } |
[4]特质用来做可堆叠改变
定义一个整数队列,有个put方法和get方法
abstract class IntQueue {
def get():Int
def put(x:Int)
}
定义特质
trait Doubling: 将数*2后再放入队列
trait Incrementing:将数+1后再放入队列
trait Filtering :将数过滤>=0 的才放入队列
[1]剖析一
如下定义特质,用extends,将特质限制为只能混入IntQueue子类,同java中<?extendssuperclass>
重写的方法为IntQueue中的方法,不过这里super(指向将要被混入的类) 没有指定(因为不知混入哪个类),为此用abstract override , abstract override只在特质成员的定义中使用。
trait Doubling extends IntQueue{
abstractoverride def put(x:Int){super.put(x*2)}
}
[2]剖析二
简单混入
class DoubleQueue extends BasicIntQueuewith Doubling{}
这里super 具体指DoubleQueue
这样put被重写,实现了双倍队列
[3]剖析三
堆叠
classaIncFilerQue extends BasicIntQueue withIncrementing with Filtering{}
val a = new aIncFilerQue()
a.put(-1) a.put(1)
这里-1将被过滤掉只存入(1+1)即2
备注:堆叠-也即从最右边的特质将put方法堆叠在一起一次执行,主要这里是有顺序的。若反过来,上面的例子中将存入(-1+1)、(1+1)
[4]匿名方式使用特质
备注特质也可以匿名方式使用特质
val a = new BasicIntQueue with Incrementing,而不去定义一个命名类
实例:
package com.scalaPrograming.chapter12 import scala.collection.mutable.ArrayBuffer abstract class IntQueue { def get():Int def put(x:Int) } class BasicIntQueue extendsIntQueue{ private val buf=newArrayBuffer[Int] def get() = buf.remove(0) def put(x:Int) {buf+= x} }
trait Doubling extendsIntQueue{ abstract override def put(x:Int){super.put(x*2)} }
trait Incrementing extendsIntQueue{ abstract override def put(x:Int) {super.put(x+1)} }
trait Filtering extendsIntQueue{ abstract override def put(x:Int): Unit ={ if(x>=0)super.put(x) } }
class DoubleQueue extendsBasicIntQueuewithDoubling{
}
object IntQueue{ def main(args: Array[String]) {
val queD = newBasicIntQueuewithDoubling queD.put(1) println(queD.get()) // 2
val queInc = new BasicIntQueue withIncrementing queInc.put(1) println(queInc.get())//2
val queIncFilter=newBasicIntQueuewithFilteringwith Incrementing //特质堆叠 queIncFilter.put(-1) queIncFilter.put(-2) println(queIncFilter.get())//0
val dbQue = new DoubleQueue() dbQue.put(1) dbQue.put(2) println(dbQue.get())//2 println(dbQue.get())//4 } } |
第13章_包和引用
[1]包和引用
package xxx
scala中的包是分层并嵌套的(java中仅是分层)
引用 import xxxpackage._ 用_而不是*
可以出现在任何地方
可以重命名或隐藏一些被引用的成员
实例:
1-只应用Apple,Orange
import Fruits.{Apple,Orange}
2-引用Apple并重命名为Ap
import Fruits.{Apple => Ap,Orange}
3-引用所有,但是Apple重命名为Ap
import Fruits.{Apple => Ap,_}
4-引用除了Apple所有,把xx重命名为_实际是隐藏
import Fruits.{Apple => _,_}
[2]隐式引用
import java.lang._
import scala._
import Predef._ //scala中常用的类型、方法、隐式转换
[3]访问修饰符
[1]private
仅能在类或对象的内部可见,和java不一样是若有内部类,内部类可见外部类所有,而外部类不可以访问内部类的私有成员
[2]protected
保护成员仅能在类的子类中被访问,而java中还允许同一个包的其他类
[3]public
默认是公开的,不加任何修饰
[4]保护的作用域-定制作用范围
private[X] 或 protected[X] X为指代的包、类、单例对象
[5]对象私有
private[this]
[4]对象和伴生对象
对象和伴生对象可相互方位
伴生对象相当于java中静态
第15章_样本类与模式匹配
[1]样本类
1:样本类中参数自动为 val 定义+声明式参数,可以供外部应用
2:自动为类添加与类型一致的工厂方法
3:自动添加toString、hashCode和equals的实现
4:支持模式匹配
样例:
假设定义一个操作数学表达式的库
第一步定义输入数据:变量、数字、一元、二元操作符
abstract class Expr //相当于 case class Var(val name:String) extends Expr {} //scala中类围绕空类的花括号可以去掉 case class Var(name:String)extendsExpr case class Number(num:Double) extends Expr case class UpOP(operator:String,arg: Expr)extendsExpr case class BinOp(operator:String,left: Expr,right: Expr)extendsExpr |
package com.scalaPrograming.chapter15 object CaseClass { def main(args: Array[String]) { val v = Var("x") //自动添加了同名工厂方法,无需 new Val("x"),这在嵌套使用时尤为方便 val binop = BinOp("+",Number(1),v) //样本类参数为定义+声明式的(val) println(v.name ) //toString println(binop) // == equals println(binop.right == v) // 输出: // x // BinOp(+,Number(1.0),Var(x)) // true } } abstract class Expr //相当于 case class Var(val name:String) extends Expr {} //scala中类围绕空类的花括号可以去掉 case class Var(name:String)extendsExpr case class Number(num:Double) extends Expr case class UpOP(operator:String,arg: Expr)extendsExpr case class BinOp(operator:String,left: Expr,right: Expr)extendsExpr |
[2]初识模式匹配
模式匹配也可作为简化函数的核心
要实现:
UpOP("-",UpOP("-",e)) => e //双重负号
BinOp("+",Number(0),e) => e //加0
BinOp("*",Number(1),e) => e //乘1
SimplifyTop(UpOP("-",UnOP("-",Var("x"))))
>>Var("x")
选择器 match {备选项}
case 模式 => {表达式}
模式中注入e同时在=>左右,e为绑定变量,_为通配
def SimplifyTop(expr:Expr):Expr = exprmatch{
case UpOP("-",UpOP("-",e)) => e//双重负号
case BinOp("+",Number(0),e)=> e//加0
case BinOp("*",Number(1),e)=> e//乘1
case _ => expr
}
object CaseClass { def main(args: Array[String]) { def SimplifyTop(expr:Expr):Expr = exprmatch{ case UpOP("-",UpOP("-",e)) => e//双重负号 case BinOp("+",Number(0),e) => e//加0 case BinOp("*",Number(1),e) => e//乘1 case _ => expr } println(SimplifyTop(UpOP("-",UpOP("-",Var("x"))))) } }
abstract class Expr //相当于 case class Var(val name:String) extends Expr {} //scala中类围绕空类的花括号可以去掉 case class Var(name:String)extendsExpr
case class Number(num:Double) extends Expr
case class UpOP(operator:String,arg: Expr)extendsExpr
case class BinOp(operator:String,left: Expr,right: Expr)extendsExpr
|
[3]match与switch比较
match可以看做switch的泛化
1:match是表达式是有值的,值为最终匹配的那个分支的值
2:永远不会“掉到”下一个case
3:如果没有模式命中有MatchError异常抛出,所以一般添加通配符匹配
object Match { def main(args: Array[String]) { val num :Int = 2 val matchFlag = num match{ case e@2=>println(e) //将2绑定到e 整个match的值为() case _ => //可以为空 } println(matchFlag) // 2 // () } } |
[4]模式的种类
[1]通配模式
[1]匹配任意对象
val matchFlag = num match{ case e@2=>println(e) //将2绑定到e 整个match的值为() case _ => //匹配任意对象 } |
[2]用于忽略不关心的部分
def SimplifyTop(expr:Expr):Expr = exprmatch{ case UpOP(_,_, _)} => “match”//不关系括号中的元素 case _ => expr } |
[2]常量匹配
常量仅匹配自身,任何字面量都可以用作常量,任何的val或单例对象
//常量仅匹配自身,任何字面量都可以用作常量,任何的val或单例对象 object Match { val m:Int = 7 def main(args: Array[String]) { //常量仅匹配自身,任何字面量都可以用作常量,任何的val或单例对象 val n:Int = 6 def describe(x:Any)= x match{ case 5 => "five" case `n` => "val_6" //说明不是变量,注意变量可以匹配并绑定任何值 case this.m => "this.7" //this.x 或者 obj.x 会当做常量,失效是用`x` case true => "true" case "hello" => "hi" case Nil => "object_Nil" case _ => "something else" } val tuple = (5,6,7,true,"hello",Nil,"sth") tuple.productIterator.foreach{i=>println(describe(i))} } } |
[3]变量模式
变量匹配任意对象,并绑定匹配对象,只有可以使用这个变量操作对象
def matcher(expr:Any) = expr match{ case varnum => varnum //变量匹配任意对象,并绑定匹配对象 case 0=>"zero" //不会被匹配 } matcher(0) |
matcher: matcher[](val expr: Any) => Any
res0: Any = 0
|
[4]插曲-常量表示方法
用小写字母开始的简单命名被当作是模式变量
所有其他的引用被认为是常量
val pi = Math.Pi pi是模式变量
两种方法给模式常量使用小写字母命名:
1:如果常量是某个对象的字段,“前缀.常量”形式,若this.pi 或obj.pi失效,使用方法2
2:反引号包住变量名,`pi`
反引号作用有两处不同目的的用法帮助解决编码问题,(1)反引号包住变量名当做常量;(2)处理关键字当做标识符的问题,如:Thread.`yield`()
[5]构造器模式
深度匹配
def SimplifyTop(expr:Expr):Expr = exprmatch{ case UpOP("-",UpOP("-",e)) => e//双重负号 } |
[6]序列模式
可像匹配样本类一样匹配如List或Array这样的序列类型
检查开始0的三元素模式:
expr match { case List(0,_,_) =>println("found it") case _ => } |
匹配不指定长度的序列,用_*作为最后一个元素
匹配由零开始的不计长度的任意列表:
expr match { case List(0, _*) =>println("found it") case _ => } |
[7]元组模式
匹配三元素元组并将元素值绑定到变量a,b,c中
deftupleDemo(expr:Any) = { expr match { case (a,b,c) => println("matched"+a+b+c) } } |
[8]类型模式
类型模式当做类型测试和装换的简易替代
defgeneralSize(x: Any) = xmatch{ case s: String => s.lenght case m: Map[_, _] => m.size case _ => -1 } |
类型检测:
expr.isInstanceOf(String)
类型转换:
expr.asInstanceOf(String)
不过这是不好的做好,scala鼓励我们用带有类型模式的模式匹配满足类型测试与转换场景
[9]插曲-类型擦除
特定元素类型能匹配吗
擦除模式:类型参数信息不会保留到运行时期,唯一例外是数组
defisIntMap(x: Any) = xmatch{ case m: Map[Int, Int] => true case _ => false }
isIntMap(Map(1->1)) isIntMap(Map('a'->1)) |
isIntMap: isIntMap[](val x: Any) => Boolean
res0: Boolean = true
res1: Boolean = true
|
defisStringArray(x: Any) = xmatch{ case m: Array[String] =>"yes" case _ => false } isStringArray(Array("abc")) isStringArray(Array(1,2,3)) |
isStringArray: isStringArray[](val x: Any) => Any
res0: Any = yes
res1: Any = false
|
[10]变量绑定
除了独立的变量模式外,可用@符号加变量名将对象绑定到变量中
valstr ="abc" val matcher = str match{ case e@"abc"=> e case _ => } |
[5]模式守卫
模式是线性的,模式变量仅允许在模式中出现一次,可以使用守卫解决;
守卫为ture时才匹配
val n = -1 def positive(x:Int) = x match{ case n ifn >0 => n case _ => } |
[6]模式重叠
待续
[7]封闭类
封闭类:在类定义所有文件外不能再添加任务新的子类,只需关心所在文件知道的类就可以了
对于封闭的样本类做匹配时,若缺失情况会编译前给出提示
如果打算做模式匹配的类层级,应当考虑封闭他们。只要把sealed关键词放在最顶层类的前面即可
sealed abstract class Expr //相当于 case class Var(val name:String) extends Expr {} //scala中类围绕空类的花括号可以去掉 case class Var(name:String)extendsExpr case class Number(num:Double) extends Expr case class UpOP(operator:String,arg: Expr)extendsExpr case class BinOp(operator:String,left: Expr,right: Expr)extendsExpr |
备注:即便使用封闭样本类,可以使用通配符强制避免警告
def describe(e:Expr):String= ematch{ case Number(_) => "a number" case Var(_) => "a variable" case _ => throw newRuntimeException //暴力方式 } |
def describe(e:Expr):String= (e:@unchecked )match{ //轻量级 case Number(_) => "a number" case Var(_) => "a variable" } |
e:@unchecked 随后的穷举性检查被抑制掉
[8]Option类型
[1]定义
Option 可选值的标准类型,两种形式:some(x)其中x是实际值
或者是None对象,代表缺失的值
var a:Option[String]=Some("adc") a= None |
某些操作会产生可选值,例如,Map的get方法,在发现了指定的键的值产生Some(value),若找不到返回None
val capital= Map("China"->"BeiJing","France"->"Paris") capital.get("China") capital.get("Japan")
capital: scala.collection.immutable.Map[String,String] = Map(China -> BeiJing, France -> Paris)
res0: Option[String] = Some(BeiJing)
res1: Option[String] = None
|
[2]分离可选值
分离可选值最通常:模式匹配
def show(s:Option[String])=smatch{ case Some(x) => x case None => "?" } show(capital.get("China")) show(capital.get("Japan")) |
[9]模式无处不在
[1]变量定义中
val tuple = (1,"abc") val (number,string) = tuple number string
tuple: (Int, String) = (1,abc)
number: Int = 1
string: String = abc
res4: Int = 1
res5: String = abc
|
解构样本类:
abstract class Expr //相当于 case class Var(val name:String) extends Expr {} //scala中类围绕空类的花括号可以去掉 case class Var(name:String)extendsExpr
case class Number(num:Double) extends Expr
case class UpOP(operator:String,arg: Expr)extendsExpr
case class BinOp(operator:String,left: Expr,right: Expr)extendsExpr
val oper= BinOp("*",Number(1),Number(1)) val BinOp(op,left,right)=oper
结果:
defined class Expr
defined class Var
defined class Number
defined class UpOP
defined class BinOp
oper: BinOp = BinOp(*,Number(1.0),Number(1.0))
op: String = *
left: Expr = Number(1.0)
right: Expr = Number(1.0)
|
[2]用作偏函数的样本序列
val withDefault :Option[Int] => Int ={ case Some(x) => x case None => 0 } withDefault(Some(10)) |
这个函数的函数体有两个样本:第一个匹配Some;第二个匹配None
valgetSecond:List[Int]=>Int ={ case x::y::_ => y } getSecond(List(1,2,3)) getSecond(List(1,2,3,4)) getSecond(List(1)) // 不能匹配时将报错
res7: Int = 2
res8: Int = 2
scala.MatchError: List(1) (of class scala.collection.immutable.$colon$colon)
at com.A$A201$A$A201$$anonfun$getSecond$1.apply(test.sc0.tmp:38)
at com.A$A201$A$A201$$anonfun$getSecond$1.apply(test.sc0.tmp:38)
at #worksheet#.#worksheet#(test.sc0.tmp:43)
|
List[Int]=>Int:包含了从整数List到整数的所有函数
PartialFunction[List[Int]=>Int]: 仅包含从整数列表到整数的偏函数
[3]for表达式中的模式匹配
valcapital=Map("China"->"BeiJing","France"->"Paris") for((cap,city)<-capital) println(cap+"--"+city) |
不能匹配的值被丢弃:
val fruitList = List(Some("apple"),None,Some("orange")) for(Some(fruit)<-fruitList) println(fruit)
fruitList: List[Option[String]] = List(Some(apple), None, Some(orange))
apple
|
第16章_使用列表
列表同质不可变
[1]构造列表
:: 从前端扩展列表,右结合
List(1,2,3) 等同于 1::2::3::Nil 等同于 1::(2::(3::Nil))
[2]基本操作
head |
第一个元素(列表为空抛异常) |
tail |
除第一个之外的所有元素组成的列表(列表为空抛异常) |
isEmpty |
|
|
|
//基于List基础操作的插入方法 def isort(ls:List[Int]):List[Int]={ if (ls.isEmpty) Nil else insert(ls.head,isort(ls.tail)) //把最前面的插入到已排序的部分中 } def insert(x:Int,xs:List[Int]):List[Int]={ if(xs.isEmpty || x<xs.head) x::xs else xs.head :: insert(x,xs.tail) } var ls = List(8,2,5,7,4,6) var ls_sort = isort(ls) print(ls_sort) |
[3]列表模式
[1]模式一
需要知道长度
//val List(a,b,c) = List(1,2,3,4) // MatchError //val List(d,e,f) = List(1,2) // MatchError val List(x,y,z) = List(1,2,3)
|
[2]模式二
不需要预先知道长度
val m::n::rest=List(1,2,3,4,5)
[3]基于模式的插入排序
//基于模式匹配的插入操作 def isort_match(xs: List[Int]): List[Int] = xs match{ case List() => Nil case x :: x1 => insort_match(x, isort_match(x1)) } def insort_match(x: Int, xs: List[Int]): List[Int] = xs match{ case List() => List(x) case y :: ys =>{ if (x <= y) x :: xs else y::insort_match(x,ys) } } |
[4]List类的一阶方法
[1]:::列表连接
连接两个List
List(1,2):::List(3,4)
[插曲]分治原则实现:::
object appendFun { def main(args: Array[String]) { def append(x : List[Int], y:List[Int]) : List[Int] = x match { case List() => y case x::xs => x::append(xs,y) } var la = List(1,2) var lb = List(3,4) var lab = la:::lb print(lab) var labappend = append(la,lb) print(labappend) } } |
[2]length方法返回长度
判断是否为空用isEmpty 不用length == 0 (先计算长度)
[3]init和last
init 除最后一个以外的
last 最后一个
[4]reverse
[5]前缀和后缀
take |
返回前n个,大于长度返回整个 |
drop |
返回除前n个之外,大于长度返回空 |
splitAt |
(x take n, x drop n) |
|
|
[6]apply和indices
"abcd" apply 2 //scala罕见
"abcd"(2)//隐式调用apply scala罕见
//实际上 apply仅简单的定义为drop和head组合
"abcd".drop(2).head
//indices返回指定列表的所有索引值组成的列表
"abcd".indices
结果:
res0: Char = c
res1: Char = c
res2: Char = c
res3:scala.collection.immutable.Range = Range(0, 1, 2, 3)
[7]zip
//zip操作将两个列表组成一个对偶列表
"abcd".indices zip "abcd"
//将列表元素和索引值契合在一起
"abcd".zipWithIndex
//长度不一致时不能匹配的元素被丢掉
"abcd" zip List(1,2,3,4,5,6)
[8]toString和mkString、addString
//显示列表 toString
List(1,2,3,4).toString()
//mkString
List(1,2,3,4).mkString("--")
List(1,2,3,4).mkString("[",";","]")
//变体addString,添加到StringBuilder
val strbuiler = new StringBuilder
List(1,2,3,4) addString(strbuiler,";")
List(1,2,3,4) addString(strbuiler,";")
strbuiler: StringBuilder =
res6: StringBuilder = 1;2;3;4
res7: StringBuilder = 1;2;3;41;2;3;4
[9]toArray/ productIterator
List(1,2,3).toArray List(1,2,3).toArray.toList //将数据从数组索引n处填入数组 val arr = newArray[Int](5) List(1,2,3).copyToArray(arr,2) arr //需要枚举器访问列表元素,elements val it = List(1,2,3).productIterator it.next() |
[10]归并排序
package com.scalaPrograming.chapter16 object Merge { def msort[T](less: (T,T) => Boolean)(xs:List[T]):List[T] = { //柯里化 //内部函数 def merge(xs: List[T], ys: List[T]): List[T] = { (xs, ys) match { case (Nil, _) => ys case (_, Nil) => xs case (x :: xs1, y::ys1) => if (less(x, y)) x :: merge(xs1, ys) else y :: merge(xs, ys1) } } val n = xs.length / 2 if (n == 0) xs else { val (ys, zs) = xs splitAt n//二分 merge(msort(less)(ys), msort(less)(zs)) //归并 } } def main(args: Array[String]) {
val ls = List(5,6,3,7,1) println(msort((x:Int,y:Int) => x<y)(ls)) } } |
[5]List类的高阶方法
[1] map-flatMap-foreach
针对列表,通过T=>U的函数f操作员作用于元素
map返回包含列表的列表
flatMap将所有结果按一个List返回
foreach作用于元素,不返回(操作元是过程-返回值为Unit的函数)
List(1,2,3) map (_+1) List("the","world") map(_.length) List("the","world") map(_.toList) List("the","world") flatMap(_.toList)
List.range(1,5) flatMap(i=>List.range(1,i) map (j=>(i,j))) for(i<- List.range(1,5); j<-List.range(1,i))yield(i,j)
var sum = 0 List.range(1,6) foreach(sum+=_) |
res0: List[Int] = List(2, 3, 4)
res1: List[Int] = List(3, 5)
res2: List[List[Char]] = List(List(t, h, e), List(w, o, r, l, d))
res3: List[Char] = List(t, h, e, w, o, r, l, d)
res4: List[(Int, Int)] = List((2,1), (3,1), (3,2), (4,1), (4,2), (4,3))
res5: List[(Int, Int)] = List((2,1), (3,1), (3,2), (4,1), (4,2), (4,3))
sum: Int = 0
res6: Unit = ()
|
[2]filter-partition-find-takeWhile-dropWhile-span
(1)xs partition p 等价于 (xs filter p, xs filter !p())
(2)find 返回第一个满足条件的元素Some(x) 或None
List(1,2,3,4) find(_ % 2 ==0) Some(2)
(3)takeWhile返回满足条件的最长前缀,dropWhile移除最长能满足p的前缀
(4)span是takeWhile和dropWhile的组合操作
List(1,2,3,-4,5) span(_>0)
(List(1,2,3),(-4,5))
[3]论断forall-exists
forall所有满足返回ture
exists只要有一个满足返回true
[3]折叠列表:/:和:\
sum(List(a,b,c)) 等价于 0+a+b+c
用左折叠表示:
def sum(xs:List[Int]):Int = (0/:xs)(_+_)
("{"/:List("ab","cd")}(_+" "+_) res0:String = { ab cd
[小试]
用折叠完成线性复杂度的翻转操作
object reverseLeft { def main(args: Array[String]) { def reverseLeftFun[T](ls:List[T])= { (List[T]() /: ls) ((y, ys) => ys :: y ) } var ls = List(1,2,3,4) println(reverseLeftFun(ls)) } } |
[4]sortWith
xs sortwith before 其中before是比较元素的方法
object sorted { def main(args: Array[String]) { var ls = List(2,3,5,1,6) println(ls.sortWith(_ > _)) println(List("abc","de").sortWith(_.length < _.length)) } } |
[5]List对象方法
[1]List.apply创建列表
[2]创建数值范围List.range
List.range(1,5) 返回List(1,2,3,4)
List.range(1,9,2)返回List(1,3,5,7,9)
[3]拷贝列表创建List.make
List.make(5,‘a’) 返回 List(a,a,a,a,a)
[4]List.unzip
[5]List.flatten和List.concat
List.flatten将列表的列表拼成一个列表
List.concat将不同列表拼成一个列表
List(List(1,2),List(3,4)).flatten List.concat(List(1,2,3),List(3,4)) |
res0: List[Int] = List(1, 2, 3, 4)
res1: List[Int] = List(1, 2, 3, 3, 4)
|
[6]配对列表的映射和测试
函数应用到两个列表相关元素上,将结果变为列表
第17章_集合类型
[1]集合概览
Seq 是有序集合
Set无重复
Map
[2]Seq
[1]列表
[2]数组
创建长度已知内容未知
val fiveInt = new Array[Int](5)
根据内容创建
Array(1,2,3)
访问用圆括号()
fiveInt(3)
[3]ListBuffer
List类提供对列表头部而非尾部的快速访问,需要向尾部添加元素,应考虑先reverse-添加-reverse
ListBuffer避免reverse提供高效添加+= 和前缀添加+:
最后用toList转化成List
[4]数组缓存ArrayBuffer
与Array类似,额外提供了在开始和结尾添加和删除元素的方法
[5]队列Queue
分为不可变和可变
不可变:添加enqueue,头部移除dequeue
可变:添加单一元素+= ,添加多个(已List的形式)++=
[6]栈
push、pop、top
[3]Set
无重复元素
[4]Map
[5]TreeSet和TreeMap
第18章_有状态的对象
[1]var的setter和getter方法
/* 在scala 中每一个非私有的var类型成员变量都隐含定义了getter和setter方法 但是命名和java不一样 var x getter方法为: x setter方法为:x_ */ class Time{ var hour=12 var minute=0 } //以上等价于
class Time2{ private[this]varh=12//只能被包含它的对象访问 private[this]varm=0 def hour:Int = h def hour_(x:Int){ require( 0 <= x && x < 24) h = x }
def minute:Int = m def minute_(x:Int){ require( 0 <= x && x < 60) m = x } }
//但是自定义getter和setter还是有好处了,比如设定限制条件
class Time3{ private[this]varh=12//只能被包含它的对象访问 private[this]varm=0 def hour:Int = h def hour_(x:Int){h= x}
def minute:Int = m def minute_(x:Int){m= x} } |
[2]数字电路模拟
要好好补看
第19章_类型参数化
[1]信息隐藏-私有构造方法和工厂方法
信息隐藏
* 私有构造方法和工厂方法
* 把private修饰符添加在类参数列表的前边,将主构造器隐藏起来,他只能被类本身和伴生对象访问
* 类仍然是公开的,但不能调用它的主构造器
* 可以添加辅助构造器来使用
/** * Created by hjw on 16/12/11. * * 信息隐藏 * * 私有构造方法和工厂方法 * 把private修饰符添加在类参数列表的前边,将主构造器隐藏起来,他只能被类本身和伴生对象访问 * 类仍然是公开的,但不能调用它的主构造器 * * 可以添加辅助构造器来使用 * * 可以添加用初识元素序列创建队列的工厂方法 */ class Queue[T] private( private val leading:List[T], private val trailing:List[T] ){ //可以添加辅助构造器来使用 def this() = this(Nil,Nil) def this(elems:T*) =this(elems.toList,Nil) def getLeading():List[T] = leading }
//可以添加用初识元素序列创建队列的工厂方法 object Queue{ //用XS构造队列,伴生对象可以调用私有方法 def apply[T](L:List[T],T:List[T]) = new Queue[T](L,T) } object privateConstructor {
def main(args: Array[String]) { //val que = new Queue[Int](List(1,2),List(1)) //connot resolved constructor val que1 = new Queue[Int]() val que2 = newQueue[Int](1,2,3) que2.getLeading().foreach(println(_)) // 1 // 2 // 3 val que3 = Queue[Int](List(1,3),List(2,3)) } } |
[2]信息隐藏-私有类
/** * Created by hjw on 16/12/11. * * 另一种更为彻底方式: * 直接把类本身隐藏,已提供一个提供公共接口的特质 */
trait Queue[T]{ //公共接口 def head: T def trail:Queue[T] def append(x:T):Queue[T] }
object Queue{ def apply[T](xs:T*) :Queue[T] =newQueueImpl[T](xs.toList,Nil) private class QueueImpl[T]( private val leading:List[T], private val trailing:List[T] )extends Queue[T]{ def mirror = if(leading.isEmpty) new QueueImpl[T](trailing.reverse,Nil) else this def head:T = mirror.leading.head def trail:QueueImpl[T]={ val q = mirror new QueueImpl[T](q.leading.tail,q.trailing) } def append(x:T) =newQueueImpl[T](leading,x :: trailing) } }
|
[3]变化型注释
* 参数的变化型:协变,逆变,非协变 * (1)严谨的子类型化:S是T的子类型,Queue[S]不能当做Queue[T]的子类型 * (2)协变(弹性)的子类型化:要在泛型前添加"+"号 * 例如:trait Queue[+T]{....}-->S是T的子类型,Queue[S]当做Queue[T]的子类型 * (3)"-"号,逆变的子类型化 * 例如:trait Queue[-T]{....}-->S是T的子类型,Queue[T]当做Queue[S]的子类型
(4)可以被重新赋值的对象是不能用参数类型(+\-)的 //class StrangeIntQue extend Queue[Int]{ // override def append(x:Int) ={ // println(Math.sqrt(x)) // super.append(x) // } // } // val x:Queue(Any) = new StrangeIntQue // x.append("abc") // // class Queue[+T] { // def append(x:T) = ... // }
|
[4]下界
U是T的超类
class Queue[+T](private valleading:List[T], private val trailing:List[T] ) { def append[U>:T](x:U) = new Queue[U](leading,x::trailing) } |
[5]逆变
//逆变的输出通道 //OutputChannel[String]可以输出,而对于OutputChannel[AnyRef]也是安全的 //T是U的子类, T的方法对U来说均可以执行 trait OutputChannel[-T]{ def write(x:T) } //有时,逆变和协变同时混合在一个类型中出现 //例如scala的函数特质 //函数类型:A=>B, scala会扩展成Function1[A,B],而Function1 //trait Function1[-S, +T]{ // def apply(x:S):T //} //Library.printBookList(getTitle) Publiction=>String --逆变--> Book=>AnyRef //结果String-->AnyRef
class Publiction(valtitle:String) class Book(title:String)extendsPubliction(title)
object Library{ //定义一个books的set val books:Set[Book] = Set( new Book("scala"), new Book("spark") ) //定义个打印函数 def printBookList(info: Book=>AnyRef): Unit ={ for (book<-books){ //将函数作为参数 println(info(book)) } } } object inverter extendsApp{ def getTitle(p:Publiction):String= p.title Library.printBookList(getTitle) // scala // spark } |
[6]对象私有数据
可以被重新赋值的对象是不能用参数类型(+\-)的, 但是
对象私有成员仅能在被定义的对象内部访问,同一个对象内部不会对变量引起变化型相关的问题
变化型的检查规则包含关于对象私有定义的特例,当检查到带有+/-号的类型参数只出现在具有相同变化型的分类的位置上时,这种定义被忽略
private[this] var leading: List[T], //私有变量 可行
/可以被重新赋值的对象是不能用参数类型(+\-)的,但是 //对象私有成员仅能在被定义的对象内部访问,同一个对象内部不会对变量引起变化型相关的问题 //变化型的检查规则包含关于对象私有定义的特例,当检查到带有+/-号的类型参数只出现在具有相同变化型的分类的位置上时,这种定义被忽略 //private[this] var leading: List[T], //私有变量 可行 private class Queue[+T]( private[this]varleading:List[T],//私有变量 private[this]vartrailing:List[T] ) {
private def mirror() = if (leading.isEmpty) { while (!trailing.isEmpty) { leading = trailing.head :: leading trailing = trailing.tail } }
def head: T = { mirror() leading.head }
def trail: Queue[T] = { mirror() new Queue[T](leading.tail, trailing) } def append[U>:T](x:U) =newQueue[U](leading, x :: trailing) } |
[7]上界
package com.scalaPrograming.chapter19 /** * Created by hjw on 16/12/11. * * T<:Ordered[T],类型参数T具有上界Ordered[T] */
//例如比较两个人,根据lastname和fistname的字母顺序 //val robert = new Person("Robert","Jones") //val sally = new Person("Sally","Smith") //robert < sally ---> 返回true
class Person(valfistName:String,vallastName:String)extendsOrdered[Person] { def compare(that: Person) = { val lastNameCmp = lastName.compareToIgnoreCase(that.lastName) if (lastNameCmp != 0) { lastNameCmp } else fistName.compareToIgnoreCase(that.fistName) } override def toString = fistName +" "+ lastName }
object shangjie extendsApp {
def orderedMergeSort[T<:Ordered[T]](xs:List[T]):List[T] = { def merge(xs: List[T], ys: List[T]): List[T] = (xs, ys) match { case (Nil, _) => ys case (_, Nil) => xs case (x::xs1,y::ys1)=> if(x<y) x::merge(xs1,ys) else y::merge(xs,ys1) } val n = xs.length/2 if(n == 0) xs else{ val (ys,zs) = xs splitAt(n) merge(orderedMergeSort(ys),orderedMergeSort(zs)) } } val people =List( new Person("Robert","Jones"), new Person("Sally","Smith") ) println(orderedMergeSort(people).toString()) // List(Robert Jones, Sally Smith) } |
第20章_抽象成员
[1]抽象成员/val
val num:Int // 相当于定义了 def num:Int
val 和def的区别:
val 限制了实现方式必须为常量不可变,而def 随调用可变
val 可重写def ,而def不可重写val
/* 抽象成员初识 */ trait Abstract { type T //抽象类型 def transform(x: T): T val initial: T var current: T val num:Int // 相当于定义了 def num:Int } class Concrete extendsAbstract { type T=String override def transform(x:String):String= x + x override val initial:String="hi" override var current:String=initial override val num: Int =1 } object abstractMember extendsApp{ val concrete=newConcrete() println(concrete.transform("h")) //hh } |
abstract class Fruit{ val v:String def m:String } abstract class Apple extendsFruit{ val v:String val m:String// val重写 def } abstract class BadApple extends Fruit{ def v:String//错误 def不能重写 val , scala里方法和变量不可以重名 val m:String } |
[2]抽象var 相当于定义抽象getter/setter方法
//对于抽象var相当于定义了抽象的getter方法和setter方法 trait AbstractTime{ var hour:Int var minute:Int } trait AbstractTime{ def hour:Int // hour的getter方法 def hour_= (x:Int) // hour的setter方法 def minute:Int def minute_= (x:Int) } |
[3]抽象val充当超类参数/匿名类
/* 抽象val 有时可以扮演超类的参数的角色 特质无参数(构造函数),可以用抽象val,在子类中实现,相当于传入参数 */ trait RationalTrait { val numerArg: Int // 抽象val做伪参数 val denomArg: Int // 抽象val做伪参数 } object abstractVal { val anonymousClass=newRationalTrait { //匿名类 val numerArg=1 val denomArg=2 } } |
[4]匿名类的参数实现的滞后性
new Rational(exp1, exp2) 普通类参数是在构造前计算,而匿名类在构造之后计算
trait RationalTrait { val numerArg: Int // 抽象val做伪参数 val denomArg: Int // 抽象val做伪参数 require(denomArg!=0) def cal = numerArg/denomArg }
object abstractVal extends App{
val anonymousClass=newRationalTrait { //匿名类 val numerArg=1 val denomArg=2 } println(anonymousClass.cal) }
//相当于在初识化的时候denomArg仍然为0,require(denomArg!=0)报错
Exception in thread "main" java.lang.IllegalArgumentException: requirement failed
at scala.Predef$.require(Predef.scala:221)
|
[5]Fields预初始化字段
[1]在匿名类和new之间加花括号
trait RationalTrait { val numerArg: Int // 抽象val做伪参数 val denomArg: Int // 抽象val做伪参数 require(denomArg!=0) def cal = numerArg/denomArg } object initialField extendsApp{ val x:Int =2 val ra=new{ val numerArg: Int =4*x val denomArg: Int =2*x }with RationalTrait println(ra.cal) } |
[2]类对象和有名字的子类也可以用预初始化
在extends和with之间
trait RationalTrait { val numerArg: Int // 抽象val做伪参数 val denomArg: Int // 抽象val做伪参数 require(denomArg!=0) def cal = numerArg/denomArg } class twoThirds extends{ val numerArg: Int =2 val denomArg: Int =3 }with RationalTrait |
[6]懒加载val
lazy 关键字放在val前面,这样val 在调用时才加载计算,类中不同lazy val字段不用考虑定义顺序的问题,编译器本身会识别依赖顺序
object Demo{ val x= {println("initializing x");"done"} } Demo |
defined module Demo
initializing x
res0: Demo.type = com.A$A9$A$A9$Demo$@33a7bcea
|
object Demo{ lazy val x= {println("initializing x");"done"} } Demo Demo.x |
defined module Demo
res0: Demo.type = com.A$A13$A$A13$Demo$@7e2af0f7
initializing x
res1: String = done
|
trait LazyRationalTrait { val numerArg: Int val demoArg: Int lazy val numer= numerArg/g lazy val demo= demoArg/g override def toString =numer+"/"+demo private lazy val g= { require(demoArg != 0) gcd(numerArg,demoArg) } private def gcd(a:Int, b:Int):Int = if (b == 0 ) a else gcd(b,a%b) } val x = 2
new LazyRationalTrait {override valnumerArg: Int =1*2 override val demoArg: Int =2*x }
x: Int = 2
res0: LazyRationalTrait = 1/2
|
[7]抽象类型
//Type T 将在子类中充当占位符 //scala 中函数重写,参数是不支持协变 class Food abstract class Animal{ def eat(food:Food) } ////函数重写,参数是不支持协变 ,正确 不然可以定义个鱼的对象给牛吃唠 class Grass extends Food class Cow extendsAnimal { override def eat(food:Grass){}//函数重写,参数是不支持协变 } |
通过抽象类型解决问题
class Food abstract class Animal{ type SuitableFood<: Food def eat(food:SuitableFood) } class Grass extendsFood class Cow extendsAnimal{ type SuitableFood= Grass override def eat(food: Grass){} } |
[8]路径依赖类型
class Outer{ class Inner } //内部类的表示形式Outer#Inner val o1 = newOuter val o2 = newOuter o1.Inner // 值特定(o1引用 )的外部对象的Inner类 o12.Inner// 值特定(o2引用 )的外部对象的Inner类 //scala 中内部类持有外部类实例的引用,可以访问外部类的成员 //不能在没有指定外部类实例的情况下实例化内部类 实例化内部类的方式1: 直接在外部类的方法体中完成 实例化内部类的方式2: 使用路径依赖类型 ,如:new o1.Inner命名了特定的外部类,但是o1#Inner不可,没有指定外部类 |
[9]枚举
//scala中通过标准库中的类实现枚举 //要创建新的枚举,只需要扩展这个类scala.Enumeration object Color extends Enumeration { val Red= Value val Green= Value val Blue= Value } //==> 可简化为: object Colortemp extends Enumeration { val Red,Green,Blue= Value } //==>可指定显示值 object Direction extends Enumeration { val North= Value("North") val East= Value("East") val South= Value("South") val West= Value("West") } object Enumeration { def main(args: Array[String]) { //引用方式 //import.Color._ //枚举值从0开始,可利用id获取计数值 println(Direction.East)//East println(Direction.East.id)// 1 //指定id获取枚举值 println(Direction(1)) // East } } |
[10]案例研究??????待补充
第21章_隐式转换和参数
隐式定义:编译器为修正类型错误而允许插入到程序中的定义.
例如:x+y 不能通过类型检查,那么编译器可能会把它改为convert(x) + y
这里convert是某个可用的隐式转化,把x改为带有+方法的东西
通过隐式转化可以直接借用已定义的类或方法,也可以在利用第三方库时,解决入口参数等其他不可用的问题
[1]隐式参数作用场景
1:转换为期望类型
在需要不同类型时直接使用语境中的类型,自动做了类型转换
2:方法调用者的转换
在方法不能应用在原本类型上时让你对方法调用者做出适配
例如:“abc”.exits,将被转换为StringWrapper(“abc”).exits,因为字符创本身无exits方法,这里通过StringWrapper转化为拥有exits方法的新调用者
3:隐式参数
用于给调用参数提供更多期待信息
[2]隐式转换为期望类型
一旦编译器看到X但是需要Y,会检查从X到Y的隐式转换
val i:Int=3.5
会报错
implicit def doubleToInt(x:Double) = x.toInt val i:Int = 3.5 |
doubleToInt: doubleToInt[](val x: Double) => Int
i: Int = 3
|
scala.Predef中就定义了很多“较小的”数据类型转变为“较大的”类型的隐式转换
scala.Predef implicit def int2double(x:Int):Double = x.toDouble |
[3]转换(方法调用的)接收者
1:接收者转换使得新类可平滑地集成到现存类层级中
2:支持在语言中编写特定语言(DSL)
工作原理:
obj.doIt,但是obj并没有doIt成员,编译器尝试将obj转化成拥有doIt成员的对象
[1]与新类型的交互操作
例如:第6章中Rational类中1+other,将1转为Rational对象才有+方法
[2]模拟新的语法
Map(1->"one")
这里->符号不是内建语法,
->只是定义再scala序文(scala.Predef)中的类ArrowAssoc的方法,
这个序文还定义了从Any到ArrowAssoc的隐式转换
在调用1->"one"时,插入从1到ArrowAssoc的转换以便找到->方法
package scala import scala.Predef.ArrowAssoc object Predef{ class ArrowAssoc[A](x:A){ def -> [B](y:B):Tuple2[A,B]=Tuple2(x,y) } implicit def any2ArrowAssoc[A](x:A):ArrowAssoc[A]=newArrowAssoc(x) } |
这种富包装器形式的模式在给定语言提供类似于语法扩展的库中常见
当看到方法不像是类的成员时,就可能是隐式转换
形如RichSomething的类,如RichInt或RichString,就有可能是在给Something 类添加类似语法的方法
[3]隐式参数
[1]隐式参数基础
someCall(a)代替someCall(a)(b)
这里b通过隐式转换参数补充,要实现必须定义一个implicit参数,同时someCall中第二个参数也要implicit标识,表示可以通过隐式提供
object otherPres{ implicit val prrompt=newPreferredPrompt("#:\t") } //假设有个提示符类 class PreferredPrompt(valpref:String) object Greeter{ def greet(name:String)(implicitprompt:PreferredPrompt): Unit ={ println("Welcome " + name +"the system is ok") println(prompt.pref) //打印提示符 } } object implicitArg extendsApp{ val greeter= Greeter import otherPres._ //导入隐式转换 greeter.greet("hjw") // Welcome hjwthe system is ok // #: } |
[2]例子带有上界的函数
//返回List的最大值 def maxListUpBound[T<:Ordered[T]](ele :List[T]):T= ele match{ case List() => throw newIllegalArgumentException("empty List") case List(x) => x case x::rest => val maxRest = maxListUpBound(rest) maxRest } val max = maxListUpBound(List(1,2,3,6,3,8,1))
Error:(9, 36) type mismatch;
found : List[Int]
required: List[T]
lazy val max = maxListUpBound(List(1,2,3,6,3,8,1))
^
|
//返回List的最大值
//这种模式很普遍,scala库中已包含了隐式的orderer方法 def maxListUpBound[T](ele :List[T])(implicitorderer:T=>Ordered[T]):T= ele match{ case List() => throw newIllegalArgumentException("empty List") case List(x) => x case x::rest => val maxRest = maxListUpBound(rest) if(orderer(x) > maxRest ) x //可以省略 if( x >maxRest ) x else maxRest } val max = maxListUpBound(List(1,2,3,6,3,8,1)) print(max)
|
[4]视界
T <% Ordered[T]: 任何T只要能当做Ordered[T],如可隐式转换为Ordered[T],
//返回List的最大值 def maxListUpBound[T <% Ordered[T]](ele :List[T]):T= ele match{ case List() => throw newIllegalArgumentException("empty List") case List(x) => x case x::rest => val maxRest = maxListUpBound(rest) if(x > maxRest ) x // else maxRest } val max = maxListUpBound(List(1,2,3,6,3,8,1)) print(max) |
[4]隐式转换的调试
[1]显示调用,看错误日志
[2]- Xprint:typer 运行
scalac看编译代码插入了什么
第22章_实现List
[1]ListBuffer
object ListBufferTestextendsApp { //不使用map实现元素自增 //这不是尾递归, def incList(xs: List[Int]): List[Int] = xs match{ case List() => List() case x :: res => x +1::incList(res) } //低效率用循环,:::的时间效率与第一个操作数成正比 val xs=List(1,2,3) var res=List[Int]() for (x<- xs)res=res:::List(x+1)
//列表缓冲,抵用buf += elem ,将elem添加到列表后部,然后toList转换,两个操作都在常数时间完成 val buf=newListBuffer[Int] for (x<- xs)buf+= x+1 }
//List类的大多数操作都是用列表缓冲的循环方案作为替代递归 final override def map [U](f: T=>U):List[U]={ val b = newListBuffer[U] var these = this while(!these.isEmpty){ b+=f(these.head) these = these.tail } }
|
第23章_重访For表达式
[1]引子
在scala中for-yeild都会被转义成map\flatMap\filter的组合调用
不带yield的for会被转义成filter和foreach的调用
//在scala中for-yeild都会被转义成map\flatMap\filter的组合调用 //不带yield的for会被转义成filter和foreach的调用 case class Person( name: String, isMale: Boolean, children: Person* )
object For extends App {
val lara = Person("Lara", false) val bob = Person("Bob", true) val julie = Person("Julie",false,lara,bob) val persons = List(lara,bob,julie)
//找出列表中所有妈妈和孩子的结对 //方法一: val list1 = persons.filter(p => !p.isMale).flatMap(p=>(p.children map (c=>(p.name,c.name))))
list1.foreach(println) // (Julie,Lara) // (Julie,Bob)
//方法二:用for显得简单清楚些 val list2 = for (p<-persons; if !p.isMale; c<- p.children ) yield (p.name,c.name) list2.foreach(println(_)) // (Julie,Lara) // (Julie,Bob) } |
[2]For基本格式
for(seq)yieldexpr seq 由抽取器 定义 及 过滤器组成序列,分号分割
for(p<-persons; n = p.name; if (n startWith "To")) yield n
//可用花括号代替小括号,此时分号可省略 for{ p<-persons n = p.name if (n startWith "To") } yield n
生成器可多个,前面的每变化一次,后面的变化一轮
|
for (x <- List(1,2);y<-List("one","two")) yield (x,y) |
res0: List[(Int, String)] = List((1,one), (1,two), (2,one), (2,two)) |
[3]皇后问题
组合问题尤其适合用for
object NQueen extends App{ def inCheck(q: (Int, Int),qq : (Int,Int)) ={ q._1 == qq._1 || // 同一行 q._2 == qq._2 || //同一列 ((qq._1-q._1).abs == (qq._2-q._2)) } def isSafe(queen: (Int,Int), queens:List[(Int, Int)]):Boolean = { queens forall (q => ! inCheck(queen, q)) } //从N皇后,在N-1的基础上接着放,0皇后是List(List()) // List中的每个List是一种方法 def queens(n: Int):List[List[(Int, Int)]] = { //如果是 def placeQueens(k: Int):List[List[(Int, Int)]] = { if (k == 0) List(List()) else for { //在第K-1皇后的基础上,遍历1到N行可以增加的位置 queens <- placeQueens(k - 1) //k-1 皇后推K皇后 ==前K-1行不能再放了,推到第K行,所有列可以接着放的情形 col <- 1 to n queen = (k, col) if isSafe(queen, queens) } yield queen :: queens } placeQueens(n) } val res =queens(4) res |
第24章_抽取器
[1]例子:抽取email
object EMail { //注入方法(可选),根据传入的用户和域名,返回用户url def apply(user:String, domain:String) = user +"@"+ domain //抽取方法(规定) //这里还要处理非email的情况用Option返回对偶 def unapply(str:String):Option[(String,String)]= { val parts = str split "@" if (parts.length == 2) Some(parts(1),parts(1))elseNone } } |
第25章_注解
第26章_XML
第17章使用对象的模块化
有空待看
第28章_对象相等性
待补充
第29章_结合java和scala