第四节 函数和闭包

函数

scala中的函数和Java中方法的概念是一样的,就是要将程序分成若干段可以个职其责任。看一个scala简单的函数:
object FunctionTest {
  def testPro(name:String,age:Int): Unit ={
    println(name+":"+"age");
  }

  def main(args: Array[String]): Unit = {
    testPro("lee",30)
  }

}

本地函数

object FunctionTest {
  def main(args: Array[String]): Unit = {
    processFile("c:/a.txt",20);
  }

  def processFile(fileName:String,width:Int): Unit = {

    def processLine(fileName: String, width: Int, line: String): Unit = {
      if (line.length > width) {
        println(fileName + ":" + line);
      }
    }
    val source = Source.fromFile(fileName);
    for (line <- source.getLines()) {
      processLine(fileName, width, line);
    }
  }
}
    为什么要使用本地函数:在拆解函数时往往将函数拆分成很多小函数。这些函数往往称之为辅助函数。程序打成包部署生产环境下,这些函数是没有任何意义的(除了对本地对象或类)。因此存在命名空间污染。在Java中本地函数/方法是用private修饰,这种办法同样适用于scala语言中。
    上面实例processLine作为内部函数定义在processFile中,processLine的作用范围仅限processFile中,外部无法访问。但是外层函数的入参,内部函数可以直接使用:
object FunctionTest {
  def main(args: Array[String]): Unit = {
    processFile("c:/a.txt",20);
  }

  def processFile(fileName:String,width:Int): Unit = {

    def processLine(line :String): Unit = {
      if (line.length > width) {
        println(fileName + ":" + line);
      }
    }
    val source = Source.fromFile(fileName);
    for (line <- source.getLines()) {
      processLine(fileName, width, line);
    }
  }
}

头等函数

first-class function。scala的函数是头等函数,不仅可以定义调用,还可以把他写成字面量,并且当作值来传递。例如:

函数字面量中也可以写成多条语句,使用“{}”可以实现。函数的字面量在编译的时候会编译成class文件,而变量只有在运行期才存在。所以函数字面量和变量的区别可以认为是类和对象的区别。

函数字面量——值函数

函数字面量(function literal),也称值函数(function values),指的是函数可以赋值给变量。

一般函数具有如下形式: 

这里写图片描述

而函数字面量具有如下形式:
/**
 *  函数字面量 function literal
 *  =>左侧的表示输入,右侧表示转换操作
 */
scala> val increase=(x:Int)=>x+1
increase: Int => Int = <function1>

scala> println(increase(10))
//前面的语句等同于
scala>  def increaseAnother(x:Int)=x+1
increaseAnother: (x: Int)Int

scala> println(increaseAnother(10))
11

//数组的map方法中调用(写法1)
scala> println(Array(1,2,3,4).map(increase).mkString(","))
1,2,3,4,5

函数字面量还可以写的更加简短,例如上面的例子:

还可以这么写(把参数类型去掉,filter是过滤器)

scala> somenums
res39: List[Int] = List(1, 2, 3, 4, 5)

scala> somenums.filter(_>0);
res40: List[Int] = List(1, 2, 3, 4, 5)

    还有一种简化函数字面量的方式——占位符。如果想让函数字面量变得更清洁,占位符是一个很不错的选择。scala中使用下划线做占位符。可以把占位符看成表达式里需要被“填入”的“空白”,也可以理解为“预留”。这个空白在每次函数被调用的时候,参数填入。有时编译器无法推断确实的参数类型,例如:val f = _+_,编译器是无法推断数据类型的。因此可以写成(_:Int)+(_:Int)。谨记:每个参数在函数字面量中最多出现一次。多个下划线出现时需指定类型,依次代表1,2...参数,而不是一个参数的重复利用。

部分应用函数

    部分应用函数是一种表达式,不需要提供函数所需要的所有参数,而是提供一部分获不提供参数。例如:

object FunctionTest {
  val somenums = List(1,2,3,4);
  somenums.foreach(println _);//注意空格
}
在看一个

object FunctionTest {

  def sum (x:Int,y:Int,z:Int)  = x + y +z
  val a = sum _;
  sum(1,2,3)
  
}

高阶函数

柯里化

闭包

到这里为止,所有函数字面量的例子仅参考了传入的参数。例如:(x:Int) => x>0,表达式x>0中用到了参数x,x被称为函数参数。然后也可以参考定义在其他地方的变量。例如(x:Int)=> x+more;这里x是绑定变量,more是自由变量,且more必须有定义。


scala编程中是这么说闭包的--闭包源自于通过捕获自由变量的绑定,从而对函数字面量执行关闭操作。不带自由变量的函数字变量,如(x:Int)=>

x+1,被称为封闭项,项指的是一部分源代码,因此按照函数字面量在运行的时创建的函数值严格意义上讲不是闭包,因为在编写的时候就封闭了。任何带有自由变量的字面量都是开放项。因此任何以(x:Int)=> x+more为模板在运行期间创建的函数值必将捕获自由变量。因此得到的函数值有指向捕获自由变量的索引,又由于函数值是关闭这个开放项(x:Int)=>x+more的最终产物,因此被称作闭包。

object FunctionTest {

  val someNumbers = List(-11,-10,5,0,5,10);
  var sum = 0;
  someNumbers.foreach(sum += _);

}

重复参数

scala允许指明函数的最后一个参数是重复的。从而允许客户向函数传入可变长度的参数列表。在参数类型后加“*”:
def echo(str : String*): Unit ={
  for(arg <- str){
    println(arg);
  }
}
这样定义的echo可以被一个或者多个参数调用:
object FunctionTest {
  def echo(str : String*): Unit ={
    for(arg <- str){
      println(arg);
    }
  }

  def main(args: Array[String]): Unit = {
    echo("hello");
    echo("hello","world","scala");
  }
  //输出结果
  hello
  hello
  world
  scala

  Process finished with exit code 0
}
在函数内部,重复参数的类型实际上是声明参数的类型的数组。在ehco被声明为String类型的str,是基上是Array[String]。但是往echo里传入数组类型编译报错, 例如val arr=Array("s","b")。 应该使用echo(arr : _*)。

尾递归



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值