Scala函数值和闭包

函数值

    函数式函数式编程的一等公民。函数可以当做参数传递给函数,可以从函数中返回,甚至可以在函数中嵌套,这些高阶函数称为函数值。闭包是一种特殊的函数值,闭包中封闭或绑定了在另一个作用域或上下文中定义的变量。

    在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}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值