函数(Functions)
函数在 Scala 中除了方法(methods)之外还有多种形态的存在,局部函数(local functions)、函数字面量(function literals)以及函数值(function values)
方法
最为普通的就是为某个对象定义一个函数作为其成员,这样的函数被称作方法
局部函数(Local Functions)
就像在函数中定义局部变量一样,可以定义局部函数,其主要是为了解决函数命名污染(pollute)的问题,而且也会让一些工具函数持有灵活性(flexiblity)的保证
头等函数(First-Class Functions)
函数在 Scala 中是头等函数,就是说你不仅仅可以定义函数然后调用,你也可以写一个没有命名的函数字面量然后将其作为值来进行传参
一个函数字面量被编译成一个类(class),在运行时(runtime)被实例化成一个函数值。因此,函数字面量和函数值之间的不同就是,函数字面量存在于源码当中,而函数值是作为对象(object)存在于运行时。可以类比为 类(source code) 和对象(object)之间的关系
关于 =>
理解,可以类比高等数学中关于函数的映射关系。这个符号指示这个函数要把左边的表达式转换成右边的表达式
函数字面量的缩写方式(限调用过程中)
1.通过类型推断,可以省略掉类型参数
2.当只有一个参数时,可以省略圆括号(parentheses)
3.当每个参数只在函数字面量中只出现一次,可以使用占位符语法(Placeholder Syntax)
部分应用函数(Partially Applied Functions)
在 Scala 中,当你调用一个函数,传递所有必须的参数,那么你就应用(apply)这个函数到这些参数上。而部分应用函数就是一个你不需要提供所有必须的参数到这个函数,而是提供部分参数
如果在一个函数的后面放置一个下划线占位符(带有空格),这个占位符不是代表单个参数(single parameter),而是代表整个参数列表(entire parameter list),(当然也可以对部分参数使用下划代替)这样将会得到一个部分应用函数
可以这样来理解这个下划线(underscope),就是转换一个def
的函数到一个函数值(function value)
可以用一句话来总结部分应用函数。对一个函数应用到部分参数,编译器生成一个函数字面量,然后在运行时实例化一个该函数字面量的函数值对象,当向这个函数值对象传递剩余的参数时,该函数值对象的 apply 方法被调用,该 apply 方法对原函数进行调用
闭包(Closures)
闭包这个词来源于捕获
绑定的自由变量(free variables)来关闭
函数字面量。此处的自由变量是指函数字面量在上下文中不能给出该变量的确切含义,相反,能够给出确切含义的变量叫做绑定变量(bound variables)
任何函数字面量不含有自由变量,叫做闭项
(closed term)
任何函数字面量含有自由变量,叫做开项
(open term),也就是说闭包最终是对自由变量的捕获来关闭开项产生函数值
Scala 的闭包是捕获
自由变量本身,而不是这个变量所指向的值。也就是说:闭包外面能够感知闭包对自由变量的修改,外部自由变量变化,闭包内部也能感知
特殊的方法调用
重复参数(Repeated Parameters)
可以指定参数列表中的最后一个参数是可以重复的。在函数内部,重复参数以数组的形式存在
def echo(foo: Int, bar: Int, others: String*) = {
// ...
}
当你想传递一个相同类型的数组给一个重复参数,可以采用如下的方式
val arr = Array("hello", "world")
echo(1, 2, arr: _*)
命名参数(Named arguments)
命名参数允许你以不同顺序传递参数,例如:
def func(foo: Int, bar: Int) = { /* ... */ }
func(1, 2)
func(bar = 2, foo = 1) // named arguments invoked.
默认参数值(Default parameter values)
默认参数值一般和命名参数配合使用,如果该参数有默认值,可以选择性的省略
该参数的传递
尾递归(tail recursive)
一个递归函数,在最后一行调用自身, Scala 会对其采用尾递归优化
,编译器会把这个函数使用 while loop
进行重写
尾递归有很多的好处,能够减少调用栈帧,防止堆栈溢出错误,减少内存使用,带来性能上的优化