基础
- 函数是一等公民
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | val squareVal = (a: Int) => a * a
def addOne(f: Int => Int, arg: Int) = f(arg) + 1
println("squareVal(2):" squareVal(2)) println("addOne(squareVal,2):" + addOne(squareVal, 2))
// squareVal(2):4 // addOne(squareVal,2):5 /******************************************************/ import scala.reflect.io.File import java.util.Scanner def withScanner(f: File, op: Scanner => Unit) = { val scanner = new Scanner(f.bufferedReader) try { op(scanner) } finally { scanner.close() } } withScanner(File("/proc/self/stat"), scanner => println("pid is " + scanner.next()))
|
- 按名称传递参数
1 2 3 4 5 6 7 8 9
| val logEnable = false
def log(msg: => String) = // 多了"=>"符号,按值传递变为按名称传递 if (logEnable) println(msg)
val MSG = "programing is running"
log(MSG + 1 / 0) //没有除零的异常,参数到用的时候才计算,所以异常被跳过了 |
- 定义类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Persion(val firstName: String, val lastName: String) {
private var _age = 0 def age = _age def age_=(newAge: Int) = _age = newAge
def fullName() = firstName + " " + lastName
override def toString() = fullName() }
val obama: Persion = new Persion("Barack", "Obama")
println("Persion: " + obama) println("firstName: " + obama.firstName) println("lastName: " + obama.lastName) obama.age_=(51) println("age: " + obama.age)
|
- 鸭子类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def withClose(closeAble: { def close(): Unit }, op: { def close(): Unit } => Unit) { //使用{def close():Unit}作为参数,任何含有close()函数的类都可以作为参数 try { op(closeAble) //第二个参数被调用,接着第一个参数被调用 } finally { closeAble.close() } }
class Connection { def close() = println("close Connection") }
val conn: Connection = new Connection() withClose(conn, conn => println("do something with Connection"))
|
- 柯里化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | def withClose(closeAble: { def close(): Unit }) (op: { def close(): Unit } => Unit) { try { op(closeAble) } finally { closeAble.close() } }
class Connection { def close() = println("close Connection") }
val conn: Connection = new Connection() withClose(conn)(conn => println("do something with Connection"))
/****************************************/ def add(x:Int, y:Int) = x + y //普通函数 def add(x:Int) = (y:Int) => x + y //柯里化的函数 def add(x:Int)(y:Int) = x + y //简化的写法
|
- 泛型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | def withClose[A <: { def close(): Unit }, B](closeAble: A) (f: A => B): B = try { f(closeAble) } finally { closeAble.close() } class Connection { def close() = println("close Connection") } val conn: Connection = new Connection() val msg = withClose(conn) { conn => { println("do something with Connection") "123456" //123456 } }
println(msg)
|
- Traits
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | trait ForEachAble[A] { def iterator: java.util.Iterator[A] def foreach(f: A => Unit) = { val iter = iterator while (iter.hasNext) f(iter.next) } }
trait JsonAble { def toJson() = scala.util.parsing.json.JSONFormat.defaultFormatter(this) } // 使用with关键字混入foreach和toJson的功能 val list = new java.util.ArrayList[Int]() with ForEachAble[Int] with JsonAble list.add(1); list.add(2)
println("For each: "); list.foreach(x => println(x)) println("Json: " + list.toJson())
|
函数式
- 模式匹配
1 2 3 4 5 6 7 8 9 10 | def fibonacci(in: Any): Int = in match { case 0 => 0 case 1 => 1 case n: Int if(n>1) => fibonacci(n - 1) + fibonacci(n - 2) // if语句排除负数 case n: String => fibonacci(n.toInt) //匹配String类型 case _ => 0 //匹配所有情况 } println(fibonacci(3)) println(fibonacci(-3)) println(fibonacci("3"))
|
- Case Class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | abstract class Expr
case class FibonacciExpr(n: Int) extends Expr { require(n >= 0) }
case class SumExpr(a: Expr, b: Expr) extends Expr
def value(in: Expr): Int = in match { case FibonacciExpr(0) => 0 case FibonacciExpr(1) => 1 case FibonacciExpr(n) => value(SumExpr(FibonacciExpr(n - 1), FibonacciExpr(n - 2))) case SumExpr(a, b) => value(a) + value(b) case _ => 0 } println(value(FibonacciExpr(3)))
|
- 函数式例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| val list = List(1, 2, 3, 4)
def containsOdd(list: List[Int]): Boolean = { for (i <- list) { if (i % 2 == 1) return true; } return false; } println("list contains Odd ? " + containsOdd(list)) println("list contains Odd ? " + list.exists((x:Int) => x%2==1)) println("list contains Odd ? " + list.exists(_%2 == 1))
/*********************************************************/ val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
println("cat file | grep 'warn' | grep '2013' | wc : " + file.filter(_.contains("warn")).filter(_.contains("2013")).size)
/******************map and reduce******************************/ val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
def wordcount(str: String): Int = str.split(" ").count("msg" == _)
val num = file.map(wordcount).reduceLeft(_ + _)
println("wordcount:" + num) |
- 尾递归
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
def wordcount(str: String): Int = str.split(" ").count("msg" == _)
def foldLeft(list: List[Int])(init: Int)(f: (Int, Int) => Int): Int = { list match { case List() => init case head :: tail => foldLeft(tail)(f(init, head))(f) } }
val num = foldLeft(file.map(wordcount))(0)(_ + _)
println("wordcount:" + num) //wordcount:4
|
- 更强大的for循环
1 2 3 4 5 6 7 8 9 10 11 12 13 | val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
def wordcount(str: String): Int = str.split(" ").count("msg" == _)
val counts = for (line <- file) yield wordcount(line)
val num = counts.reduceLeft(_ + _)
println("wordcount:" + num) //wordcount:4
|
- Option
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | def getProperty(name: String): Option[String] = { val value = System.getProperty(name) if (value != null) Some(value) else None }
val osName = getProperty("os.name")
osName match { case Some(value) => println(value) case _ => println("none") } //Linux
println(osName.getorElse("none")) //Linux
osName.foreach(print _) //Linux
|
- Lazy初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class ScalaCurrentVersion(val url: String) { lazy val source= { println("fetching from url...") scala.io.source.fromURL(url).getLines().toList } lazy val majorVersion = source.find(_.contains("version.major")) lazy val minorVersion = source.find(_.contains("version.minor")) }
val version = new ScalaCurrentVersion( "https://raw.github.com/scala/scala/master/build.number") println("get scala version from " + version.url) version.majorVersion.foreach(println _) version.minorVersion.foreach(println _)
|
并发
- 使用Actor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | import akka.actor.{ Actor, ActorSystem, Props }
val system = ActorSystem() //开始
class EchoServer extends Actor { //继承 def receive = { //接受消息 case msg: String => println("echo " + msg) } }
val echoServer = system.actorOf(Props[EchoServer]) //创建 echoServer ! "hi" //发送
system.shutdown //结束
/************************Actor更简化的用法********************************/ import akka.actor.ActorDSL._ import akka.actor.ActorSystem
implicit val system = ActorSystem()
val echoServer = actor(new Act { become { case msg => println("echo " + msg) } }) echoServer ! "hi" system.shutdown
|
- Actor原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import akka.actor.{ Actor, Props, ActorSystem } import akka.testkit.CallingThreaddispatcher
implicit val system = ActorSystem()
class EchoServer(name: String) extends Actor { def receive = { case msg => println("server" + name + " echo " + msg + " by " + Thread.currentThread().getName()) } }
val echoServers = (1 to 10).map(x => system.actorOf(Props(new EchoServer(x.toString)) .withdispatcher(CallingThreaddispatcher.Id))) (1 to 10).foreach(msg => echoServers(scala.util.Random.nextInt(10)) ! msg.toString)
system.shutdown |
Actor比线程轻量。在Scala中可以创建数以百万级的Actor。奥秘在于Actor直接可以复用线程。
Actor和线程是不同的抽象,他们的对应关系是由dispatcher决定的。
这个例子创建4个Actor,每次调用的时候打印自身线程名称。
可以发现Actor和线程之间没有一对一的对应关系。一个Actor可以使用多个线程,一个线程也会被多个Actor复用。
- 同步返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import akka.actor.ActorDSL._ import akka.pattern.ask
implicit val ec = scala.concurrent.ExecutionContext.Implicits.global implicit val system = akka.actor.ActorSystem()
val versionUrl = "https://raw.github.com/scala/scala/master/starr.number"
val fromURL = actor(new Act { become { case url: String => sender ! scala.io.source.fromURL(url) .getLines().mkString("\n") } })
val version = fromURL.ask(versionUrl)(akka.util.Timeout(5 * 1000)) version.foreach(println _)
system.shutdown |
Actor非常适合于较耗时的操作。比如获取网络资源。
在Actor内部通过 sender ! 传递结果。
Future像Option一样有很多高阶方法,可以使用foreach查看结果。
- 异步返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | import akka.actor.ActorDSL._ import akka.pattern.ask
implicit val ec = scala.concurrent.ExecutionContext.Implicits.global implicit val system = akka.actor.ActorSystem()
val versionUrl = "https://raw.github.com/scala/scala/master/starr.number"
val fromURL = actor(new Act { become { case url: String => sender ! scala.io.source.fromURL(url) .getLines().mkString("\n") } })
val version = fromURL.ask(versionUrl)(akka.util.Timeout(5 * 1000)) version onComplete { case msg => println(msg); system.shutdown } |
异步操作可以最大发挥效能。Scala的Futrue很强大,可以异步返回。
可以实现Futrue的onComplete方法。当Futrue结束的时候就会回调。
在调用ask的时候,可以设定超时。
- 并行集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | val urls = List("http://scala-lang.org", "https://github.com/yankay/scala-tour")
def fromURL(url: String) = scala.io.source.fromURL(url) .getLines().mkString("\n")
val t = System.currentTimeMillis() //urls.map(fromURL(_)) urls.par.map(fromURL(_)) //并发执行 println("time: " + (System.currentTimeMillis - t) + "ms")
/********************************************************/ val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
def wordcount(str: String): Int = str.split(" ").count("msg" == _)
val num = file.par.map(wordcount).par.reduceLeft(_ + _) // 并发执行
println("wordcount:" + num)
|
- 远程Actor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import akka.actor.{ Actor, ActorSystem, Props } import com.typesafe.config.ConfigFactory
implicit val system = akka.actor.ActorSystem("RemoteSystem", ConfigFactory.load.getConfig("remote")) class EchoServer extends Actor { def receive = { case msg: String => println("echo " + msg) } }
val server = system.actorOf(Props[EchoServer], name = "echoServer")
val echoClient = system .actorFor("akka://RemoteSystem@127.0.0.1:2552/user/echoServer") echoClient ! "Hi Remote"
system.shutdown |
Actor是并发模型,也使用于分布式。
这个例子创建一个Echo服务器,通过actorOf来注册自己。
然后再创建一个client,通过akka url来寻址。
除了是通过url创建的,其他使用的方法和普通Actor一样。
实践
- 使用Java
1 2 3 4 5 6 7 8 | import org.apache.commons.beanutils.BeanUtils import scala.beans.BeanProperty
//用@BeanProperty注解来生成Java Style的Bean class SimpleBean(@BeanProperty var name: String) { } val bean = new SimpleBean("foo") println(BeanUtils.describe(bean))
|
1 2 3 4 5 6 7 8 9 10 11 12 | class Person(val name: String) { override def equals(other: Any) = other match { case that: Person => name.equals(that.name) case _ => false } }
case class Test(val name:String, val age:Int){ // case类自动生成正确的equals方法 }
println(new Person("Black") == new Person("Black")) println(new Test("Black",12) == new Test("Black",12))
|
- 抽取器
1 2 3 4 5 6 7 8 9 10 11 12 13 | import scala.util.matching.Regex
object Email { def unapply(str: String) = new Regex("""(.*)@(.*)""") .unapplySeq(str).get match { case user :: domain :: Nil => Some(user, domain) case _ => None } }
"user@domain.com" match { case Email(user, domain) => println(user + "@" + domain) } |
抽取器可以帮助模式匹配进行解构。