第6章——函数值和闭包
在函数式编程中,函数是一等公民。函数可以作为参数值传入其他函数中,函数的返回值可 以是函数,函数甚至可以嵌套函数。这些高阶函数在 Scala 中被称为函数值(function value)。闭 包(closure)是函数值的特殊形式,会捕获或者绑定到在另一个作用域或上下文中定义的变量。
6.1 常规函数的局限性
FunctionValuesAndClosures/Sum.scala
def sum(number: Int) = {
var result = 0
for (i <- 1 to number) {
result += i
}
result
}
6.2 可扩展性与高阶函数
FunctionValuesAndClosures/Loop.scala
def totalResultOverRange(number: Int, codeBlock: Int => Int) = {
var result = 0
for (i <- 1 to number) {
result += codeBlock(i)
}
result
}
FunctionValuesAndClosures/Loop.scala
println(totalResultOverRange(11, i => i))
FunctionValuesAndClosures/Loop.scala
println(totalResultOverRange(11, i => if (i % 2 == 0) i else 0))
FunctionValuesAndClosures/Loop.scala
println(totalResultOverRange(11, i => if (i % 2 != 0) i else 0))
6.3 具有多个参数的函数值
FunctionValuesAndClosures/ZeroParam.scala
def printValue(generator: () => Int): Unit = {
println(s"Generated value is ${generator()}")
}
printValue(() => 42)
FunctionValuesAndClosures/Inject.scala
def inject(arr: Array[Int], initial: Int, operation: (Int, Int) => Int) = {
var carryOver = initial
arr.foreach(element => carryOver = operation(carryOver, element))
carryOver
}
FunctionValuesAndClosures/Inject.scala
val array = Array(2, 3, 5, 1, 6, 4)
val sum = inject(array, 0, (carry, elem) => carry + elem)
println(s"Sum of elements in array is $sum")
FunctionValuesAndClosures/Inject.scala
val max =
inject(array, Integer.MIN_VALUE, (carry, elem) => Math.max(carry, elem))
println(s"Max of elements in array is $max")
运行结果
Sum of elements in array is 21
Max of elements in array is 6
FunctionValuesAndClosures/Inject2.scala
val sum = array.foldLeft(0)((sum, elem) => sum + elem) //可以被替换为对sum方法的调用
val max = array.foldLeft(Integer.MIN_VALUE) { (large, elem) =>
Math.max(large, elem)
}
println(s"Sum of elements in array is $sum")
println(s"Max of elements in array is $max")
FunctionValuesAndClosures/Inject2.scala
val sum = (0 /: array) { (sum, elem) =>
sum + elem
}
val max =
(Integer.MIN_VALUE /: array) { (large, elem) =>
Math.max(large, elem)
}
FunctionValuesAndClosures/Inject3.scala
val sum = inject(array, 0) { (carryOver, elem) =>
carryOver + elem
}
运行结果
Inject3.scala:9: error: not enough arguments for method inject: (arr:
Array[Int], initial: Int, operation: (Int, Int) => Int)Int.
Unspecified value parameter operation.
val sum = inject(array, 0) {(carryOver, elem) => carryOver + elem}
^
one error found
6.4 柯里化
scala> def foo(a: Int)(b: Int)(c:Int) = {}
foo: (a: Int)(b: Int)(c: Int)Unit
scala> foo _
res0: Int => (Int => (Int => Unit)) = <function1>
scala> :quit
FunctionValuesAndClosures/Inject4.scala
def inject(arr: Array[Int], initial: Int)(operation: (Int, Int) => Int): Int = {
var carryOver = initial
arr.foreach(element => carryOver = operation(carryOver, element))
carryOver
}
FunctionValuesAndClosures/Inject4.scala
val sum: Int = inject(array, 0) { (carryOver, elem) =>
carryOver + elem
}
6.5 参数的占位符
FunctionValuesAndClosures/Underscore.scala
val arr = Array(1, 2, 3, 4, 5)
val total = arr.foldLeft(0) { (sum, elem) =>
sum + elem
}
FunctionValuesAndClosures/Underscore.scala
val total = arr.foldLeft(0) { _ + _ }
FunctionValuesAndClosures/Underscore.scala
val negativeNumberExists1 = arr.exists { elem =>
elem < 0
}
val negativeNumberExists2 = arr.exists { _ < 0 }
6.6 参数路由
FunctionValuesAndClosures/RouteParams.scala
val largest =
(Integer.MIN_VALUE /: arr) { (carry, elem) =>
Math.max(carry, elem)
}
FunctionValuesAndClosures/RouteParams.scala
val largest = (Integer.MIN_VALUE /: arr) { Math.max(_, _) }
FunctionValuesAndClosures/RouteParams.scala
val largest = (Integer.MIN_VALUE /: arr) { Math.max _ }
FunctionValuesAndClosures/RouteParams.scala
val largest = (Integer.MIN_VALUE /: arr) { Math.max }
6.7 复用函数值
FunctionValuesAndClosures/Equipment.scala
class Equipment(val routine: Int => Int) {
def simulate(input: Int): Int = {
print("Running simulation...")
routine(input)
}
}
FunctionValuesAndClosures/EquipmentUseNotDry.scala
object EquipmentUseNotDry extends App {
val equipment1 = new Equipment({ input =>
println(s"calc with $input"); input
})
val equipment2 = new Equipment({ input =>
println(s"calc with $input"); input
})
equipment1.simulate(4)
equipment2.simulate(6)
}
运行结果
Running simulation...calc with 4
Running simulation...calc with 6
FunctionValuesAndClosures/EquipmentUseNotDry.scala
object EquipmentUseDry extends App {
val calculator = { input: Int =>
println(s"calc with $input"); input
}
val equipment1 = new Equipment(calculator)
val equipment2 = new Equipment(calculator)
equipment1.simulate(4)
equipment2.simulate(6)
}
运行结果
Running simulation...calc with 4
Running simulation...calc with 6
FunctionValuesAndClosures/EquipmentUseNotDry2.scala
object EquipmentUseDry2 extends App {
def calculator(input: Int) = { println(s"calc with $input"); input }
val equipment1 = new Equipment(calculator)
val equipment2 = new Equipment(calculator)
equipment1.simulate(4)
equipment2.simulate(6)
}
运行结果
Running simulation...calc with 4
Running simulation...calc with 6
6.8 部分应用函数
FunctionValuesAndClosures/Log.scala
import java.util.Date
def log(date: Date, message: String): Unit = {
//...
println(s"$date ---- $message")
}
val date = new Date(1420095600000L)
log(date, "message1")
log(date, "message2")
log(date, "message3")
FunctionValuesAndClosures/Log.scala
val date = new Date(1420095600000L)
val logWithDateBound = log(date, _: String)
logWithDateBound("message1")
logWithDateBound("message2")
logWithDateBound("message3")
运行结果
scala> import java.util.Date
import java.util.Date
scala> def log(date: Date, message: String) = println(s"$date ----
$message")
log: (date: java.util.Date, message: String)Unit
scala> val logWithDateBound = log(new Date, _ : String)
logWithDateBound: String => Unit = <function1>
scala> :quit
6.9 闭包
FunctionValuesAndClosures/Closure.scala
def loopThrough(number: Int)(closure: Int => Unit): Unit = {
for (i <- 1 to number) { closure(i) }
}
FunctionValuesAndClosures/Closure.scala
var result = 0
val addIt = { value: Int =>
result += value
}
FunctionValuesAndClosures/Closure.scala
loopThrough(10) { elem =>
addIt(elem)
}
println(s"Total of values from 1 to 10 is $result")
result = 0
loopThrough(5) { addIt }
println(s"Total of values from 1 to 5 is $result")
FunctionValuesAndClosures/Closure.scala
var product = 1
loopThrough(5) { product *= _ }
println(s"Product of values from 1 to 5 is $product")
运行结果
Total of values from 1 to 10 is 55
Total of values from 1 to 5 is 15
Product of values from 1 to 5 is 120
6.10 Execute Around Method 模式
FunctionValuesAndClosures/Resource.scala
class Resource private () {
println("Starting transaction...")
private def cleanUp(): Unit = { println("Ending transaction...") }
def op1(): Unit = println("Operation 1")
def op2(): Unit = println("Operation 2")
def op3(): Unit = println("Operation 3")
}
object Resource {
def use(codeBlock: Resource => Unit): Unit = {
val resource = new Resource
try {
codeBlock(resource)
} finally {
resource.cleanUp()
}
}
}
FunctionValuesAndClosures/Resource.scala
Resource.use { resource =>
resource.op1()
resource.op2()
resource.op3()
resource.op1()
}
运行结果
Starting transaction...
Operation 1
Operation 2
Operation 3
Operation 1
Ending transaction...
FunctionValuesAndClosures/WriteToFile.scala
import java.io._
def writeToFile(fileName: String)(codeBlock: PrintWriter => Unit): Unit = {
val writer = new PrintWriter(new File(fileName))
try {
codeBlock(writer)
} finally {
writer.close()
}
}
FunctionValuesAndClosures/WriteToFile.scala
writeToFile("output/output.txt") { writer =>
writer.write("hello from Scala")
}
运行结果
hello from Scala