高阶函数
1 概念
如果一个函数的传入参数为函数或者返回值是函数,则该函数即为高阶函数。
2. 传入参数为函数(重要)
Scala中,函数是头等公民,和数字一样。不仅可以调用,还可以在变量中存放函数,也可以作为参数传入函数,或者作为函数的返回值。
3. 传入参数为匿名函数(重要)
在Scala中,你不需要给每一个函数命名,就像不必给每个数字命名一样,将函数赋给变量的函数叫做匿名函数
还可以
4. 传入参数为方法(隐式转换方法到函数)
在Scala中,方法和函数是不一样的,最本质的区别是函数可以做为参数传递到方法中
case class WeeklyWeatherForecast(temperatures: Seq[Double]) {
private def convertCtoF(temp: Double) = temp * 1.8 + 32
//方法convertCtoF作为参数传入
def forecastInFahrenheit: Seq[Double] = temperatures.map(convertCtoF)
}
5.返回值为函数
//返回值为函数类型:(String, String) => String
def urlBuilder(ssl: Boolean, domainName: String): (String, String) => String = {
val schema = if (ssl) "https://" else "http://"
(endpoint: String, query: String) => s"$schema$domainName/$endpoint?$query"
}
val domainName = "www.example.com"
def getURL = urlBuilder(ssl=true, domainName)
val endpoint = "users"
val query = "id=1"
val url = getURL(endpoint, query) // "https://www.example.com/users?id=1": String
方法的嵌套(备选)
方法里嵌套定义其他方法
def factorial(x: Int): Int = {
def fact(x: Int, accumulator: Int): Int = {
if (x <= 1) accumulator
else fact(x - 1, x * accumulator)
}
fact(x, 1)
}
println("Factorial of 2: " + factorial(2))
println("Factorial of 3: " + factorial(3))
方法的多态(备选)
Scala里方法可以通过类型实现参数化,类似泛型。
def listOfDuplicates[A](x: A, length: Int): List[A] = {
if (length < 1)
Nil
else
x :: listOfDuplicates(x, length - 1)
}
println(listOfDuplicates[Int](3, 4)) // List(3, 3, 3, 3)
println(listOfDuplicates("La", 8)) // List(La, La, La, La, La, La, La, La)
6. 闭包(难点)
闭包是一个函数,返回值依赖于声明在函数外部的一个或多个变量。
函数体内可以方法相应作用域内的任何变量。
闭包通常来讲可以简单的认为是可以访问一个函数里面局部变量的另外一个函数。
普通函数:
val multiplier = (i:Int) => i * 10
函数体内有一个变量 i,它作为函数的一个参数。
val multiplier = (i:Int) => i * factor
在 multiplier 中有两个变量:i 和 factor。其中的一个 i 是函数的形式参数,在 multiplier 函数被调用时,i 被赋予一个新的值。然而,factor不是形式参数,而是自由变量,考虑下面代码:
var factor = 3 val multiplier = (i:Int) => i * factor
这里我们引入一个自由变量 factor,这个变量定义在函数外面。
这样定义的函数变量 multiplier 成为一个"闭包",因为它引用到函数外面定义的变量,定义这个函数的过程是将这个自由变量捕获而构成一个封闭的函数。
object Test {
def main(args: Array[String]) {
println( "muliplier(1) value = " + multiplier(1) )
println( "muliplier(2) value = " + multiplier(2) )
}
var factor = 3
val multiplier = (i:Int) => i * factor
}
7. 柯里化
Curring函数,指的是,将原来接收两个参数的一个函数,转换为两个函数,第一个函数接收原先的第一个参数,然后返回接收原先第二个参数的第二个函数。
在函数调用的过程中,就变为了两个函数连续调用的形式
下面先给出一个普通的非柯里化的函数定义,实现一个加法函数:
scala> def plainOldSum(x:Int,y:Int) = x + y
plainOldSum: (x: Int, y: Int)Int
scala> plainOldSum(1,2)
res0: Int = 3
使用“柯里化”技术,把函数定义为多个参数列表:
scala> def curriedSum(x:Int)(y:Int) = x + y
curriedSum: (x: Int)(y: Int)Int
scala> curriedSum (1)(2)
res0: Int = 3
当你调用 curriedSum (1)(2)时,实际上是依次调用两个普通函数(非柯里化函数),第一次调用使用一个参数 x,返回一个函数类型的值,第二次使用参数y调用这个函数类型的值,我们使用下面两个分开的定义在模拟 curriedSum 柯里化函数:
首先定义第一个函数:
scala> def first(x:Int) = (y:Int) => x + y
first: (x: Int)Int => Int
然后我们使用参数1调用这个函数来生成第二个函数。
scala> val second=first(1)
second: Int => Int = <function1>
scala> second(2)
res1: Int = 3
first,second的定义演示了柯里化函数的调用过程,它们本身和 curriedSum 没有任何关系,但是我们可以使用 curriedSum 来定义 second,如下:
scala> val onePlus = curriedSum(1)_
onePlus: Int => Int = <function1>
下划线“_” 作为第二参数列表的占位符, 这个定义的返回值为一个函数,当调用时会给调用的参数加一。
scala> onePlus(2)
res2: Int = 3
通过柯里化,你还可以定义多个类似 onePlus 的函数,比如 twoPlus
scala> val twoPlus = curriedSum(2) _
twoPlus: Int => Int = <function1>
scala> twoPlus(2)
res3: Int = 4