函数值
函数式函数式编程的一等公民。函数可以当做参数传递给函数,可以从函数中返回,甚至可以在函数中嵌套,这些高阶函数称为函数值。闭包是一种特殊的函数值,闭包中封闭或绑定了在另一个作用域或上下文中定义的变量。
在scala中,可以在函数里创建函数,将函数赋给引用 ,或者把它们当做参数传递给其他函数。scala内部实现对这些所谓的函数值进行了处理,把它们创建为特殊类的实例,所以scala的函数值是真正的对象。
package com.fanshadoop
class MethodDemo {
/**
* codeblock是一个函数值,接受一个Int,返回一个Int
*/
def totalResultCompute(number:Int, codeblock:Int => Int) = {
var result = 0
for (i <- 1 to number) {
result += codeblock(i)
}
result
}
//=> 将参数列表和实现分开
println(totalResultCompute(11, i=> i))//单纯返回i
println(totalResultCompute(11, i=> if (i % 2 == 0) 1 else 0))//偶数计数
}
具有多参数的函数值
def inject(arr:Array[Int], init:Int ,operation:(Int,Int)/*参数类型*/ => Int/*函数返回类型*/) = {
var carryout = init
arr.foreach(element => carryout = operation(carryout,element))
carryout
}
var array = Array(1,3,-9,98,83)
inject(array,0,(i , j) => i + j)//求和
inject(array,0,(i , j) => Math.max(i,j))//最大值
遍历容器里的元素,scala提供了内置方法:foldLeft(),也就是/:方法。
此处未整明白。。。
Curry化
scala的curry化,可以把函数从接收多个参数转换成接收多个参数列表。如果要用同样的一组实参多次调用一个函数,可以用curry化来减少噪音,让代码更有味道。
package com.fanshadoop
class CurryDemo {
//将
def curry(arr:Array[Int], init:Int) (operation:(Int,Int) => Int) = {
var carryover = init
arr.foreach(elem => carryover = operation(carryover, elem))
carryover
}
//curry调用方式
var array = Array(1,3,-9,98,83)
val sum = curry(array, 0){ (carryover,elem) => carryover + elem }
}
重用函数值
函数值对创建重用性代码以及小区代码重复有很大的帮助,我们可以创建函数值的引用作为参数传递。
/**
* 构造函数参数为函数值
*/
class Equipment(val rounte/*函数值名称*/: Int/*参数类型*/ => Int/*返回值类型*/) {
def simulate(input : Int) = {
rounte(input)
}
}
val calucator = {input : Int/*函数值参数*/ => /*函数体*/println("calc with "+input); input}
val calc = new Equipment(calucator)
calc.simulate(10)
参数的位置标记法
scala中,下划线(_)可以表示函数值的参数。如果某个参数在函数里仅使用一次,就可以用下划线表示
package com.fanshadoop
import java.util.ArrayList
object HelloWorld {
def main(args: Array[String]): Unit = {
val arr = Array(1,2,3,4,5)
// /:方法计算数组和
println((0 /: arr) {(sum,element) => sum + element})
//第一个出现的_表示函数调用过程中持续存在的值,第二个_表示数组元素
println("sum=="+(0 /: arr) {_ + _})
}
}
execute around method模式
想在对对象进行任意一组操作的前后执行一堆操作,这种模式就属于Execute Around Method模式。类似Java中的synchronized实现,监视器会自动加锁和解锁。在scala中使用伴生对象来实现:
package com.fanshadoop
class Resource private() {
println("starting transaction")
private def cleanup() = {//清理函数
println("ending transaction");
}
def op1 = {println("op1")}
def op2 = {println("op2")}
def op3 = {println("op3")}
def op4 = {println("op4")}
}
object Resource{//伴生对象
def use(block : Resource => Unit) = {
val resource = new Resource()
try {
block(resource)
}finally {
resource.cleanup()
}
}
def main(args:Array[String]) = {
Resource.use({resource => resource.op1
resource.op2
resource.op3
resource.op4})/*要执行的操作*/
}
}
偏应用函数
调用函数可以说是将函数应用与实参,如果传入所有的预期的参数,就完全应用了这个函数。如果只传入几个参数,就会得到一个偏应用函数。
package com.fanshadoop
import java.util.Date
object Partial {
def log(date : Date, message: String) = {
println(date + "==" + message)
}
def main(args:Array[String]) {
val date = new Date
val bindingMessage = log(date,_:String)//_使得第二个方法没有被绑定
bindingMessage("message1")
bindingMessage("message2")
bindingMessage("message3")
}
}
创建偏应用函数,scala内部会创建一个新类,使用它的特殊函数apply()
闭包
在之前的例子中,用于函数值或代码块的变量和值都是绑定的,你能清楚知道他们绑定到哪,局部变量或参数。此外还可以创建有未绑定变量的代码块,调用函数之前,必须绑定它们不过它们可以再局部范围和参数列表之外绑定变量。
package com.fanshadoop
class Closure {
//接受代码块作为参数
def loopthrought(number : Int)(closure : Int => Unit) = {
for (i <- 1 to number) {
closure(i)
}
}
var result = 0
/*
* 在代码块内,value绑定到参数,不过变量result在代码块和参数列表没有被定义
* 实际上,绑定到代码块外部变量result上了
* 绑定并不是获得当前变量的一个副本,而是变量本身,如果result重置,闭包也能看到变量的变化。
*/
val add = {value : Int => result += value}
}