3. 什么是 scala http://www.scala-lang.org/ scala 是 Scalable Language 的简写,由 Martin Odersky (Generic Java 的作者 ) 发明,目的就是创造一个 jvm 上的高度可扩展的语言。 2003 诞生,前身是 Pizza ( 1996 ,晚 java 一年)、 Funnel ,最新版本是 2.9.0.1 ,它具有以下特性: 1.It has an event-based concurrency model. (基于事件的并行模式) 2.It supports both an imperative style and a functional style. (支持命令式和函数式编程) 3.It is purely object-oriented. (纯粹的面向对象) 4.It intermixes well with Java. (和 java 的完美混合) 5.It enforces sensible static typing. (强制静态) 6.It is concise and expressive. (简洁且易于表达的) 7.It is built on a small kernel. (只有一个很小的核心) 8.It is highly scalable, and it takes less code to create high-performing applications. (易于扩展,只用很少的代码即可写出高性能的程序。 type less , do more )
4. 什么是 scala 之 FP Functional Programming 函数式编程 , 和命令式编程相比,函数式编程强调函数的计算比指令的执行重要。和过程化编程相比,函数式编程里,函数的计算可随时调用。函数式编程常被认为严重耗费在 CPU 和存储器资源 1. 函数是头等公民( class 、 object 、 xml ),正如 int 之类的,你可以将函数当做参数来传递或返回,即高阶函数;也可以在函数中定义函数,可以定义匿名函数。 2. 不可变数据结构,也就是说程序的操作是对数值的映射修改而不是改数据,同时任何方法都不容许出现副作用,它们只通过参数和返回值与环境进行交互,应该是引用透明的) 函数编程语言最重要的基础是 λ 演算(又被称为最小的通用程序设计语言 ),它包括一条变换规则(变量替换)和一条函数定义方式,这种演算可以用来清晰地定义什么是一个可计算函数 函数式编程一般会采用递归而不是循环,纯函数编程不含有变量和副作用,所以在并行运算中将有很大的发挥空间,最早的是 Lisp (现在是一个家族),后来有 Haskell 、 Clean 、 Erlang 、 ML (也是一个家族,如 Ocaml )、 F# (基于 Ocaml )和 Miranda 等。
10. 为什么要用 scala 举几个例子: class MyClass( var index: Int, var name: String) 看看生成的代码 import scala.ScalaObject; import scala.reflect.ScalaSignature; @ScalaSignature(bytes= " 略 ..." ) public class MyClass implements ScalaObject{ private int index; private String name; public int index(){ return this .index; } public void index_$eq(int paramInt) { this .index = paramInt; } public String name() { return this.name; } public void name_$eq(String paramString) { this .name = paramString; } public MyClass(int index, String name) { } }
11. 快速入门 val 和 var (类型推断) 不过 var a=null 和 java 不一样,不能再次赋值,因为这时已经被附成了 Null 类(强类型) def Unit ,可认为是 void ,但不同 Type less ,do more 省略 ; 省略 return :但,递归函数必须显示声明返回类型 省略 . 省略括号:注意的是 println 和 groovy 的不同 脚本语言? Ok ,可以当成脚本语言去写然后用 scala xx.scala 执行脚本 数不存在 ++ , -- ,但可以自己定义 任何东西都有返回值
12. 快速入门 数组 Array 不是用 [] 而是用 () 一个数组 Array[String] strs, 则 strs(1) 实际上是 strs.apply(1) strs(1)= "fsda" 实际上是 strs.update(1, "fsda" ) val numNames = Array( "zero" , "one" , "two" ) 实际上仍然是 Array.apply("zero", "one", "two") ,但这里调用的是 Array 的伴随类的 apply fill 、 tabulate 、 range (采用的是 until 策略) if 和 java 一样,必须是 boolean 而不像 groovy 那样 不会强求你是否处理异常,但因为有个异常强大的模式匹配,异常处理也变得很华丽 catch { case ex: FileNotFoundException => // Handle missing file case ex: IOException => // Handle other I/O error }
14. 快速入门 函数文法 : 形如 (x: Int, y: Int) => x + y for 的使用 for (arg <- 0 to 10) println(arg) 没有 for(int i=0;i<10;i++) 而且 for 中的东西都是 val 的 PS : to ,这里是 Range ,不包含右界则是 until range 用 by 定义 step:(10 until 0) by -1 List ::: 组装两个 list 中的元素 :: 加入到 list ,只容许 el::list, 反过来不行 Nil 为空,相当于 List() l(0)=3 是不可以的,因为没有 update , scala 中 List 是不可变的
15. 快速入门 Tuple 从 1 开始 对于一些简单对象就没必要再弄 class 了,直接返回 tuple val pair = (99, "Luftballons" ) println(pair._1) println(pair._2) Set 、 Map map(1-> "fsda" ) 读取文件 import scala.io.Source for (line <- Source.fromFile(args).getLines()) println(line.length +" "+ line)
17. 快速入门 Option 类型( Option type ) , 可选类型,返回不确定类型的时候 非常常用 有两种形式: 1.Some(x),x 就是实际值; 2.None 例子 val capitals =Map( "France" -> "Paris" , "Japan" -> "Tokyo" ) scala> capitals get "France" res23: Option[java.lang.String] = Some(Paris) scala> capitals get "North Pole" res24: Option[java.lang.String] = None 所以这时可以用模式匹配 def show(x: Option[String]) = x match { case Some(s) => s case None => "?" }
18. 快速入门 保留字 abstract case catch class def do else extends false final finally for if implicit import match new null object override package private protected requires return sealed super this throw trait try true type val var while with yield 对比 java abstract break boolean byte case catch char class continue const default do double else extends false final finally float for goto if implements import int interface instanceof long native new null package private protected public return short static super switch synchronized this throw throws transient true try void volatile while 而 groovy abstract, as, assert, boolean, break, byte, case, catch, char, class, const, continue, def, default, do, double, else, enum, extends, false, final, finally, float, for, goto, if, implements, import, in, instanceof, int, interface, long, native, new, null, package, private, protected, public, return, short, static, strictfp, super, switch, synchronized, this, threadsafe, throw, throws, transient, true, try, void, volatile, while
20. 加点难度 类、对象、函数 默认都是 public , var 、 val 、 def 也是 public 的 默认的参数只要不加 var 就是 val 的 不带返回类型和 = 的 def 方法返回都是 Unit object : scala 没有 static ,但提供了 object 来满足这种需要 不可有参数 单例对象: standalone object ,可认为是 final 类 与 class 同名的叫做伴随对象( companion object )对应的类叫做伴随类,它可以与同名类互相访问对方的私有对象 object 会生成一个 $ 的 class ,仍然用原名可访问,但伴随类必须用 Object$.MODULE$ Scala 程序 main 方法:在 object 中定义 main 方法即可 def main(args: Array[String]) java 默认引入了 lang , scala 默认引入了 Predef 及一些其他有用的东西 scala 不用在意文件名之类的,也可以往文件中放任何类或对象 scalac ; scala 脚本; fsc ; scaladoc ; sbaz ; sbt sbaz install scala-devel-docs 可以继承 Application (已 deprecate 了,用 App 代替,这两个都是 trait )
21. 加点难度 基础类型 类型 Byte , Short , Int , Long 和 Char 被称为整数类型: integral type 。整数类型加上 Float 和 Double 被称为数类型: numeric type 。 一个独立的 Boolean 和 java 一一对应,但都有相应的 RichXXX 类型,而且均为 final 类 String 也有 """ ,用 | 来表示一行的起始, stripMargin 方法就可以去掉前面的空格,如 println( """|Welcome to Ultamix 3000. |Type "HELP" for help.""" .stripMargin) 操作符(前中后缀,其实,应该认为没有操作符,全是方法) 任一方法都是操作符,操作符也可以随意重载 从左到右,有一个例外:任何以‘ :’ 字符结尾的方法由它的右手侧操作数调用,并传入左操作数 unary_ 前缀操作符,实际上如: -2.0 // Scala invokes (2.0).unary_- 前缀可用的只有 +, -, !, and ~ 其他基本和 java 一样,但是 == 不一样,相当于 equals , eq 和 ne 方法是 java 的 == 任何包括保留字都可以用反引号当做操作符 `yield` 任何操作符都是方法调用 XX op YY 相当于 XX.op(YY)
22. 加点难度 隐式转换 class Myint(i:Int){ def ++()=i+1 } implicit def intToMyint(i:Int):Myint= new Myint(i) 内建控制结构 if , while ( do-while ), for , try , match 和函数调用 没有 1>2?5:6 函数类语言一般没有循环类结构, while , for 之类的,因为他们不返回值,比如 (line = readLine()) != "" 将永远为真,因为前者返回 Unit 相当于 () ,这个就没法在循环中判断,一般通过递归等方法实现 , 但 scala 不是纯 FP ,所以有
23. 更进一步 之 for 没有 for(int i=0;i<xxx.length;i++) ,但却比这强大 简易数字循环的话 for (i <-(50 to 0) by -2) println(i) 打印文件: for ( file<-(new java.io.File(".")).listFiles ) println(file) 可以附加过滤器,但超过一个过滤器的时候要加上分号 val filesHere = ( new java.io.File( "." )).listFiles for ( file <- filesHere if file.isFile; if file.getName.endsWith( ".scala" ) ) println(file) // 可以使用大括号代替小括号环绕发生器和过滤器。使用大括号的一个好处是你可以省略一些使用小括号必须加的分号
24. 更进一步 之 for 容许内部迭代和 mid-stream (流间)变量绑定 def grep(pattern: String) = for { file <- filesHere if file.getName.endsWith( ".scala" ) line <- fileLines(file) trimmed = line.trim //if line.trim.matches(pattern) if trimmed.matches(pattern) } println(file + ": " + trimmed) grep(".*gcd.*") 可以用 for 创建新的集合,用 yield def scalaFiles = // 返回的结果是 Array[File] for { file <- filesHere if file.getName.endsWith( ".scala" ) } yield file 具体语法是: for { 子句 } yield { 循环体 }
25. 更进一步 之 函数和闭包 函数可以定义成方法: method ,某个对象的成员 本地函数:即把函数定义在函数内部 第一类函数: first-class function ,形如 XX=>YYY (生成的是类) 比如 val increase = (x: Int) => x + 1 // 等号右边就是第一类函数 如果超过一行的话,用大括号包起来 可以使用短格式或者占位符代替参数,比如 filter ( x=>x>1 )或者 filter(_>1) 或者 filter(1<) 甚至可以 val f = (_: Int) + (_: Int) //f(5, 10) 调用 偏应用函数: partially applied function (我个人认为应该叫部分应用函数) def sum(a: Int, b: Int, c: Int) = a + b + c val a = sum _ //=sum 是不行的,这时是定义函数,不可以简略,只有下面 foreach 那种确定要传入的是函数的情况下才可以省略 val b = sum(1, _: Int, 3)
26. 更进一步 之 函数和闭包 闭包 闭包( Closure )是词法闭包( Lexical Closure )的简称,是引用了自由变量(未绑定)的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。最早实现闭包的程序语言是 Scheme 。 举个例子: (x: Int) => x + more more 是个自由变量: free variable ,因为函数文本自身没有给出其含义 ; 相对的, x 变量是一个绑定变量: bound variable ,因为它在函数的上下文中有明确意义 不带自由变量的函数文本,如 (x: Int) => x + 1 ,被称为封闭术语: closed term ,这里术语: term 指的是一小部分源代码 而例子则是是开放术语: open term 如果要是用这个开放术语的例子,则要定义 more 是什么,比如初始化为 0 , 依照这个函数文本在运行时创建的函数值(对象)被称为闭包: closure 。
36. 更进一步 之 特质 trait scala 线性继承算法: 1. 自己; 2. 从右边开始继承,并把每一个所继承的线性继承累计进入线性继承; 3. 从左往右看,把每一个右边已有的继承去掉重复的; 4. 加上 ScalaObject , AnyRef , Any 举个例子: class Human trait Woman extends Human trait Driver extends Human trait FemaleDriver extends Driver trait MarriedWoman extends Woman trait SixFingers extends Human class MarriedSixFingersFemaleDriver extends MarriedWoman with FemaleDriver with SixFingers 最终的结果就是 SF FD D MW W H ( SO AR A )
39. 更进一步 之 case 类和模式匹配 case 类( Case Classes ) Case 类用 case 关键词修饰 1. 会在编译时加入一个工厂方法,不用再 new 出来,可以直接生成。 2. 所有的类参数会加上 val ,也就是自动转变成 field ; 3. 加入一些自然实现的方法 toString,hashCode, and equals ; 4. 自动加入了一个 copy 方法 模式匹配( Pattern Matching ): selector match { alternatives } 而 alternatives 都是从 case 开始,一个模式和一或多个表达式去判断模式是否匹配,还有一个 => 所有 break 都是隐含的(而且没有 break 这个东西)
40. 更进一步 之 模式匹配 通配符匹配 _ 可以通配所有,类似 default ,但是在 pattern 中出现的话就是通配中间的东西了 常量匹配、变量匹配(正则 Regex() 或者 .r ) scala> import math.{E, Pi} import math.{E, Pi} scala> val pi = math.Pi pi: Double = 3.141592653589793 scala> E match { | case `pi` => "strange math? Pi = "+ pi | case _ => "OK" | } res0: java.lang.String = OK scala> Pi match { | case `pi` => "strange math? Pi = "+ pi | case _ => "OK" | } res1: java.lang.String = strange math? Pi = 3.141592653589793 val Dpi = math.Pi Pi match { case Dpi => "strange math? Pi = "+ pi case _ => "OK" }
41. 更进一步 之 模式匹配 构造器匹配、序列化匹配 expr match { case List(0, _, _) => println("found it") //case List(0, _*) => println("found it") case _ => } tuple 匹配 expr match { case (a, b, c) => println("matched "+ a + b + c) case _ => } 类型匹配 x match { case s: String => s.length case m: Map[_, _] => m.size case _ => -1 } 注意的是,类型是消除的,不能用泛型匹配,比如 case m: Map[Int, Int] => true ,但唯一例外的是 Array ,可以泛型匹配
42. 更进一步 之 模式匹配 变量绑定 expr match { case UnOp("abs", e @ UnOp("abs", _)) => e case _ => } 模式保护 e match { case BinOp("+", x, x) => BinOp("*", x, Number(2)) case _ => e } // 编译不过,因为出现了两个 x ,而 scala 的模式要求是线性模式 def simplifyAdd(e: Expr) = e match { case BinOp("+", x, y) if x == y =>BinOp("*", x, Number(2)) case _ => e } 模式保护就是指在模式后以 if 开头的 boolean 表达式
43. 更进一步 之 封闭类 封闭类( Sealed classes )非封闭类在 match 可用 @unchecked 取消 warning sealed abstract class HttpMethod() case class Connect(body: String) extends HttpMethod case class Delete (body: String) extends HttpMethod case class Get (body: String) extends HttpMethod case class Head (body: String) extends HttpMethod case class Options(body: String) extends HttpMethod case class Post (body: String) extends HttpMethod case class Put (body: String) extends HttpMethod case class Trace (body: String) extends HttpMethod def handle (method: HttpMethod) = method match { case Connect (body) => println("connect: " + body) case Delete (body) => println("delete: " + body) case Get (body) => println("get: " + body) case Head (body) => println("head: " + body) case Options (body) => println("options: " + body) case Post (body) => println("post: " + body) case Put (body) => println("put: " + body) case Trace (body) => println("trace: " + body) }
44. 更进一步 之 模式匹配的例子 scala> val myTuple = (123, "abc") myTuple: (Int, java.lang.String) = (123,abc) scala> val (number, string) = myTuple number: Int = 123 string: java.lang.String = abc
45. 更进一步 之 模式匹配的例子 scala> val exp = new BinOp("*", Number(5), Number(1)) exp: BinOp = BinOp(*,Number(5.0),Number(1.0)) scala> val BinOp(op, left, right) = exp op: String = * left: Expr = Number(5.0) right: Expr = Number(1.0) for 也是有模式匹配的 for ((country, city) <- capitals) println( "The capital of " + country + " is " + city)
46. 更进一步 之 模式匹配的例子 scala> val results = List(Some("apple"), None,Some("orange")) results: List[Option[java.lang.String]] = List(Some(apple),None, Some(orange)) scala> for (Some(fruit) <- results)println(fruit) apple orange
47. 更进一步 之 List 和 Array 类似,但是: 1.List 不可变; 2.List 是递归的, Array 是平的 1.List 是同质的( homogeneous ),即 List 中的东西都是同类的 2.List 是协变的( covariant ),举例: S 是 T 的子类,那么 List[S] 是 List[T] 的子类 List() 是 List[Nothing] 构造 List : List 有两个东西构成: 1. Nil ,为空 list ; 2.:: ( cons ),中 缀符,连接了元素(前面)和 list (后面) 注意这三个是类, List 本身是 +T 的 abstract 类,有一个伴随类以用于 初始化 Nil 继承自 List[Nothing] ,由于是协变的,所以适用于所有 List 对象 List(1, 2, 3) // 1 :: (2 :: (3 :: Nil))//1::2::3::Nil //1 :: List(2, 3) = List(2, 3).::(1) = List(1, 2, 3)
48. 更进一步 之 List 所有的对 List 的操作都可以定义到下面的三个: 1.isEmpty :是否为空 2.head : list 中的第一个元素 3.tail : list 除了第一个剩下的元素 这三个方法在 List 中是 abstract 的,被 Nil 和 :: 定义 :: 在模式匹配时,操作符的定义不同, x::xs 是一个特殊中缀操作模式,并不是 x.::(xs) ,而是 ::(x,xs) // 下面的例子是插入排序 留作业自己看吧 def isort(xs: List[Int]): List[Int] = xs match { case List() => List() case x :: xs1 => insert(x, isort(xs1)) } def insert(x: Int, xs: List[Int]): List[Int] = xs match { case List() => List(x) case y :: ys => if (x <= y) x :: xs else y :: insert(x, ys) }
49. 更进一步 之 List ::: 也是右联合的,和 :: 一样,组合 list 内的元素为新 list 实现同样的功能 def append[T](xs: List[T], ys: List[T]): List[T] =xs match { case List() => ys case x :: xs1 => x :: append(xs1, ys) } length xs length// 返回长度,而不是 size(), 由于 list 是递归的,所以用这个方法需要谨慎,因为它的定义 var these = self var len = 0 while (!these.isEmpty) { len += 1 these = these.tail } len 也就是说,为了得到长度需要遍历整个 list ,和 LinkedList 不一样, LinkedList 在 内部有个 size 域
50. 更进一步 之 List init :除了最后一个以外的 list last :最后一个,都不能对 () 使用 ------------------- 这两个也要遍历整个 list------------------- reverse :反转,同时,也是自反的 xs.reverse.reverse equals xs drop :扔掉前 n 个 , take :得到前 n 个 , splitAt :从 n 处砍成两个 list ,当 n 大于长度时,第一个返回 () ,第二个全部 xs splitAt n 相当于 (xs take n, xs drop n) 选择 list 中的元素通过 apply ,也就是 xs(1) 实际上是 xs.apply(1) ,还可以写成 xs apply 1, 复杂度是 n ,所以 list 很适合随机取 indices 可以返回元素的序号,返回的是 Range 类型
51. 更进一步 之 List flatten :扁平化多个 list 成一个,只对 list 本身也是由 list 组成的 list 管用 scala> List(List(1, 2), List(3), List(), List(4, 5)).flatten res14: List[Int] = List(1, 2, 3, 4, 5) zip :将两个 list 组成配对的 list (一般这个配对就是指 Tuple ) scala> val xs=List(1,2,3);val xs1=List('a','b','n') xs: List[Int] = List(1, 2, 3) xs1: List[Char] = List(a, b, n) scala>val xs2= xs zip xs1 xs2: List[(Int, Char)] = List((1,a), (2,b), (3,n)) zipWithIndex :用序号组合 scala> xs1 zipWithIndex res6: List[(Char, Int)] = List((a,0), (b,1), (n,2))
52. 更进一步 之 List unzip : zip 的反例 scala> xs2 unzip res6: (List[Int], List[Char]) = (List(1, 2, 3),List(a, b, n)) toString : scala> xs2 toString res12: String = List((1,d), (2,fsd), (das,fsda)) mkString :组装成需要的 String ,一种是提供三个参数,一种是不提供或一个参数 scala> xs2 mkString " and " res14: String = (1,d) and (2,fsd) and (das,fsda) scala> xs2.mkString("a list:",","," end") res15: String = a list:(1,d),(2,fsd),(das,fsda) end
53. 更进一步 之 List sum 、 product 、 min 、 max iterator, toArray, copyToArray (当然 Array 也有 toList ) xs copyToArray (arr, start) ,把 xs 拷到 arr 从 start 开始的位置,覆盖, arr 是不动的
54. 更进一步 之 List 高阶方法 map : xs map f ,表示着返回一个有 xs 里的每个元素执行一遍 f 的结果的 list val xs=List("fsdaFsdaFsdaFdsa","gfdh","fFDS") scala> xs map (_.length) res25: List[Int] = List(16, 4, 4) for (x <- expr1) yield expr2 就相当于 expr1.map(x => expr2) flatMap :对 list 中的 list 的每个元素进行操作 scala> xs map (_.toList) res26: List[List[Char]] = List(List(f, s, d, a, F, s, d, a, F, s, d, a, F, d, s, a), List(g, f, d, h), List(f, F, D, S)) scala> xs flatMap (_.toList) res27: List[Char] = List(f, s, d, a, F, s, d, a, F, s, d, a, F, d, s, a, g, f, d, h, f, F, D, S)
55. 更进一步 之 List 高阶方法 foreach 每一个进行 f ,返回最终结果永远是 Unit ,也就是说,这里的 f 是一个过程计算,不返回东西 scala> var sum=0;List.range(1,5) foreach (sum+=_) sum: Int = 10 filter :过滤 scala> List.range(1,10) filter (_ %2==1) res34: List[Int] = List(1, 3, 5, 7, 9) partition :和 filter 相似,但会返回两个 list 组成的 pair ,前面是符合的,后面是不符合的 scala> List.range(1,10) partition (_ %2==1) res36: (List[Int], List[Int]) = (List(1, 3, 5, 7, 9),List(2, 4, 6, 8))