【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()
}
}
}