什么是闭包?
引用至少一个自由变量的函数称为闭包。
闭包是一个函数,可纯函数或非纯函数,可有名字或匿名,但重要的是它是一个函数。 为何称其为闭包,它与函数最重要的区别是:引用自由变量。
// p相对于getHike,是其自由变量。getHike函数没有局部变量和列表参数p。
var p =10
def getHike(salary:Double) = salary * p/100
getHike(5000)
如果自由变量值发生变化会怎样?
执行闭包时,它采用最新的自由变量的值。
var p =10
def getHike(salary:Double) = salary * p/100
getHike(5000)
//res1: Double = 500.0
p=20
getHike(5000)
//res2: Double = 1000.0
闭包是否为纯函数:取决于自由变量的类型var和val
如果闭包修改了自由变量的值会怎样?
如果闭包修改了自由变量,则更改在闭包外部可见。
var p =10
def getHike(salary:Double) = {
p=p*2
salary * p/100
}
println(p)
//10
getHike(5000)
//res8: Double = 1000.0
println(p)
//20
为什么需要闭包,有什么优势?
函数式编程,函数可以最为参数传递和返回,与面向对象类似。
对于某些例子,对象更灵活,因为对象携带方法和数据元素(状态)。然而,函数是唯一的,因为它没有任何数据元素(状态)。
所以,如果我们需要传递一堆状态和一个函数,那么使用:闭包和自由变量。
val l = (1001 to 1005).toList
l.map(getHike)
def getHike = {
//Load employee and their current salary
val e:Map[Int,Double] = Map(1001->35000.00,
1002->43000.00,
1003->28000.00,
1004->54000.00,
1005->17000.00)
// Some logic to derive percentage for each employee
val p:Map[Int,Double] = Map(1001 -> 10.00,
1002->12.00,
1003->7.50,
1004->6.80,
1005->20.00)
(empID:Int) => (empID, e(empID) * p(empID) /100.00) // 返回一个匿名函数,即闭包
}
val f = getHike
f: Int => (Int, Double) = <function1>
//Get Hike for an employee
f(1001)
//res10: (Int, Double) = (1001,3500.0)
//Get Hike for a non existant employee
f(1006)
//java.util.NoSuchElementException: key not found: 1006
从最后一行返回的匿名函数是一个闭包。它使用两个自由变量e和p。
当我们从getHike返回它时,它带有e和p的状态。
所以,f包含它的数据。闭包就像面向对象世界里传递的一个对象!
它节省了大量复杂且不必要的代码,并简化了解决方案。