函数(function) vs 方法(method)
函数
- 函数定义使用:
=>
, 如val f1 = (x:Int) => x+3
- f1的类型是Int=>Int,也即是个函数,输入是Int,返回是Int类型的函数
- 函数是一个完整的对象,和普通对象一样,有属于的类型,如
Function1<T,T>
(描述有一个输入参数,返回的是T),有方法,如apply
- apply方法包含了函数的body部分代码,当调用函数变量名(args)时,scala实际会转换为调用函数对象的apply(args)方法
- 函数可以赋值给变量和作为参数
- 函数既然是对象,消耗的内存比method多
常见函数形式
//f1函数参数是x,类型Int,自动推断返回类型是Int val f1 = (x:Int) => x+3 //REPL显示,冒号右边是类型,f1: Int => Int = <function1> var f1 = => 100 //函数必须有参数列表,该例报错 var f2 = () => 100 //函数可以有一个空的参数列表
- 有一种说法是将函数叫做无名方法,匿名方法
//匿名函数赋值给变量,最后一行如果是表达式,可以作为返回值 val sayHelloFunc = (name: String) => println("Hello, " + name)
- 函数定义使用:
方法
- 方法定义使用:
def
,如def m1(x:Int) = x+3
,参数是x,类型Int,返回类型是Int(自动推断) 常用方法形式
def m2 = 100;//方法可以没有参数列表 def m3() = 100 //方法可以有一个空的参数列表 def m4(x:Int,Y:Int) = {100} //多个参数,默认最后一行表达式是返回值,在REPL中显示m4: (x: Int, Y: Int)Int,说明是方法 //方法参数可以是函数 m1(x: => Int) //函数名是x,该函数无输入,返回Int m1(x: Int => Int) //参数是函数,函数名是x,该函数输入是Int,返回Int,冒号右边是类型,带=>是函数类型 //方法调用:无参数列表或空参数列表的方法,方法名就可以触发调用 m2 //m2是无参列表 m3 //m3是空参数列表 //方法可以转换为函数 val f3:(Int)=>Int = m4 //期望出现函数的地方,会自动转为函数,f3冒号后(Int)=>Int说明f3是函数类型,方法m4自动转为函数 val v3 = m4 //不期望出现函数的地方,方法并不会自动转换成函数,报错 val v3 = m4 _ //强制将方法转为函数,这种情况需要加`空格和_` //参数传递可以默认按值传递,如果参数类型是函数,则按名传递 def m_call_2(x: => Int)=List(x,x) //两次进行x方法或函数调用 def m_p(y:Int)=4 def m_t(x: (Int) => Int)={ println(x) //<function1> 说明x是一个函数,因为带参方法是不能直接使用的 println(x(1)) //4 调用了函数;但如果这里x是无参,直接使用x,不是输出函数形式,而是调用了,说明x是方法,所以比较疑惑 } m_t(m_p) //疑问 def m_val()=3 def m_t(x: => Int)={ //我理解这里参数类型是函数,但解释说这个传名参数x是一个方法,不是函数,不知道为啥 val y = x _ //强制将传名方法转为函数 println(y) //<function0> println(x) //3,为什么不是function呢,因为x是一个无名方法参数,所以可以通过名字x来调用 println(x) //3,为什么不是function呢,因为x是一个无名方法参数,所以可以通过名字x来调用 } m_t(m_val) //如果参数类型是方法,方法也可以这么传递参数,解释不通 //方法调用其他形式 缀操作符:op obj 被解释称obj.op 中缀操作符:obj1 op obj2被解释称obj1.op(obj2) 后缀操作符:obj op被解释称obj.op
方法其他补充:
- 构成:方法名、参数、返回类型、方法体
def sayHello(name: String, age: Int): Unit = {}
如果带参数,参数的类型必须给出
如果需要返回值,需要符号=;与Java不同,可用return也可以不用return返回值;类型可以自动推断,但如果最后一行调用自身,则无法推断
Unit类似java void,无返回,这种函数叫过程(procedures)- 支持默认参数,如
def sayHello(name: String, age: Int=29)
- 支持混合使用未带值参数和带值参数,注意未带值数必须在带值
参数前面 变长参数:参数个数不定
def sum(nums: Int*) = {}
普通调用方式sum(1, 2, 3, 4, 5)
序列调用方式sum(1 to 5: _*)
按名传递
def getFirst(x: Int, y: =>Int) = { x } def loop:Int = loop //可以将loop给loop,很奇怪,返回类型Int,函数体是loop getFirst(loop, 3) //死循环,函数体里会触发loop getFirst(3, loop) //不会死循环,loop按名传递,使用的时候才触发
- 方法定义使用:
高阶函数:以下不区分函数和方法
- 接收其他函数作为参数的函数或者返回值是函数
//接收函数作为参数
def greeting(func: (String) => Unit, name: String) { func(name) }
//使用
greeting((name: String) => println("Hello, " + name), "leo")
//高阶函数可以自动推断出参数类型,而不需要写明类型
greeting((name) => println("Hello, " + name), "leo")
//对于只有一个参数的函数,可以省掉小括号
greeting(name => println("Hello, " + name), "leo")
//通过通配符_,可以省略输入参数,_个数代表参数的个数
greeting(println(_), "leo")
Array(1, 2, 3, 4, 5).map(2 * _)
(1 to 9).map("*" * _).foreach(println _)
(1 to 20).filter(_ % 2 == 0)
//reduceLeft: 从左侧元素开始,进行reduce操作,即先对元素1和元素2进行处理,然后将结果与元素3处理,再将结果与元素4处理,依次类推
(1 to 9).reduceLeft( _ * _) //多个参数再调用的时候依次填入
Array(3, 2, 5, 4, 10, 1).sortWith(_ < _)
//返回值为函数
def getGreetingFunc(msg: String) = (name: String) => println(msg + ", " + name)
闭包
- 函数在变量不处于其有效作用域时,还能够对变量进行访问,即为闭包
/* 闭包就是拥有对外部函数或类的变量的引用,
从而可以在外面函数栈执行结束以后,依然握有外面函数栈/堆变量的引用
,并可以改变他们的值。
*/
def main(args: Array[String]) {
var factor = 3
def multiplier(i:Int) = {
i * factor
}
/* 我理解是调用multiplier(2)的时候,会进行栈的切换,
导致局部变量factor看不见了,而闭包却可以看见,还能修改外部变量
*/
println(multiplier(2))
}
Currying函数
- 简单说,通过返回函数可以将原来一次接收多个参数的一个函数,转换为多个函数多次调用参数的情况;在函数部分重用,定义的时候简便
//带有两个括号定义的函数叫currying函数
def sum3(a: Int)(b: Int) = a + b
//调用方式
sum2(1)(1)
//类似以下定义
def sum2(a: Int) = (b: Int) => a + b //sum2: (a: Int)Int => Int,参数是a,返回类型是函数
def sum(a: Int, b: Int) = a + b