最常见的是object内部的成员函数,直接通过object名称调用
此外Scala还有内嵌在函数中的函数,函数字面量和函数值
函数嵌套 Nested Functions
可以在函数内部嵌套定义别的函数,叫做局部函数local function,只在包含它的代码块中可见,可以访问包含函数的参数
def factorial(i: Int): Int = {
def fact(i: Int, accumulator: Int): Int = {
if (i <= 1)
accumulator
else
fact(i - 1, i * accumulator)
}
fact(i, 1)
}
匿名函数Anonymous Functions
也叫作叫做函数字面量function literals,Scala函数是头等函数first class function,可以用匿名函数字面量作为值传递
函数字面量被编译到类中,并在运行时实例化为函数值function value,常常用作高阶函数的输入
实质
每个函数值都是某个扩展scala
包中FunctionN
特质 类的实例,每个FunctionN
特质都有一个用于调用函数的apply
方法
object Main extends App {
val succ = (x: Int) => x + 1
val anonfun1 = new Function1[Int, Int] {
def apply(x: Int): Int = x + 1
}
assert(succ(0) == anonfun1(0)) // succ匿名类是anonfun1的简写
}
scala> val add = (a: Int, b: Int) => a + b
add: (Int, Int) => Int =
scala> var increase = (x: Int) => x + 1
increase: Int => Int =
简化
当赋值给高阶函数的时候
可以省略参数类型,通过类型推断得到 (x) => x + 1
参数只有一个的时候,可以去掉输入参数的括号x => x + 1
值函数参数在右边只出现一次,可以使用占位符_ + 1
someNumbers.filter((x: Int) => x > 0)
someNumbers.filter((x) => x > 0) // 目标类型化 target typing,可以使用目标类型推测表达式的类型
someNumbers.filter(x => x > 0) //如果参数可以推测,省略圆括号
占位符
把下划线当做一个或多个参数(只要参数在函数字面量内只出现一次)的占位符,进一步简写函数字面量
someNumbers.filter(x => x > 0)
//等价于
someNumbers.filter(_ > 0)
多个下划线指代的是多个参数,不是单个参数的重复使用
val add = (_: Int) + (_: Int) //(a: Int, b: Int) => a + b
var increment=(_:Int)+1
部分应用函数 Partially Applied Functions
部分应用函数是一种表达式,不需要提供函数需要的所有参数,只需要提供部分,或不提供所需参数
例如:
//定义函数
scala> def sum(a: Int, b: Int, c: Int) = a + b + c
sum: (a: Int, b: Int, c: Int)Int
//通过部分应用表达式 sum _ 自动生成类,实例化为函数值并赋值
scala> val a = sum _
a: (Int, Int, Int) => Int =
//调用函数值的apply方法
scala> a(1, 2, 3)
res11: Int = 6
提供部分参数,Scala编译器生成一个新的函数类,其apply
方法对应缺失的参数
scala> val b = sum(1, _: Int, 3)
b: Int => Int =
用下划线表示整个参数列表,可以理解为把def
函数转换为函数值,因为不能把成员函数或者嵌套函数直接赋值给变量或者是函数参数,但函数值是可以直接赋值的
someNumbers.foreach(println _) // someNumbers.foreach(x => println(x))
someNumbers.foreach(println) // 当函数参数需要一个函数时,可以进一步省略,例如 def foreach[U](f: A => U): Unit
闭包
闭包是一个函数,内部包含对外部的一个或多个变量的引用
函数makeIncreaser
中more
为自由变量(free variable),x
为绑定变量(bound variable),带自由变量的函数字面量称为开放项(open term),内部包含指向捕获变量的引用
var more = 1
def makeIncreaser(more: Int) = (x : Int) => x +more
外部变量在闭包创建后改变,闭包会受到影响;闭包对捕获变量的改变,变量在闭包之外同样受到影响
scala> val inc1 = makeIncreaser(1)
inc1: Int => Int =
scala> val inc9999 = makeIncreaser(9999)
inc9999: Int => Int =
scala> inc1(10)
res20: Int = 11
scala> inc9999(10)
res21: Int = 10009
特殊函数调用的形式
Scala支持重复参数,命名实参,默认参数
重复参数 Repeated parameters
函数的最后一个参数可以是重复的,在参数的类型之后放一个星号来指明重复的参数,参数数目可以从0到任意多
printStrings("Hello", "Scala", "Python")
def printStrings( args:String* ) = {}
函数内部重复参数的类型是对应类型的数组,但是不能直接传递数组,而是在数组名称后加上: _*
,指示编译器把数组的每个元素作为参数传递
printString(args: _*)
命名实参 Named Arguments
正常情况下,参数按被调用函数的参数顺序依次匹配
命名参数允许以不同的顺序将参数传递给函数,每个实参前面加上形参名称和等号
object Demo {
def main(args: Array[String]) {
printInt(b = 5, a = 7);
}
def printInt( a:Int, b:Int ) = {
println("Value of a : " + a ); //7
println("Value of b : " + b );//5
}
}
默认参数 Default Parameter
Scala 可以为函数参数指定默认参数值,Java语言不支持该功能,必须通过重载才能间接实现
有默认值的实参可以选择忽略,设为的默认值,可以和命名实参结合使用
def addInt( a:Int = 5, b:Int = 7 ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
addInt() //12
addInt(a = 3) //10
addInt(b = 15) //20
高阶函数 Higher-Order Functions
使用其他函数作为参数,或其返回值是一个函数
object Files {
object FileMatcher {
private def filesHere = (new java.io.File(".")).listFiles
private def filesMatching(matcher: String => Boolean) =
for (file <- filesHere; if matcher(file.getName))
yield file
def filesEnding(query: String) =
filesMatching(_.endsWith(query))
def filesContaining(query: String) =
filesMatching(_.contains(query))
def filesRegex(query: String) =
filesMatching(_.matches(query))
}
}
柯里化 Currying
函数定义多个参数列表,使用部分参数列表调用某个方法时,产生一个将缺少的参数列表作为其参数的函数
scala> def plainOldSum(x: Int, y: Int) = x + y
plainOldSum: (x: Int, y: Int)Int
scala> def curriedSum(x: Int)(y: Int) = x + y
curriedSum: (x: Int)(y: Int)
scala> curriedSum(1)(2) //实质上是接连进行了两次函数调用,中间的函数值是 curriedSum(1)_
res5: Int = 3
传名参数 by-name parameters
在函数内部进行参数表达式的值计算,传名类型中空的参数列表被省略,只用写 =>
,仅用于形参
var assertionsEnabled = true
def myAssert(predicate: () => Boolean) =
if (assertionsEnabled && !predicate())
throw new AssertionError
//正确调用
myAssert(() => 5 > 3)
// 错误调用
myAssert(5 > 3) // 缺少() =>
//传名调用
def byNameAssert(predicate: => Boolean) =
if (assertionsEnabled && !predicate)
throw new AssertionError
//正确调用
byNameAssert(5 > 3)
byNameAssert(5 > 3)
括号中的表达式不是直接计算的,而是创建一个函数值,在它的apply
方法进行比较
在一个函数内部多次使用传名调用值时,每次都会重新计算一次表达式的值