函数相当于Java中的静态方法:除了递归之外不需要声明返回类型
def as(x:Double)={if(x>=0) x else -x}
def fac(n:Int):Int={if(n<=0) 1 else fac(n-1)}
注意:一,函数声明中并没有声明变量是否可变,函数的参数默认声明是不可变,也就是val。
二,不写没有返回值的方法必须用大括号括起来
1.除了递归函数以外,函数的返回类型都不用写,这是因为递归函数的时候只有在运行的时候才能知道返回类型是什么,这显然是不行的,scala只能在代码编译的时候做类型推测。
2.当函数仅仅只有一条语句时不用加大括号
在scala中,语句块的最后一个值就是这个语句块的值,因此函数并不使用return来返回值,而是简单的把结果放到最后一条语句
1.如果一个方法仅仅有一个参数,那么就可以不用点号和括号来引用,使用空格分割即可,注意这种方法仅仅适用于类实例方法,并不能在函数中使用
2.如果函数仅有一个参数,那么可以使用大括号来代替圆括号。如果使用柯里化,那么所有的函数都可以只有一个参数,这种情况下最后最后一个参数列表才可以这么做。
def max(x:Any)(y:Any)=if(x>=y) x else y
max(1){2}//2
apply和update
apply:可以为一个类的伴生对象定义一个apply方法,相当于一个静态构造方法,定义在类的伴生对象中。可以让我们不加new来产生对象
var c = Array[Int](10)
也可以给类定义一个对象方法apply
var string = c(1)//可以看到把圆括号翻译成apply方法
update:可以为一个类定义一个update方法:f(arg1,arg2) = value,相当于f.update(arg1,arg2,value)
var score = Map()
score("Bob")=12
map
map接受一个处理单个元素的函数,map把这个函数运用在列表的每个元素上放回一个一样长的列表
filter
filter接受一个返回boolean的的值,filter会取出得到true的元素,返回过滤过的列表
foreach
类似于map
mkString
相当于toString,但是需要一个分隔符
zip
zip把两个列表的元素依次结合得到一个元祖集合
reduce
reduceLeft接受一个接受两个参数的函数,reduceLeft会依次把前一次得到的结果和下一个列表元素传递给函数,最后得到一个单个元素
fold
foldLeft和foldRight接受一个需要两个参数的函数,并且依层调用如(1::2::3).foldLeft(1)(_*_)=6
以层次话的方式求值
increase = (x: Int) => {
println("We")
println("are")
println("here!")
x + 1
}
increase = (x: Int) => x + 9999
注意简写只能用在向其他函数传递匿名函数时,由于简写利用的是自动推测,因此在其他情况下,编译器没有足够的信息来推测。
1.去掉类型声明
(x,y)=>x+y
2.当只有一个参数时可以去掉圆括号
x=>x+100
3.如果参数在表达式中仅仅出现一次,可以用下划线来表示,这时候就没必要再前面写明参数列表,因为编译器会把参数一次填入下划线的位置,注意有几个下划线就有几个参数
list.reduce(_+_)//两个下划线表示有两个不同的参数
list.forEach(print _)
事实上参数推测的能力更大
def filesMatching(query: String,matcher: (String, String) => Boolean) = {
for (file
if matcher(file.getName, query))
yield file
}
filesMatching(query, _.endsWith(_))
filesMatching(query, _.contains(_))
filesMatching(query, _.matches(_))
someNumbers.foreach((x: Int) => println(x))
val a = sum _
increase = (x: Int) => x + 9999
val b = sum(1, _: Int, 3)
b(2)
//对于类的偏函数
val c = new Rational(1,_:Int)
val vb = c(2)
vb.toString
1.闭包会在每次外层函数调用的时候重新产生,就好像他们分别有两份一样,事实上可以在每次调用时把闭包函数所使用的变量绑定得到一个函数。
2.闭包语法:scala中不允许返回函数名,但是可以返回偏函数.
def makeIncreaser(more: Int) ={
def other(x:Int)=x+more
val addMore = other _
addMore
}
或者返回匿名函数
def makeIncreaser(more: Int) ={
val addMore = (x:Int)=>{x+more}
addMore
}
注意scala只允许最后一个参数可变
def echo(args: String*) =for (arg
事实上args是一个Array,但是用这样的语法使得调用时,可以用多个参数调用
echo("hello", "world!")
但是不可以直接传递一个Array
尾递归是指一个递归的递归语句出现在最后一个地方,注意不能有其他的语句,如下不可以。
def boom(x: Int): Int =
if (x == 0) throw new Exception("boom!")
else boom(x-1)+ 1//最后的语句是一个加法
尾递归在最后递归的地方并没有创建一个新的栈,而是跳转到函数的开头,因此可以大幅度的减小递归调用的开销。
可以接受函数或者返回函数的函数叫做高阶函数。
def filesMatching(query: String,matcher: (String, String) => Boolean) = {
for (file <- filesHere;if matcher(file.getName, query)) yield file
}
把原来需要接受两个参数的函数变成两个接受一个参数的函数,新的函数接受一个参数并返回一个接受一个参数的函数
def mul(x:Inr,y:Int)={x*y}
def mul(x:Int)=(y:Int)=>x*y
(仅仅scala支持的简写)
def mul(x:Int)(y:Int)={x*y}
1.在只有一个参数的函数中可以用大括号代替圆括号,因此函数更像控制结构
2.控制结构简化后如下if(experssion*){experssion*},可以看做吧一个用两个参数的函数柯里化后的结果
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)
}
3.由于这种使用非常不方便,每次使用控制结构时都需要传递一个匿名函数,而且定义非常奇怪。因此scala引入了换名参数,在传统的控制结构中,后面的一部分可以看做一个无参有一个返回值的函数。声明如下:op:()=>Boolean,那么使用时就必须为()=>experssion。而换名参数把声明时的()换成了空格:op: =>Boolean,那么使用时就可以仅仅写expersion,这样就更加像传统的控制结构。同时,更重要的是,如果声明成函数。那么,由于声明和使用的地方不一致,也就不能在调用时使用本地变量。而换名参数正好克服了这种情况,因为他相当于在使用的地方声明,也就可以使用本地变量
在这种情况下,使用资源的函数不能控制资源,他只是声明需要一个资源,然后在产生资源的地方把资源借给他,使用之后,由产生资源的一方负责释放资源。因此资源从来没有赤裸裸地出现在程序中,保证了资源一定会得到释放。
def withPrintWriter(file: File, op: PrintWriter => Unit) {
val writer = new PrintWriter(file)
try {
op(writer)
} finally {
writer.close()
}
}
这儿的withPrintWrite把file资源借给op函数,op函数仅仅使用,没有管理资源的权限。
在类声明前面加上case关键字的叫做样例类
1.每一个样例类都会有一个默认的apply方法
2.样例类中主构造器中的属性都会产生字段。
3.样例类添加了toString,hashcode和equal方法
4.样例类还实现了一个copy方法,用于产生一个新对象
val other = op.copy(operator="-",left=v,rigth=v)
def simplifyTop(expr: Expr): Expr =
expr match {
case UnOp("",UnOp("",e)) => e // Double negation
case BinOp("+", e, Number(0)) => e // Adding zero
case BinOp("*", e, Number(1)) => e // Multiplying by one
case _ => expr
}
1.在这儿不仅仅有匹配还有变量的赋值,e就会在模式匹配的时候来得到变量的值
2.匹配还允许进行嵌套。
3._是通用匹配,其实就是这个结果一般都是无视或者抛出异常。
def describe(x: Any) =
x match {
case 5 => "five"
case true => "truth"
case "hello" => "hi!"
case Nil => "the empty list"
case _ => "something else"
}
expr match {
case 0 => "zero"
case somethingElse => "not zero: "+ somethingElse
}
注意:
1.scala仅允许每个变量在匹配中被赋值一次
expr match {
case BinOp("+", e, Number(0)) => println("a deep match")
case _ =>
}
这儿的类必须是样例类。
数组匹配
arr match{
case Array(0) => "0"
case Array(x,y) => x+" "+y
case Array(0,_*) => "0..."
case _ => something else
}
列表匹配
lst match {
case 0 :: Nil => "0"
case x::y::Nil => x+" "+y
case 0::tail => "0..."
case _ => "something "
}
元组匹配
pair match{
case (0,_) => "0 ..."
case (y,0) => y + "0"
case _ => "neither is 0"
}
def generalSize(x: Any) = x match {
case s: String => s.length
case m: Map[_, _] => m.size
case _ => 1
}
1.使用类型匹配可以同时实现类型检测和类型转化,因此在scala中要使用模式匹配来
2.由于java的类型擦除,故不能精确匹配泛型的类型
// match only positive integers
case n: Int if 0 < n => ...
// match only strings starting with the letter ‘a’
case s: String if s(0) == 'a' => ...
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
case class BinOp(operator: String,left: Expr, right: Expr) extends Expr
密封类仅允许同文件的类检测它,因此不用担心在模式匹配的时候出现未匹配的问题
val exp = new BinOp("*", Number(5), Number(1))
val BinOp(op, left, right) = exp
注意这儿的类必须是样例类