scala 除了支持方法(方法是对object的操作,也就是java中定义的class的方法)外还支持函数的; C/C++、Python 也有函数, 而在Java中我们只能通过静态方法来模拟。
函数
函数定义
要定义一个函数,你要给出函数的名称、参数和函数体 ; 例如:
def fac(x:Int) = {
var result = 1
for (i <- 1 to x) result = result * i
result
}
你必须给出所有参数的类型。 不过,只要函数不是递归的,你就不需要指定返回类型, scala 会根据 = 号右边的表达式推导出返回的类型。或进行显式定义为:
def fac(x:Int) :Int = {
var result = 1
for (i <- 1 to x) result = result * i
result
}
注: 本例中不需要使用return,当然我们也可以使用return来立即从函数中退出, 不过在scala中使用return并不常见, 我们最好要适应没有return的日子, 我们可以把 return 当做 函数版本的 break 语句, 仅在需要时进行调用。
默认参数和带名参数
我们在调用某些函数时不显式的给出所有参数值,对于这些函数可以使用默认值:
scala> def show(msg:String, left:String="[", right:String="]"){
| println("%s%s%s".format(left, msg, right))
| }
show: (msg: String, left: String, right: String)Unit
scala> show("hello") // 调用方式1
[hello]
scala> show("hello", "<", ">") // 调用方式2
<hello>
scala> show(left ="info[", msg ="hello") // 调用方式3
info[hello]
注: 这里定义的函数在Scala中被称为过程; Scala 对于不返回值的函数有特殊的表示法, 如果函数体包含在 {} 中但前面没有 = 号, 那么返回的类型为Unit。 这种函数被称为 过程(procedure) , 也可以显式定义为 Unit返回类型:
def show( ... ): Unit = {
......
}
不定长参数
实现一个可变长参数的函数, 例如:
scala> def show(args: Any *){
| for (arg <- args){
| arg match {
| case i: Int =>
| println("type int: %d".format(i))
| case s: String =>
| println("type string: %s".format(s))
| case _ =>
| println("unknow type arg ")
| }
| }
| }
show: (args: Any*)Unit
scala> show(1, "2", 3.0)
type int: 1
type string: 2
unknow type arg
scala> show(1 to 3: _*) // 追加 : _* 告诉编译器希望参数被当做参数序列处理
type int: 1
type int: 2
type int: 3
作为值的函数
在scala中, 函数时 “头等公民” ,就和数字一样。 你可以在变量中存放函数(有点像C++中的函数指针):
scala> def add(x:Int, y:Int) = x+y
add: (x: Int, y: Int)Int
scala> val func : (Int, Int) => Int = add
func: (Int, Int) => Int = $$Lambda$1502/1003761391@5fa842b5
scala> val func1 = add _ // _ 占位符
func1: (Int, Int) => Int = $$Lambda$1503/817093103@5d32d8f
匿名函数
Scala 中定义匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。使用匿名函数后,我们的代码变得更简洁了。
scala> val func = (x: Int) => 2*x
func: Int => Int = $$Lambda$1517/285812804@2640893e
scala> val func = 2*(_:Int) // 精简版, 通过 _ 占位符指定类型来帮助函数推导出返回值
func: Int => Int = $$Lambda$1556/399984046@787aa9f1
scala> val func: Int=>Int = 2*_ // 精简版, 通过指定func的函数的类型
func: Int => Int = $$Lambda$1557/143470731@6ed810f3
原型
scala> def func = new Function1[Int, Int] {
| def apply(v: Int) : Int= {
| 2 * v
| }
| }
func: Int => Int
高阶函数
高阶函数(Higher-Order Function)就是操作其他函数的函数。Scala 中允许使用高阶函数, 高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。
例如: 常用要熟悉的高阶函数
scala> val lst = 1::2::3::Nil
lst: List[Int] = List(1, 2, 3)
scala> lst.map(2*_)
res35: List[Int] = List(2, 4, 6)
scala> lst.foreach(println)
1
2
3
看下高阶函数如何定义通过源码:
/** 我们最关心的仅是 (f: A => B)函数
* A:表示List[T] T的类型,
* B:表示map后的List[T] T的类型, 取决于传入的函数定义
*/
final override def map[B, That](f: A => B)(implicit bf: CanBuildFrom[List[A], B, That]): That = {
if (bf eq List.ReusableCBF) {
if (this eq Nil) Nil.asInstanceOf[That] else {
val h = new ::[B](f(head), Nil)
var t: ::[B] = h
var rest = tail
while (rest ne Nil) {
val nx = new ::(f(rest.head), Nil)
t.tl = nx
t = nx
rest = rest.tail
}
h.asInstanceOf[That]
}
}
else super.map(f)
}
/** 这个比较简单, 直接进行List遍历调用 f函数
*/
@inline final override def foreach[U](f: A => U) {
var these = this
while (!these.isEmpty) {
f(these.head)
these = these.tail
}
}
闭包
在函数中可以(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。运行时,一旦外部的 函数被执行,一个闭包就形成了,闭包中包含了内部函数的代码,以及所需外部函数中的变量的引用。其中所引用的变量称作上值(upvalue)。
闭包一词经常和匿名函数混淆。这可能是因为两者经常同时使用,但是它们是不同的概念。
scala> val flag = 2
flag: Int = 2
scala> def func(x:Int) = (v: String) =>{
| for(i <- 1 to flag*x){
| println(v * i)
| }
| }
func: (x: Int)String => Unit
scala> val f = func(1)
f: String => Unit = $$Lambda$1574/696606344@dcd6f19
scala> f("*")
*
**
scala> val f = func(2)
f: String => Unit = $$Lambda$1574/696606344@74d16083
scala> f("*")
*
**
***
****
柯里化(Currying)
柯里化把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数。
可以换个逻辑理解:
看做是一个数学问题,一个逐次消元的过程。当把函数的元全消掉,就得到了值,值就是零元函数。 例如:
- 二元函数 f(x,y) = x+y
- 在y=1时, g(x) = f(x,1) = x+1
- 在x=2时, g(2) = 2+1=3
接受两个参数的函数
// 函数类型 (Int, Int) => Int
scala> def add(x:Int, y:Int) :Int = {
| x+y
| }
add: (x: Int, y: Int)Int
柯里化
// 函数类型 Int => (Int => Int)
scala> def add(x:Int) = (y:Int) => x+y
add: (x: Int)Int => Int
那么如何从 函数转换为 柯里化?
scala> def add(x:Int, y:Int) :Int = x+y
add: (x: Int, y: Int)Int
scala> val f1= add _
f1: (Int, Int) => Int = $$Lambda$1580/18999287@57715987
scala> f1.curried // 柯里化后的函数形式
res39: Int => (Int => Int) = scala.Function2$$Lambda$1433/579868782@12855741
- 关于 curried 函数
// 看 scala Function2 的定义, 它会将自己转换一个柯里化版本的函数
trait Function2[@specialized(scala.Int, scala.Long, scala.Double) -T1, @specialized(scala.Int, scala.Long, scala.Double) -T2, @specialized(scala.Unit, scala.Boolean, scala.Int, scala.Float, scala.Long, scala.Double) +R] extends AnyRef { self =>
/** Apply the body of this function to the arguments.
* @return the result of function application.
*/
def apply(v1: T1, v2: T2): R
/** Creates a curried version of this function.
*
* @return a function `f` such that `f(x1)(x2) == apply(x1, x2)`
*/
@annotation.unspecialized def curried: T1 => T2 => R = {
(x1: T1) => (x2: T2) => apply(x1, x2)
}
/** Creates a tupled version of this function: instead of 2 arguments,
* it accepts a single [[scala.Tuple2]] argument.
*
* @return a function `f` such that `f((x1, x2)) == f(Tuple2(x1, x2)) == apply(x1, x2)`
*/
@annotation.unspecialized def tupled: Tuple2[T1, T2] => R = {
case Tuple2(x1, x2) => apply(x1, x2)
}
override def toString() = "<function2>"
}
那么问题来了,绕那么大的圈, 柯里化有什么实用场景呢?
- 例如 控制抽象
def until(condition: => Boolean)(block: => Unit): Unit = {
if ( !condition ){
block
until(condition)(block)
}
}
var flag = 3
// 柯里化
until( flag<0 ) {
println(flag)
flag -= 1
}
// 如果是接受多参函数
// def until(condition: => Boolean, block: => Unit): Unit = { ... }
// until( ... , { .... } )
虽然没有柯里化也可以实现相应功能,看起来可能并不美观,但柯里化并不是看起来美观那么简单, 而是函数式编程的一个新的概念。
优点:
- 代码复用;
- 代码逻辑设计分层;
- 抽象计算等;