教程地址:http://www.guidebee.info/wordpress/%E6%95%99%E7%A8%8B/scala%E5%BC%80%E5%8F%91%E6%95%99%E7%A8%8B
expr match{
case List(0,_*) => print("found it")
case _ =>
}
def tupleDemo(expr:Any)= expr match{
case (a,b,c) => print("matched"+a+":"+b+":"+c)
case _ =>
}
expr match {
case UnOp("abs",e @ UnOp("abs",_)) => e
case _ =>
}
def simplifyAdd(e:Expr) = e match{
case BinOp("+",x,y) if x==y => BinOp("*",x,Number(2))
case _ =>
}
sealed abstract class Expr
case class Var(name:String) extends Expr
case class Number(num:Double) extends Expr
case class UnOp(operator:String,arg:Expr) extends Expr
def describe(e:Expr):String = (e: @unchecked) match{
case Nubmer(_) => "matched"
case Var(_) => "a variable"
}
val myTuple=(123,"345")
var (number,string)=myTuple
val exp=new BinOp("*",Number(5),Number(1))
val BinOp(op,left,right)=exp
val second: List[Int] => Int = {
case x::y::_ => y
}
val second:PartialFunction[List[Int],Int] = {
case x::y::_ => y
}
second.isDefinedAt
val capitals = Map("France"->"Paris")
for((country,city)<-capitals)
println("The capital of"+ country +" is "+ city)
package Object bobsdelights{
def showFruit(fruit:Fruit){
import fruit._
println(name+"s are"+ color)
}
}
package printmunu
import bobsdelights.Fruits
import bobsdelights.showFruit
object PrintMenu{
def main(args:Array[String]){
for(fruit<- Fruits.menu)
showFruit(fruit)
}
}
object Email{
def apply(user:String,domain:String) = user + "@" + domain
def unapply(str:String) : Option[(String,String)] = {
val parts = str split "@"
if(parts.length == 2) Some(parts(0),parts(1)) else None
}
}
object UpperCase {
def unapply(s:String):Boolean = s.toUpperCase ==s
}
def userTwiceUpper(s:String) = s match{
case EMail(Twice(x @ UpperCase()),domain) =>
match:" + x + " in domain " + domain case _ => "no match"
}
object Domain{
def apply(parts:String *) :String = parts.reverse.mkString(".")
def unapplySeq(whole:String): Option[Seq[String]] =
Some(whole.split("\\.").reverse)
}
def isTomDotCom(s:String):Boolean =s match{
case EMail("tom",Domain("com",_*)) => true
case _ => false
}
object ExpendedEMail{
def unapplySeq(email: String)
:Option[(String,Seq[String])] ={
val parts = email split "@"
if(parts.length==2)
Some(parts(0),parts(1).split("\\.").reverse)
else
None
}
}
val s ="james.shen@mail.guidebee.com"
val ExpendedEMail(name,topdomain,subdoms @ _*) =s//赋值也可以用Extractor
我们在前面介绍模式匹配介绍了可以使用如下的方式访问列表的元素:
List()
List(x,y,*)
Array(x,0,0,)
实际上,这些序列模式内部实现都是使用Extractor来定义的。 比如Scala标准库的List的定义具有下面的定义:
package scala{
def apply[T](elems: T*) = elems.toList
def unapplySeq[T](x:List[T]): Option[Seq[T]] =Some(x)
}
val Deciaml = new Regex("""(-)?(\d+)(\.\d*)?""")
val Deciaml = """(-)?(\d+)(\.\d*)?""".r
for(s <- Decimal findAllIn input) println(s)
val Decimal(sign,integerpart,decimalpart) = "-1.23"
和Java相比,Scala的import的使用更加灵活:
可以出现在文件中任何地方
可以import对象(singleton或者普通对象)和package本身
支持对引入的对象重命名或者隐藏某些类型
import java.util.regex //直接引入包
class AStarB {
val pat= regex.Pattern.compile("a*b")
}
import Fruits.{Apple,Orange}
import Fruits.{Apple=>MaIntosh,Orange}
import java.{sql => S}
如果需要隐藏某个类型,可以使用 Type => _ ,将某个类型改名为就达到隐藏某个类型的效果,比如
import Fruits.{Apple=>,_}
这个引用,引入Fruits中除Apple之外的其它类型
class Rational (n:Int, d:Int) extends Ordered[Rational]{
require(d!=0)
private val g =gcd(n.abs,d.abs)
val number=n/g
override def compare(that:Rational)=
(this.number*that.danom)-(that.number*that.denom)
}
此外要注意Ordered Trait 没有定义equal 方法,因为如果需要定义equal方法,那么需要检查传入参数的类型,Ordered Trait无法实现,因此你如果需要==比较运算符,需要另外定义。
trait Rectangular {
def topLeft:Point
def bottomRight:Point
def left =topLeft.x
def right =bottomRight.x
def width=right-left
// and many more geometric methods
}
class Rectangle(val topLeft:Point, val bottomRight:Point) extends Rectangular{
// other methods
}
object TestConsole extends App{
val rect=new Rectangle(new Point(1,1),new Point(10,10))
println (rect.left)
println(rect.right)
println(rect.width)
}
trait Philosophical{
def philosophize() {
println("I consume memeory, therefor I am!")
}
}
class Animal
class Frog extends Animal with Philosophical{//后面可以跟多个with //很像ruby的混入
override def toString="green"
}
trait不能直接定义类
第一点,Trait不能有任何“类”参数,也就是说,传递给类的主构造器的参数。换句话说,尽管你可以定义如下的类:
class Point(x: Int, y: Int)
但下面的Trait定义直接报错:
scala> trait NoPoint(x:Int,y:Int)
<console>:1: error: traits or objects may not have parameters
trait NoPoint(x:Int,y:Int)
类Null代表null引用,它是所有引用类(每个由AnyRef派生的类)的子类。Null和值类型不兼容,也就是比如说,你不能把null赋值给一个整数类型变量:
Nothing类型为图中类层次关系的最下方,它是所有其他类的子类,然而这个类型没有任何实例(也就是没有任何值对应Nothing类型)前面提到,
Nothing类型的一个用法是示意应用程序非正常终止,比如Predef的有一个error方法:
def error(message:String) :Nothing =
throw new RuntimeException(message)
def first(x:Int) = (y:Int) => x + y
val onePlus = curriedSum(1)_
onePlus(2)
val f = (_ :Int ) + ( _ :Int)
someNumbers.filter(_ >0)
(x :Int ) => x +1
(x) => x +1
x => x +1
someNumbers.foreach(println )
someNumbers.foreach( x => println (x))
在Scala中,如果你定义一个部分应用函数并且能省去所有参数,比如println _ ,你也可以省掉“”本身,比如:
someNumbers.foreach(println)
因此这里的“” 代表了Println的整个参数列表,而不仅仅替代单个参数。
当你采用这种方法使用“”,你就创建了一个部分应用的函数(partially applied function)。
def sum = (_:Int) + (_ :Int) + (_ :Int)
一个部分应用的函数指的是你在调用函数时,不指定函数所需的所有参数,这样你就创建了一个新的函数,
这个新的函数就称为原始函数的部分应用函数,比如说我们固定sum的第一和第三个参数,定义如下的部分应用函数:
val b = sum ( 1 , _ :Int, 3)
val c = sum (_:Int, 2, _:Int)
var more =1
val addMore = (x:Int) => x + more
这样定义的函数变量addMore 成为一个“闭包”,因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。
有意思的是,当这个自由变量发生变化时,Scala的闭包能够捕获到这个变化,因此Scala的闭包捕获的是变量本身而不是当时变量的
同样的,如果变量在闭包在发生变化,也会反映到函数外面定义的闭包的值。
如果一个闭包所访问的变量有几个不同的版本,比如一个闭包使用了一个函数的局部变量(参数),然后这个函数调用很多次,那么所定义的闭包应该使用所引用的局部变量的哪个版本呢?
简单的说,该闭包定义所引用的变量为定义该闭包时变量的值,也就是定义闭包时相当于保存了当时程序状态的一个快照。比如我们定义下面一个函数闭包:
scala> def makeIncreaser(more:Int) = (x:Int) => x + more
makeIncreaser: (more: Int)Int => Int
scala> val inc1=makeIncreaser(1)
inc1: Int => Int = <function1>
scala> val inc9999=makeIncreaser(9999)
inc9999: Int => Int = <function1>
scala> inc1(10)
res5: Int = 11
scala> inc9999(10)
res6: Int = 10009
def echo (args: String *) =
for (arg <- args) println(arg)
val arr= Array("What's","up","doc?")
echo (arr: _*)
还有命名参数和缺省参数
def filesContaining( query:String ) =
for (file <-filesHere; if file.getName.contains(query))
yield file
def filesMatching( query:String,
matcher: (String,String) => Boolean) = {
for(file <- filesHere; if matcher(file.getName,query))
yield file
}
def filesEnding(query:String) =
filesMatching(query,_.endsWith(_))
def withPrintWriter (file: File, op: PrintWriter => Unit) {
val writer=new PrintWriter(file)
try{
op(writer)
}finally{
writer.close()
}
}
withPrintWriter(
new File("date.txt"),
writer => writer.println(new java.util.Date)
)
import scala.io._
import java.io._
def withPrintWriter (file: File)( op: PrintWriter => Unit) {
val writer=new PrintWriter(file)
try{
op(writer)
}finally{
writer.close()
}
}
val file = new File("date.txt")
withPrintWriter(file){
writer => writer.println(new java.util.Date)
}
第一个参数我们还是使用()(我们也可以使用{}),第二个参数我们使用{}来替代(),这样修改过的代码使得withPrintWriter 看起来和Scala内置的控制结构语法一样。
传名参数
var assertionsEnabled=true
def myAssert(predicate: () => Boolean ) =
if(assertionsEnabled && !predicate())
throw new AssertionError
myAssert(() => 5 >3 )
符号标志符包含一个或多个符号,如+,:,? 等,比如
+ ++ ::: < ?> :->
Scala 内部实现时会使用转义的标志符,比如:->
使用$colon$minus$greater
来表示这个符号。
因此如果你需要在Java代码中访问:->
方法,你需要使用Scala的内部名称$colon$minus$greater
implicit def intToRational(x:Int) = new Rational(x)
Array(1, 2, 3).toString
"hello".length // 没有副作用,所以无须()
println() // 最好别省略()
和Java稍有不同的一点是,Scala中成员函数和成员变量地位几乎相同,而且也处在同一个命名空间,也就是Scala中不允许定义同名的成员函数和成员变量,
但带来的一个好处是,可以使用成员变量来重载一个不带参数的成员函数。比如,接着前面的例子,你可以通过一个成员变量来实现基类中定义的抽象函数contents.
class ArrayElement(conts: Array[String]) extends Element {
val contents: Array[String] = conts
}
可以看到,这是使用成员变量来实现基类中不带参数的抽象函数的一个非常恰当的例子。 Scala中的这种实现是Java语言所不支持的,
一般来说只有两个不同的命名空间来定义类,而Java可以有四个,Scala支持的两个命名空间如下:
值(字段,方法,包还有单例对象)
类型(类和Trait名)
Scala把字段和方法放进同一个命名空间的理由很清楚,因为这样你就可以使用val重载无参数的方法,
使用Val默认为公有
var默认为私有
使用val定义了一个无法重新赋值的成员变量。这个成员变量初始值为参数的值,可以在类的外面访问这个成员变量。它的一个等效的实现如下:
class ArrayElement(val x123: Array[String])
extends Element {
val contents: Array[String] = x123
}
Scala 也允许你使用var关键字来定义参数化成员变量,使用var定义的成员变量,可以重新赋值。
此外Scala也允许你使用 private, protected ,override来修饰参数化成员变量,和你定义普通的成员变量的用法一样。 比如:
class Cat {
val dangerous =false
}
class Tiger (
override val dangerous: Boolean,
private var age: Int
) extends Cat
这段代码中Tiger的定义其实为下面类定义的一个缩写:
class Tiger(param1: Boolean, param2: Int) extends Cat {
override val dangerous = param1
private var age = param2
}
两个成员都初始化自相应的参数。我们任意选择了这些参数名,param1和param2。重要的是它们不会与范围内的任何其它名称冲突。
调用基础类的构造函数
class LineElement(s:String) extends ArrayElement(Array(s)) {
override def width = s.length
override def height = 1
}
在前面的例子LineElement使用了override来修饰width和height成员变量,在Scala中需要使用
override来重载父类的一个非抽象成员,实现抽象成员无需使用override,如果子类没有重载父类中的成员,不可以使用override修饰符。
scala继承时和Java一样是动态绑定的
final定义方法则不可以重载,final修饰类则不可以继承、
工厂方法
object Element {
private class ArrayElement(val contents: Array[String])
extends Element {
}
private class LineElement(s:String) extends ArrayElement(Array(s)) {
override def width = s.length
override def height = 1
}
private class UniformElement (ch :Char,
override val width:Int,
override val height:Int
) extends Element{
private val line=ch.toString * width
def contents = Array.fill(height)(line)
}
def elem(contents: Array[String]):Element =
new ArrayElement(contents)
def elem(chr:Char, width:Int, height:Int) :Element =
new UniformElement(chr,width,height)
def elem(line:String) :Element =
new LineElement(line)
}
捕获异常
import java.io.FileReader
import java.io.FileNotFoundException
import java.io.IOException
try {
val f = new FileReader("input.txt")
} catch {
case ex: FileNotFoundException => //handle missing file
case ex: IOException => //handle other I/O error
}
网友评论