scala学习笔记(9-12)

目录

  • 9.文件和正则表达式
  • 10.特质
  • 11.操作符
  • 12.高阶函数

9.文件和正则表达式

  • Source.fromFile(...).getLines.toArray输出文件的所有行

  • Source.fromFile(...).mkString 以字符串形式输出文件内容

  • 将字符串转换为数字,可以用toInt或toDouble方法

  • “正则”.r是一个Regex对象

  • 如果你的正则表达式包含反斜杠或引号的话,用"""...""",如val p = """\s+[0-9]\s+""".r

  • 如果正则模式包含分组,你可以用如下语法来提取它们的内容,for(regex(变量,...,变量)<- 字符串)

  • 读取行

val source = Source.fromFile("myfile.txt","UTF-8")
source.getLines
  • 读取字符 for( c <- source ) 处理c
  • 读取词法单元和数字。 val tokens = source.mkString.split("\s+")
  • 从URL或其他源读取 source.formURL、fromString、stdin
  • 读取二进制文件:需要使用Java类库
  • 写入文本文件:使用java的PrintWriter来写入文本文件

访问目录

  • Scala没有访问目录中所有文件和遍历所有目录的类。
  • 自己写函数解决,如下:
  def subdirs(dir:File):Iterator[File]={
    val children = dir.listFiles().filter(_.isDirectory)
    children.toIterator ++ children.toIterator.flatMap(subdirs(_))
  }

  def main(args: Array[String]): Unit = {
    val dirStr = "C:\\Users\\zhouliang6\\Desktop\\项目";
    val dir = new File(dirStr)
    for( d <- subdirs(dir)) println(d.toString)
  }

序列化

  • @SeriaVersionUID(42L) class Person extends Serializable
  • 如果能接受缺省ID,可以省略@SeriaVersionUID注解
  • scala集合类都是可序列化的
scala学习笔记(9-12)_第1张图片
序列化反序列化

进程控制

  • scala.sys.process包提供了用于与shell程序交互的工具,可以用Scala编写shell脚本。
import sys.process._
"ls -al .."!          
  • ls -al ..命令被执行,显示上层目录的所有文件。
  • sys.process包包含一个从字符串到ProcessBuilder对象的隐式转换,!操作符执行的就是这个ProcessBuilder对象。
  • !操作符返回结果:0:程序执行成功。失败显示非0
  • !!操作会以字符串形式返回。

正则表达式组

scala学习笔记(9-12)_第2张图片
image.png

《快学Scala》第九章习题解答

10.特质

  • 特质中方法没有实现,则特质可以当做接口使用,子类要实现特质的接口
  • 带有具体实现的特质,在子类中可以直接使用特质的实现方法。

叠加在一起的特质

  • super调用特质层级中的下一个特质,特质从最后一个开始被处理。

带有特质的对象

  • 特质B中的方法log有实现,但是特质B的实现满足不了子类Obj的需求,此时可以在构造子类Obj具体对象的时候混入一个更好的特质A(A实现了B并且重写了方法log)

trait Logged {
  def log (msg :String){ } //有实现,非抽象方法  
}

class SavingAccount extends  Logged{
  def withdraw(ammount:Double): Unit ={
    log("withdraw :"+ammount)
  }
}

trait ConsLogger extends Logged{                  //重写特质Logged的log方法
  override def log(msg:String){println(msg)}
}

object SavingAccount{
  def main(args: Array[String]): Unit = {
    val acct = new SavingAccount with ConsLogger      //构造对象时候加入特质
    acct.withdraw(100)
  }
}

特质中抽象字段和具体字段

  • 字段给出了具体值,那么字段是具体的
    • 使用该特质的类都会获得一个字段与之对应,这些字段不是被继承的,而是简单地被加到了类当中,而超类中的字段是被继承的。
  • 抽象字段,在一个具体的类中必须提供值。
trait ShortLogger{
  val maxLength:Int      //注:该抽象字段并没有在特质的构造器中使用,如果在构造器中使用了,则下面方法有误
}
class SavingAccount extend Account with ShortLogger{
  val maxLength = 20 //不需要override
}
val acct1 = new SavingsAccount 
val acct2 = new SavingsAccount with ShrotLogger{ 
   val maxLength = 20 
}

特质构造顺序

  • 构造器由特质字段的初始化和其他特质中的语句构成


    scala学习笔记(9-12)_第3张图片
    特质构造顺序

初始化特质中的字段

  • 特质不能有构造器参数,每个特质都有一个无参数的构造器,如果特质的构造器中使用到了抽象字段,那么在子类中如何初始化该字段呢?
scala学习笔记(9-12)_第4张图片
抽象字段在构造中使用后,在子类中如何初始化抽象字段
  • 解决:在子类SavingsAccount中初始化抽象字段


    方法一:实例化时提前定义
scala学习笔记(9-12)_第5张图片
方法二:定义类时提前定义
scala学习笔记(9-12)_第6张图片
方法三:使用懒值

扩展类的特质

  • 特质可以扩展类,这个类将自动成为所有混入该特质的超类
trait PropertyChange extends PropertyChangeSupport{
}
class MyPoint extends PropertyChange{
}
class  Test extends  App{
  val myp = new MyPoint
}
  • 如果累已经扩展另一个类怎么办?
    • 只要那是特质超类的一个子类即可。
class MyPoint extends Exception with PropertyChage            //错误,因为Exception不是PropertyChangeSupport的子类

自身类型

  • 当特质以如下代码开始定义时,它便只能被混入指定类型的子类。
 this:类型 =>
scala学习笔记(9-12)_第7张图片
image.png

快学scala第十章习题答案

11.操作符

apply和update方法

  • f(arg1,arg2,...)是函数调用语法,但是可以应用到非函数和方法之外的值
    • f(arg1,arg2,...) 等于 f.apply(arg1,arg2,...) //此时的f不是函数或方法
  • f(arg1,arg2,...)=value 等于 f.update(arg1,arg2,...,value) //该机制只限于数组和映射
scala学习笔记(9-12)_第8张图片
案例

提取器

  • 一个带有unapply方法的对象,一般可以创建的伴生对象中,其它地方也可以。
  • 从对象中提取值。
  • 提取固定数量值
  • 返回Option[(元组类型)]或、单值目标类型的Option[类型]、Boolean
* Name对象,其unapply方法返回一个Option[(String,String)]
object Name{
  def unapply(input:String)={
  val pos = input.indexOf(" ")
  if( pos == -1 ) None
  else Some( (input.substring(0,pos) , input.substring(pos+1) )
  }
}
  • 使用方式
1.变量定义时使用,如下first、last变量
val author ="Cay Horstmann"   
val Name(first,last) =author 
// 实际调用Name.unapply(author )并返回(Cay,Horstmann)元组,然后将元组值赋给first、last

2.模式匹配中使用
author match{
   case Name(a,b) => ...a,b分别绑定到Option的值上

}

带单个参数或无参数的提取器

  • 此处的单参数和无参数不是unapply方法的参数,而是提取器可传入的参数,如Name(a,b)两个参数,也就是Option返回的元组中组件个数。
  • 带unapply方法的对象
  • 返回Option[Int]
  • 返回Boolean
  • 提取固定数量值
使用unapply提取单值,则返回一个目标类型Option
object Number{

 def unapply(input: String ):Option[Int] ={
   try{
    Some(  Integer.parseInt( input.trim ) )
   }catch{
    case ex :NumberFormatException => None
   }
 }
}

使用:定义变量n
val Number( n ) = "123"
  • 无参:即IsCompond() 没有参数,说明其返回的不是Option[类型]也不是Opting[()],不然必须在IsCompond()中定义变量,那么要他干嘛?IsCompond()可以返回Boolean类型,Boolean值不用变量来接收,而只是做一个判断。即提取器只是测试输入而不将值提取出来。
// unapply返回Boolean,
object IsCompond{
 def unapply(input: String )= input.contains(" ")
}

使用:
author match{
   case Name(a, last @ IsCompound() ) => ...a,b分别绑定到Option的值上,

}
  • @的用法

unapplySeq方法

  • 要取任意长度的值得序列,应该用unapplySeq来命名方法.
  • 返回一个Option[ Seq[A] ]
  • 提取不固定数量值
object Name{
   def unapplySeq(input:String):= Option[ Seq[String] ]={
     if( input.trim == "" ) None
     else Some( input.trim.split( "\\s+" ) )
  }
}

12.高阶函数

作为值得函数

import scala.math._
val num =3.14
val fun = ceil _    //函数
fun(num)       // 4
  • ceil函数后的_意味着你确实指定的是这个函数,而不是碰巧忘记了给它参数。

匿名函数

  • (x: Double ) = > 3 * x
  • 该函数将传给它的参数乘以3
  • Array(3, 2).map( (x: Double ) = > 3 * x ) //Array(9, 6)
  • val triple = (x: Double ) = > 3 * x //存放到变量中

带函数参数的函数

  • 接受函数参数的函数叫做高阶函数
  • 函数类型写作:(参数类型)=>结果类型
  • 1.接收函数作为参数的函数
valueAtOneQuarter(ceil _)
def valueAtOneQuarter( f:(Double) => Double ) = f(0.25)
  • valueAtOneQuarter的类型为:结果类型为Double,参数类型为(Double)=>Double,所以函数类型为( (Double)=>Double ) => Double
  • 2.高阶函数也可以产出另一个函数。
 def mulBy(factor:Double) = (x :Double) => factor * x
  • mulBy(3)返回匿名函数(x:Double)=>3*x。
  • val quintuple = mulBy(5)
    quintuple (20) //100
  • mulBy的类型为:(Double) => ( (Double) => Double )

参数(类型)推断

def valueAtOneQuarter( f:(Double) => Double ) =f(0.25)
  • valueAtOneQuarter函数接收一个函数作为参数,且参数类型为(Double)=>Double

  • 传入匿名函数调用,4种方式:

    1. valueAtOneQuarter( (x:Double) => 3 * x )
    1. valueAtOneQuarter( (x) => 3 * x )
      因为valueAtOneQuarter知道传入的函数类型为(Double)=>Double所以可以省略Double
    1. valueAtOneQuarter( x => 3 * x )
      对于只有一个参数的函数,可以省略外围的()
    1. valueAtOneQuarter( 3 * _ )
      如果参数在=>右侧只出现一次,可以用"_"替换它。
  • 注意:这些简写仅在参数类型已知情况下有效。valueAtOneQuarter函数在定义时已经知道了参数的类型,所以可以这样简写

  • 如下4种都是相同函数:

  • val fun = (x:Double) => 3 * x

  • val fun = 3 * (_:Double)

  • val fun = x = > 3* (x:Double) (错误)

  • val fun = (x:Double) = > 3* (x:Double)

  • val fun:(Double)=>Double = 3 * _
    先声明了fun为(Double)=>Double 类型,所以3 * _ 正确

  • 调用: fun(3) //结果:9.0

常用高阶函数

  • Array类型有一个map方法,参数为函数,且参数类型为泛型。
def map[B](f: (A) ⇒ B): [Array][B]
  • 调用
(1 to 9).map("*" * _).foreach(println _)

传入的匿名函数的非简写形式:(x:Int)=>"*" * x

 Array("z","liang").map( "jd" + _).foreach(println _)
 Array("z","liang").map( (x:String)=>"jd" + x ).foreach(println _)

传入的匿名函数的非简写形式:(x:String)=>"jd" + x

柯里化

  • 将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数作为参数的函数。
  • 接受两个参数的函数
def mul(x:Int ,y:Int) =x * y

定义柯里化函数

  • 接受一个参数的函数,生成另一个接受单个参数的函数
def mulOneAtAtime(x:Int ) = (y: Int) => x * y

调用:mulOneAtAtime(6)(7) ,
调用过程mulOneAtAtime(6)结果是(y :Int)=>6 * y ,这个函数被应用到7,最终得到42

  • 简写定义柯里化函数
def mulOneAtAtime(x: Int)(y: Int) = x * y

目的:可以把某个函数参数单领出来,已提供更多用于类型推断的信息。

scala学习笔记(9-12)_第9张图片
柯里化典型案例

控制抽象

  • 将一系列语句组成不带参数也没有返回值的函数。
    • 是个函数
    • 没有参数
    • 没有返回值
    • 但要里面要执行一系列语句
  • 过程就没有返回值也可以没有参数
def box(){
println("hi");
Thread.sleep(1000)
}
  • 把一系列语句传给函数执行,称为控制抽象。因为执行的内容是通过外部传过来的。函数中用来接收一系列语句的参数类型为()=>Unit的函数,因为该一系列语句组成的函数不接受参数也没有返回值。
  • 举例
def runInThread(block: ()=>Unit){
  new Thread{
   override def run(){ block() }
  }.start()
}
  • 调用
runInThread{ ()=> println("hi"); Thread.sleep(1000); }
  • 省略()=>,可以使用换名调用表示法:在参数声明和调用该函数参数的地方去掉(),但保留=>
def runInThread(block: =>Unit){
  new Thread{
   override def run(){ block }
  }.start()
}
  • 调用
runInThread{  println("hi"); Thread.sleep(1000); }

柯里化

  • 第二个参数类型为()=>Unit的函数,
  • scala学习笔记(9-12)_第10张图片
    定义
  • 调用
var  x =10
until( x== 0) {
  x -= 1
 println(x)
}

你可能感兴趣的:(scala学习笔记(9-12))