Scala 方法和函数

Scala 方法和函数

方法与函数


Scala 有方法与函数,二者在语义上的区别很小。Scala 方法是类的一部分,而函数是一个对象可以赋值给一个变量。换句话来说在类中定义的函数即是方法。


Scala 中的方法跟 Java 的类似,方法是组成类的一部分。


Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。


Scala 中使用 val 语句可以定义函数,def 语句定义方法。

方法定义


方法定义由一个 def 关键字开始,紧接着是可选的参数列表,一个冒号 : 和方法的返回类型,一个等于号 = ,最后是方法的主体。


定义格式如下:

1
2
3
4
def functionName ([参数列表]) : [return type] = {
function body
return [expr]
}

以上代码中 return type 可以是任意合法的 Scala 数据类型。参数列表中的参数可以使用逗号分隔。
以下方法的功能是将两个传入的参数相加并求和:

1
2
3
4
5
6
7
object add{
def addInt( a:Int, b:Int ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
}

函数


Scala 也是一种函数式语言,所以函数是 Scala 语言的核心。以下一些函数概念有助于我们更好的理解 Scala 编程,定义方式上,和scala方法是一样的。


传值函数&传名函数
**
Scala的解释器在解析函数参数(function arguments)时有两种方式:先计算参数表达式的值(reduce the arguments),再应用到函数内部;或者是将未计算的参数表达式直接应用到函数内部。前者叫做传值调用(call-by-value),后者叫做传名调用(call-by-name)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
object Test {
def main(args: Array[String]) {
delayed(time());
}

def time() = {
println("获取时间,单位为纳秒")
System.nanoTime
}
def delayed( t: => Long ) = {
println("在 delayed 方法内")
println("参数: " + t) 此时计算 time()函数
t
}
}


注:scala函数体和方法体中的最后一行为返回值
结果:

1
2
3
4
在 delayed 方法内
获取时间,单位为纳秒
参数: 241550840475831
获取时间,单位为纳秒


=> Unit 与 () =>Unit的区别


简单来说, => Unit是 传名函数, 只传入了一个表达式, 在调用时才会去执行, 使用 code调用
() => 是传值函数, 传入的计算后的值,示例例如:

1
2
3
4
5
6
7
def function_1(t: () => Long): Unit = {
xxxx
}

def function_2(t: => Long): Unit = {
xxxx
}


指定函数参数名
**
不管在用什么语言在进行开发的过程中,对于函数的传参,我们几乎都是按照函数定义中,参数的顺序进行传参的,但是在Scala中会灵活一些,我们也可以通过指定函数参数名,并且不需要按照顺序向函数传递参数,实例如下:

1
2
3
4
5
6
7
8
9
object Test {
def main(args: Array[String]) {
printInt(b=5, a=7);
}
def printInt( a:Int, b:Int ) = {
println("Value of a : " + a );
println("Value of b : " + b );
}
}


可变参数
**
Scala 允许你指明函数的最后一个参数可以是重复的,即我们不需要指定函数参数的个数,可以向函数传入可变长度参数列表。Scala 通过在参数的类型之后放一个星号来设置可变参数(可重复的参数)。例如:

1
2
3
4
5
6
7
8
9
10
11
12
object Test {
def main(args: Array[String]) {
printStrings("Runoob", "Scala", "Python");
}
def printStrings( args:String* ) = {
var i : Int = 0;
for( arg <- args ){
println("Arg value[" + i + "] = " + arg );
i = i + 1;
}
}
}


**递归函数**
1
2
3
4
5
6
7
8
9
10
11
12
13
object Test {
def main(args: Array[String]) {
for (i <- 1 to 10)
println(i + " 的阶乘为: = " + factorial(i) )
}

def factorial(n: BigInt): BigInt = {
if (n <= 1)
1
else
n * factorial(n - 1)
}
}


默认参数值
Scala 可以为函数参数指定默认参数值,使用了默认参数,你在调用函数的过程中可以不需要传递参数,这时函数就会调用它的默认参数值,如果传递了参数,则传递值会取代默认值。实例如下:

1
2
3
4
5
6
7
8
9
10
object Test {
def main(args: Array[String]) {
println( "返回值 : " + addInt() );
}
def addInt( a:Int=5, b:Int=7 ) : Int = {
var sum:Int = 0
sum = a + b
return sum
}
}


高阶函数

高阶函数(Higher-Order Function)就是操作其他函数的函数。
Scala 中允许使用高阶函数, 高阶函数可以使用其他函数作为参数,或者使用函数作为输出结果。
以下实例中,
apply() 函数使用了另外一个函数 f 和 值 v 作为参数,而函数 f 又调用了参数 v:**

1
2
3
4
5
6
7
8
9
10
11
12
object Test {
def main(args: Array[String]) {

println( apply( layout, 10) )

}
// 函数 f 和 值 v 作为参数,而函数 f 又调用了参数 v
def apply(f: Int => String, v: Int) = f(v)

def layout[A](x: A) = "[" + x.toString() + "]"

}


匿名函数
**
匿名函数的语法很简单,箭头左边是参数列表,右边是函数体。使用匿名函数后,我们的代码变得更简洁了。


下面的表达式就定义了一个接受一个Int类型输入参数的匿名函数:

1
var inc = (x:Int) => x+1

上述定义的匿名函数,其实是下面这种写法的简写:

1
2
3
def add2 = new Function1[Int,Int]{  
def apply(x:Int):Int = x+1;
}


以上实例的 inc 现在可作为一个函数,使用方式如下:

1
var x = inc(7)-1

同样我们可以在匿名函数中定义多个参数:

1
var mul = (x: Int, y: Int) => x*y

mul 现在可作为一个函数,使用方式如下:

1
println(mul(3, 4))

我们也可以不给匿名函数设置参数,如下所示:

1
var userDir = () => { System.getProperty("user.dir") }

userDir 现在可作为一个函数,使用方式如下:

1
println( userDir() )


偏应用函数

偏应用函数也是一个蛮有意思的用法,**Scala 偏应用函数是一种表达式,你不需要提供函数需要的所有参数,只需要提供部分,或不提供所需参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Date
object Test {
def main(args: Array[String]) {
val date = new Date
log(date, "message1" )
Thread.sleep(1000)
log(date, "message2" )
Thread.sleep(1000)
log(date, "message3" )
}
def log(date: Date, message: String) = {
println(date + "----" + message)
}
}

执行以上代码,输出结果为:

1
2
3
4
5
$ scalac Test.scala
$ scala Test
Mon Dec 02 12:52:41 CST 2018----message1
Mon Dec 02 12:52:41 CST 2018----message2
Mon Dec 02 12:52:41 CST 2018----message3


实例中,log() 方法接收两个参数:date 和 message。我们在程序执行时调用了三次,参数 date 值都相同,message 不同。
我们可以使用偏应用函数优化以上方法,绑定第一个 date 参数,第二个参数使用下划线(_)替换缺失的参数列表,并把这个新的函数值的索引的赋给变量。以上实例修改如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Date
object Test {
def main(args: Array[String]) {
val date = new Date
val logWithDateBound = log(date, _ : String)
logWithDateBound("message1" )
Thread.sleep(1000)
logWithDateBound("message2" )
Thread.sleep(1000)
logWithDateBound("message3" )
}
def log(date: Date, message: String) = {
println(date + "----" + message)
}
}


执行以上代码,输出结果为和上面的是一样的。