看完了Martin Odersky的《Programming in Scala》。作者是写java编译器的大牛,技术够强,书写的一般,我个人感觉如果是被“scala是更好的java”这句传言吸引而来的的java程序员,恐怕会很快被吓跑----scala的代码跟java代码实在是太不一样。反而如果之前学过haskell这样的纯函数式编程语言,学scala不会有太多障碍。我个人建议想学scala的程序员先看完learnyourhaskellforgreatgood ,对函数式编程有个纯粹的认识后再来看scala在java虚拟机上能实现到什么程度。
我写这篇博客记录一下我觉得对java程序员来说比较需要掌握的scala编程要素
- scala解释器
下载安装装好scala,把bin加入path, 然后可以用文本文件编写scala脚本,在命令行下运行scala -i filepath加载脚本文件,然后就可以在scala提示符scala>里面“玩弄”你写在脚本里的函数,对象等等
修改了文件后可以在scala>里面输入:l filepath来加载
用好解释器对学习scala非常有帮助,所以特别说一下 - 头等函数(first-class function)
函数跟类一样是顶级元素,所以可以直接在代码里定义函数,不需要放在类里作为“方法”def sum(x:Int, y:Int, z:Int) :Int = { x + y + z }
scala>sum(1,2,3)
res0: Int = 6
头等函数跟变量一样可以传递,可以被赋值(scala里需要以偏函数的形式,haskell可以直接赋值)
scala>val s = sum _ //sum函数赋值给变量s
scala>s(1,2,3)
res: Int = 6 - 函数字面值
可以不给函数起名字,直接把函数定义赋值给变量scala> val s2 = (x:Int, y:Int ,z:Int) => x + y + z s2: (Int, Int, Int) => Int = <function3> scala> s2(1,2,3) res1: Int = 6
有必要认识函数的这种写法,因为会有助于看懂scala的库里的接口,比如List类里的filter方法def filter (p: (A) ⇒ Boolean): List[A] 这个函数接受一个谓词(其实就是一个函数)p,这个p无所谓名字,只要是能够接受一个元素作为参数,返回Boolean型就行了 scala> val l1 = List(1,2,3,4,5) l1: List[Int] = List(1, 2, 3, 4, 5) scala> l1.filter(x => x > 3) //过滤出大于三的结果,返回到新集合里 res4: List[Int] = List(4, 5) scala> l1.filter(x => x % 2 == 0) //过滤出偶数 res7: List[Int] = List(2, 4)
- 占位符和偏函数(partially applied function,也有翻译为部分应用函数的)
占用符,就是_, 看scala的代码会看到大量的_,
有些地方这个_的意思跟*一样作为通配符,只是scala里*不是内置语法元素,是可以用来做方法名和函数名的,以便用户可以方便开发自己的DSLscala> def *(x:Int, y:Int) = x * y //用*作为函数名 $times: (x: Int, y: Int)Int scala> *(2,3) res8: Int = 6
scala> import scala.collection._import scala.collection._ 这个就是跟*一样的效果了
更重要的作用是用做偏函数的占位符,比较给力scala> def sum(x:Int, y:Int, z:Int):Int = x + y + z sum: (x: Int, y: Int, z: Int)Int scala> val s = sum(2, 3, _:Int) 在这里,给sum函数传入2,3作为前两个参数,但是不传入第3个函数,而是传入一个占位符,于是产生了只接受一个参数的函数s s: (Int) => Int = <function1> //函数的签名变成了一个参数 scala> s(4) // 2 + 3 + 4 res9: Int = 9 这个东东就是所谓的偏函数(部分应用函数)
占位符的应用可以很灵活,可以占任何位置scala> def say(greeting:String, title:String, name:String) = println(greeting + "," + title + " " + name) say: (greeting: String, title: String, name: String)Unit scala> val callNotyy = say(_:String, "Mr", _:String) callNotyy: (String, String) => Unit = <function2> scala> callNotyy("hello","notyy") hello,Mr notyy
- 模式匹配
用函数式编程的风格写代码首先要适应的就是“模式匹配”,先来个简单的例子def sayInt(i:Int): String = i match { case 1 => "one" case 2 => "two" case 3 => "three" case _ => "unknown int" //_作为通配符,提供一个默认case } scala> sayInt(1) res0: String = one scala> sayInt(2) res1: String = two scala> sayInt(4) res4: String = unknown int 从最基本的概念来说和switch很像,但是灵活的多,上例只是最简单的常量模式匹配。
模式匹配机制可以匹配各种“东西”,比如说匹配一个列表中的元素:def zeroStartList(l:List[Int]) = l match { case List(0, _, _) => println("found it") //匹配3个元素的列表,并且第一个元素是0 case _ => println("not found") } scala> zeroStartList(List(0,1,2)) found it scala> zeroStartList(List(1,1,2)) not found
还可以匹配类型def size(x: Any) = x match { case s: String => s.length case m: Map[_, _] => m.size case _ => 1 } scala> size("abc") res7: Int = 3 scala> size(Map(1 -> 'a', 2->'b')) res11: Int = 2
- case class与模式匹配还有守卫(guard)
构造器模式匹配是模式匹配的真正强大之出,但是需要引入样本类(case class)的概念case class User(name:String, age:Int, position:String) def sayHello(user:User) = user match { case User(name, _, "admin") => println("hello,admin " + name) case User(_, _, _) => println("how do you get here! guards!!") } 这个模式匹配可以理解为“形如”,意会吧,看到这里,上面两行代码应该不难理解吧 scala> sayHello(User("notyy",35,"admin")) hello,admin notyy scala> sayHello(User("badguy",35,"thief")) how do you get here! guards!!
构造器模式可以深层匹配,稍微改下代码:case class Email(name:String,domain:String) case class User(name:String, age:Int, position:String, email:Email) def sayHello(user:User) = user match { case User(name, _, "admin",Email(_, "notyy.iteye.com")) => println("hello,admin " + name) case User(_, _, _, _) => println("how do you get here! guards!!") } 这里我们不仅匹配User里的内容,还匹配到User里的Email里的内容 scala> sayHello(User("notyy",35,"admin",Email("notyy","somedomain.com"))) how do you get here! guards!! scala> sayHello(User("notyy",35,"admin",Email("notyy","notyy.iteye.com"))) hello,admin notyy
我们再来结合guards,以便对参数进行更细致的匹配:我们改下上面的代码,对年龄不满18的用户进行限制def sayHello(user:User) = user match { case User(name, age, _, _) if age < 18 => println("you are too young to come here," + name + " ;-) ,guards!") case User(name, _, "admin",Email(_, "notyy.iteye.com")) => println("hello,admin " + name) case User(name, _, _, _) => println("how do you get here!" + name + ", guards!!") } scala> sayHello(User("notyy",17,"admin",Email("notyy","notyy.iteye.com"))) you are too young to come here ;-) ,guards!
- for 表达式 for循环要拿出来说一下。如果是从纯函数式编程语言如haskell转过来,会知道函数式编程语言里一般木有循环的。代之的是递归。更常用的是一些通用的循环函数框架,foreach, filter, map等
val users = List( User("tom", 17, "admin", Email("tom", "notyy.iteye.com")), User("notyy", 35, "admin", Email("notyyy", "notyy.iteye.com")), User("badguy", 35, "admin", Email("badguy", "badguys.com")) ) scala> users.foreach(sayHello) you are too young to come here,tom ;-) ,guards! hello,admin notyy how do you get here!badguy, guards!! List类里的foreach方法的定义是 def foreach (f: (A) ⇒ Unit): Unit [use case] Applies a function f to all elements of this list 这方法的意思就是对集合的每个元素应用传入进来的函数f这是我喜欢的编码风格,不过喜欢for循环的也可以用scala里的for表达式
for (user <- users) sayHello(user)还可以在for里面结合模式匹配。。。
for (User(name,_ ,pos ,_) <- users) println("name=" + name + ",pos=" + pos)如此强力。。。
- scala的特性太多,语法糖多的有点甜腻,写完此文总算记住了一些,接下来研究liftweb框架,用上面那些基础基本上可以看下去了。《scala编程》这本书作为入门书虽然是艰深了点,作为参考手册倒是相当不错的,可以常备案头常翻翻