Scala编程_学习记录

时间:2021-02-16 00:08:48

<<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_2value,可省略 =
 
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.dthat.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))//argString
     
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 = "混入特质AB"
}
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 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仅简单的定义为drophead组合
"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类型成员变量都隐含定义了gettersetter方法
但是命名和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
  }
}

//但是自定义gettersetter还是有好处了,比如设定限制条件

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{
  //定义一个booksset
 
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]
  */

//
例如比较两个人,根据lastnamefistname的字母顺序
//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  // hourgetter方法
 
def hour_= (x:Int) // hoursetter方法
 
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的调用

//scalafor-yeild都会被转义成map\flatMap\filter的组合调用
//
不带yieldfor会被转义成filterforeach的调用
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皇后的基础上,遍历1N行可以增加的位置
         
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