【2019-05-21】函数和闭包

2019-05-21  本文已影响0人  BigBigFlower

(1)方法
定义函数最通用的方法是作为某个对象的成员。这种函数称为方法。

//带私有的processLine方法的LongLines 对象
import scala.io.Source

object LongLines {
//processFile方法以filename和width为参数,文件名用来创建Source对象,并通过for表达式的发生器调用source的getLines方法。
  def processFile(filename: String, width: Int) {
    val source = Source.fromFile(filename)
    for (line <- source.getLines) //getLines返回的枚举器能在每次枚举中从文件里取出一行
      processLine(filename, width, line)
  }

  private def processLine(filename: String,
      width: Int, line: String) {

    if (line.length > width)
      println(filename +": "+ line.trim)
  }
}

object FindLongLines {
  def main(args: Array[String]) {
    val width = args(0).toInt
    for (arg <- args.drop(1))
      LongLines.processFile(arg, width)
  } 
} 

(2)本地函数
把函数定义在别的函数内部,类似于本地变量。

import scala.io.Source

object LongLines2 {
  object LongLines {
    def processFile(filename: String, width: Int) {
    
      def processLine(filename: String,
          width: Int, line: String) {
    
        if (line.length > width)
          print(filename +": "+ line)
      }    
    
      val source = Source.fromFile(filename)
      for (line <- source.getLines) {
        processLine(filename, width, line)
      }
    }
  }

  object FindLongLines {
    def main(args: Array[String]) {
      val width = args(0).toInt
      for (arg <- args.drop(1))
        LongLines.processFile(arg, width)
    } 
  } 

  def main(args: Array[String]) = FindLongLines.main(args)
}

直接使用外部processLine函数的参数

import scala.io.Source

object LongLines3 {
  import scala.io.Source
  
  object LongLines {
  
    def processFile(filename: String, width: Int) {
  
      def processLine(line: String) {
        if (line.length > width)
          print(filename +": "+ line)
      }    
  
      val source = Source.fromFile(filename)
      for (line <- source.getLines)
        processLine(line)
    }
  }

  object FindLongLines {
    def main(args: Array[String]) {
      val width = args(0).toInt
      for (arg <- args.drop(1))
        LongLines.processFile(arg, width)
    } 
  } 

  def main(args: Array[String]) = FindLongLines.main(args)
}

(3)头等函数
Scala的函数是头等函数(first-class function)
可以把它们写为匿名字面量,并作为值传递
ex.对数执行递增操作的函数字面量:

//指明这个函数把左边的东西(任何整数x)转变成右边的东西(x+1)
(x: Int) => x + 1
//res14: Int => Int = $$Lambda$1151/993016696@685e97e0
//定义为变量形式
var increase = (x: Int) => x + 1
increase(15)
//res15: Int = 16

(4)函数字面量的短格式
函数字面量更简短的方式是去除参数

//过滤
val someNumbers = List(-11, -10, -5, 0, 5, 10)
someNumbers.filter((x) => x > 0)
//List[Int] = List(5, 10)

(5)占位符语法
如果想让函数字面量更简洁,可以把下划线当作一个或更多参数的占位符,只要每个参数在函数字面量内仅出现一次。

someNumbers.filter(_ > 0)
//res18: List[Int] = List(5, 10)
val f = (_: Int) + (_: Int)
f(5, 10)
//res19: Int = 15

(6)部分应用函数
单个下划线还可以替换整个参数列表

someNumbers.foreach(println _)
//-11
//-10
//-5
//0
//5
//10

部分应用函数是一种表达式,不需要提供函数需要的所有参数,仅提供部分,或不提供所需参数。

def sum(a: Int, b: Int, c: Int) = a + b + c
sum(1, 2, 3)
//res22: Int = 6
val a = sum _
 a(1, 2, 3)
//res23: Int = 6

(7)闭包
依照函数字面量在运行时创建的函数值(对象)被称为闭包。名称源于通过“捕获”自由变量的绑定,从而对函数字面量执行的“关闭”行动。不带自由变量的函数字面量被称为封闭项。

var more = 1
val addMore = (x: Int) => x + more
addMore(10)
// res19: Int = 11

(8)重复参数
scala可以指明函数的最后一个参数是重复的,从而允许客户向函数传入可变长度参数列表,想要标注一个重复参数,可以在参数的类型之后放一个参数。

def echo(args: String*) =   for (arg <- args) println(arg)
echo("one")
//one
echo("hello", "world!")
//hello
//world!

(9)尾递归
如果想把更新var的while循环转换成仅使用val这种更函数式的风格,可以使用递归。

object Approximate {
  def isGoodEnough(guess: Double): Boolean = {
      println("guess [" + guess + "]")
      Math.abs(guess*guess - 2.0)  <  1.0E-6
  }

  def improve(guess: Double): Double =
      (guess + 2.0/guess)/2.0
      
  def approximate(guess: Double): Double = 
    if (isGoodEnough(guess)) guess
    else approximate(improve(guess))

  def approximateLoop(initialGuess: Double): Double = {
    var guess = initialGuess
    while (!isGoodEnough(guess))
      guess = improve(guess)
    guess
  }

  def main(args: Array[String]) {
    approximateLoop(3.3)
  }
}

尾递归函数跟踪

object TailRecursion {
  def boom(x: Int): Int = 
    if (x == 0) throw new Exception("boom!")
    else boom(x - 1) + 1

  def bang(x: Int): Int = 
   if (x == 0) throw new Exception("bang!")
   else bang(x - 1)

  def main(args: Array[String]) {
    try {
      boom(3)
    } catch {
      case ex: Exception => ex.printStackTrace()
    }

    try {
      bang(5)
    } catch {
      case ex: Exception=> ex.printStackTrace()
    }
  }
}
上一篇下一篇

猜你喜欢

热点阅读