C#程序员的Scala之路第六章(函数和闭包)

1.方法

在Scala中定义函数的最通用方法就是某个对象的成员,这种函数被称为方法:Method

import scala.io.Source
object LongLines extends App {
  def processFile(fileName: String, width: Int) {
    val source = Source.fromFile(fileName)
    for (line <- source.getLines()) {
      processLine(fileName, width, line)
    }
  }
  private def processLine(fileName: String, width: Int, line: String) {
    if (line.length > width)
      println(fileName + ":" + line.trim)
  }
  processFile("D:\\ScalaFile.txt",3)
}

上面代码被定义在LongLines 中,所以他们是LongLines的方法,processFile负责读取文件,processLine负责读取行判断长度是否符合并且打印输出。

2.本地函数

Scala为了更加详细的描述函数的级别关系,加入了本地函数的概念,也就是函数中的函数

import scala.io.Source
object LongLines extends App {
  def processFile(fileName: String, width: Int) {
    def processLine(fileName: String, width: Int, line: String) {
      if (line.length > width)
        println(fileName + ":" + line.trim)
    }
    val source = Source.fromFile(fileName)
    for (line <- source.getLines()) {
      processLine(fileName, width, line)
    }
  }
  processFile("D:\\ScalaFile.txt", 3)
}

将processLine 移动到processFile里面,让processLine 成为processFile的子函数,processLine的范围只局限性与processFile之内,外部无法访问。

而且在processLine中就可以访问ProcessFile的局部变量,如下代码:

import scala.io.Source
object LongLines extends App {
  def processFile(fileName: String, width: Int) {
    def processLine(line: String) {
      if (line.length > width)
        println(fileName + ":" + line.trim)
    }
    val source = Source.fromFile(fileName)
    for (line <- source.getLines()) {
      processLine(line)
    }
  }
  processFile("D:\\ScalaFile.txt", 3)
}

3.第一类函数

Scala的第一类函数与C#的匿名函数lambda一样,如下代码:

object LongLines extends App {
  val increase = (x: Int) => x + 1
  print(increase(12))
}

如果你想在函数文本中输入多行,而函数的返回值就是最后一行产生的那个表达式

object LongLines extends App {
  val increase = (x: Int) =>
    {
      println("We")
      println("are")
      println("here!")
      x + 1
    }
    increase(20)
}

Scala中提供了很多库来支持他的函数文本,如集合类的foreach表达式:

object LongLines extends App {
   val list = List(-11,-10,-5,0,5,10)
   list.foreach { x => println(x) }
}

另一个列子是集合类的Filter:

object LongLines extends App {
  val list = List(-11, -10, -5, 0, 5, 10)
  list.foreach { x => println(x) }
  println("Filter")
  println(list.filter { x => x > 0 })
}

4.函数文本的短格式

Scala提供很多方法使函数文本更加简便如下:

object LongLines extends App {
  val list = List(-11, -10, -5, 0, 5, 10)
  list.foreach { x => println(x) }
  println("Filter")
  println(list.filter { x => x > 0 })
  list.filter(x => x > 0)
  list.filter(_ > 0)
}

_>是最简便的写法,这叫占位符写法,_编译器会生成一个零时的变量,多个_代表多个变量,如下语法:

object LongLines extends App {
  val add = (_:Int) +(_:Int)
  println(add(10,5))
}

5.偏应用函数

_还可以替换整个参数列表如下代码:

object LongLines extends App {
  val list = List(-11, -10, -5, 0, 5, 10)
  list.foreach(println(_)) //这是一种写法
  list.foreach(println _)  //这是更方便的写法
  list.foreach(println) //这是最简便的方式
}

println _这种写法就是偏应用函数,在Scala里,当你调用函数,传入任何需要的参数,你就是在把函数应用到参数上。如,给定下列函数:

 def sum(a: Int, b: Int, c: Int) = a + b + c

你就可以把函数sum应用到参数1,2,3上如sum(1,2,3),偏应用函数是一种表达式,你不需要提供函数需要的所有参数,代之仅提供部分,或者不提供参数:

object LongLines extends App {
  def sum(a: Int, b: Int, c: Int) = a + b + c
  val a = sum _
  println(a(1,2,3))
}

a(1,2,3)Scala编译器将它编译为a.apply(1,2,3),为什么叫偏应用函数呢?因为函数应用的参数不完全,所以它是偏应用函数,如下:

object LongLines extends App {
  def sum(a: Int, b: Int, c: Int) = a + b + c
  val a = sum(1,_:Int,3)
  println(a(2)) //这里调用的是 sum(1,2,3)
  println(a(5)) //这里调用的是 sum(1,5,3)
}

这样就能看明白了,sum(1,_:Int,3)中间的被占位符所取代了,之后调用的时候只有占位符是未知的,调用sum函数只需要输入占位符所需要的参数就好了。

因为函数应用在参数上不完全,所以叫做偏应用函数。

6.闭包

scala的闭包与C#的闭包是一样的,函数对象使用自由变量时,这个函数被叫做闭包函数:

object LongLines extends App {
  var more = 1
  def addMore(x: Int): Int = x + more
  println(addMore(1))
}

在这里more 相对于函数 addMore 来说是一个自由变量,而x则是绑定变量,

如addMore(x:Int)=x+1 这个函数就是一个封闭函数,因为他不需要绑定上一个函数堆栈里的内容。

而addMore(x:Int) = x+more 则是一个开放函数,而函数的值是关闭这个开放术语的行动最终产物,所以叫做闭包。

下面就来看看闭包的特性:

object LongLines extends App {
  var more = 1
  val addMore = (x: Int) => {
    more = 100
    x + more
  }
  more = 10
  println(addMore(10))
  println(more)
}

输出110,100

7.重复参数

Scala允许你指明函数的最后一个参数是重复的在参数类型后加*就可以,如C#里的params,语法如下:

object LongLines extends App {
  def outArray(x :String*) = x.foreach(println)
  outArray("1","2","3","4","5")
}

其实scala是吧参数对应成Array[String],但是你如果想直接传入一个Arrary[String]你则需要像下面代码这样:

object LongLines extends App {
  def outArray(x: String*) = x.foreach(println)
  outArray(Array("1", "2"): _*)
}

添加一个: _*,这样就可以了

更多内容请参考Scala编程

 

你可能感兴趣的:(scala)