Scala中的函数式编程

Scala中的函数式编程

作为一门面向对象与函数式的混合范式语言,Scala 并不强制要求函数必须是纯函数,也不要求变量不可变。尽管它的确推荐你在任何可能的情况下这么做。

以下是几个高阶函数,我们将其组合在一起,用它来对一个整数列表进行遍历,过滤出其中的偶数,对每个偶数乘以2,再使用reduce 函数将各个整数乘在一起:

object Test extends App {
  val l = (1 to 10) filter (_ % 2 == 0) map (_ * 2) reduce (_ * _)
  println(l)
}

输出:
122880

_ % 2 == 0,_ * 2,与_ * _ 是函数字面量。前两个函数只有一个参数,赋值给占位符_;最后一个函数带两个参数,该函数本身是reduce 函数的参数。

reduce 函数将各个元素做累乘,也就是说它将整数的集合reduce 为一个值。reduce 函数带两个参数,均赋值给了占位符_。其中一个参数是集合中的当前元素,另一个参数就是累乘值,是上一次调用reduce函数得到的部分元素的累乘结果。(第一个参数是累乘参数,还是第二个参数是累乘参数取决于具体实现)对传入的函数的要求是:其计算必须满足结合律,类似乘法与加法,因为我们不保证集合中元素的计算顺序。于是,我们只用了一行代码,没有用可变的计数器,也没有用可变变量作为累乘结果,就“循环”遍历了列表得出结果。

把代码稍微修改一下:

object Test extends App {
  var factor = 2
  val multiplier = (i: Int) => i * factor
  var l = (1 to 10) filter (_ % 2 == 0) map multiplier reduce (_ * _)
  println(l)
  factor = 3
  l = (1 to 10) filter (_ % 2 == 0) map multiplier reduce (_ * _)
  println(l)
}

输出:
122880
933120

我们定义一个名为factor 的变量,作为累乘因子。而之前的匿名函数_ * 2 则替换为一个名为multiplier 的变量,变量的值由factor 决定。注意,multiplier 事实上也是一个函数。由于函数在Scala 中是第一等的,因此我们定义了表示函数的变量。不过,这不是简单的替换,在这里multiplier 引用了factor,而不是将其硬编码为2。注意看我们使用两个不同的factor 值时,程序的运行结果。首先我们的输出值为122880,与之前相同,但接着输出值为933120。

尽管multiplier 是一个不可变的函数字面量,当factor 改变时,multiplier 的行为也跟着改变。在multiplier 函数中有两个变量i 和factor。i 是一个函数的参数,所以每次调用时,i都绑定了一个新的值。然而,factor 并不是multiplier 的参数,而是一个自由变量,是一个当前作用域中某个值的引用。所以,编译器创建了一个闭包,用于包含(或“覆盖”)multiplier与它引用的外部变量的上下文信息,从而也就绑定了外部变量本身。这就是factor 变化时,multiplier 也跟着变化的原因。Multiplier 引用了factor,每次调用时都重新读取factor 的值。如果函数没有外部引用,那它就只是包含了自身,不需要外部上下文信息。

即使factor 处于某个局部作用域(如某个方法)中,而我们将multiplier 传递给其他作用域(如另一个方法)中时,这一机制仍然有效。该自由变量factor 的有效性一直伴随multiplier 函数:

object Test extends App {
  def m1(multiplier: Int => Int) = {
    (1 to 10) filter (_ % 2 == 0) map multiplier reduce (_ * _)
  }

  def m2: Int => Int = {
    val factor = 2
    val multiplier = (i: Int) => i * factor
    multiplier
  }

  println(m1(m2))
}

输出:
122880

我们调用m2, 返回了一个类型为Int => Int 的函数。返回的内部值是multiplier,multiplier 引用了m2 中定义的变量factor,一旦m2 返回,就离开了factor 变量的作用域。然后调用m1,将m2 的返回值传递给它。尽管factor 变量已经离开了m1 的作用域,但程序的输出与之前的例子相同,仍为122880。m2 返回的函数事实上是一个闭包,它包含了对factor 的引用。

三个关键字

• 函数
一种具有名或匿名的操作。其代码直到被调用时才执行。在函数的定义中,可能有也可能没有引用外部的未绑定变量。
• Lambda
一种匿名函数。在它的定义中,可能有也可能没有引用外部的未绑定变量。
• 闭包
是一个函数,可能匿名或具有名称,在定义中包含了自由变量,函数中包含了环境信息,以绑定其引用的自由变量。

猜你喜欢

转载自blog.csdn.net/u014646662/article/details/84253352