Scala从零开始:函数参数的传名调用(call-by-name)和传值调用(call-by-value)
问题导读:
1.Scala函数参数传值调用?
2.Scala函数参数传名调用?
static/image/hrline/4.gif
引言
Scala的解释器在解析函数参数(function arguments)时有两种方式:先计算参数表达式的值(reduce the arguments),再应用到函数内部;或者是将未计算的参数表达式直接应用到函数内部。前者叫做传值调用(call-by-value),后者叫做传名调用(call-by-name)。
package com.doggie
object Add {
def addByName(a: Int, b: => Int) = a + b
def addByValue(a: Int, b: Int) = a + b
}
addByName是传名调用,addByValue是传值调用。语法上可以看出,使用传名调用时,在参数名称和参数类型中间有一个=》符号。以a为2,b为2 + 2为例,他们在Scala解释器进行参数规约(reduction)时的顺序分别是这样的:
addByName(2, 2 + 2)
->2 + (2 + 2)
->2 + 4
->6
addByValue(2, 2 + 2)
->addByValue(2, 4)
->2 + 4
->6
可以看出,在进入函数内部前,传值调用方式就已经将参数表达式的值计算完毕,而传名调用是在函数内部进行参数表达式的值计算的。这就造成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。对于有副作用(side-effect)的参数来说,这无疑造成了两种调用方式结果的不同。
酒鬼喝酒
举一个例子,假设有一只酒鬼,他最初有十元钱,每天喝酒都会花掉一元钱。设他有一个技能是数自己的钱,返回每天他口袋里钱的最新数目。代码如下:
package com.doggie
object Drunkard {
//最开始拥有的软妹币
var money = 10
//每天喝掉一个软妹币
def drink: Unit = {
money -= 1
}
//数钱时要算上被喝掉的软妹币
def count: Int = {
drink
money
}
//每天都数钱
def printByName(x: => Int): Unit = {
for(i <- 0 until 5)
println("每天算一算,酒鬼还剩" + x + "块钱!")
}
//第一天数一下记墙上,以后每天看墙上的余额
def printByValue(x: Int): Unit = {
for(i <- 0 until 5)
println("只算第一天,酒鬼还剩" + x + "块钱!")
}
def main(args: Array) = {
printByName(count)
printByValue(count)
}
}
我们使用成员变量money来表示酒鬼剩下的软妹币数量,每次发动drink技能就消耗一枚软妹币,在count中要计算因为drink消费掉的钱。我们定义了两种计算方式,printByName是传名调用,printByValue是传值调用。查看程序输出:
每天算一算,酒鬼还剩9块钱!
每天算一算,酒鬼还剩8块钱!
每天算一算,酒鬼还剩7块钱!
每天算一算,酒鬼还剩6块钱!
每天算一算,酒鬼还剩5块钱!
只算第一天,酒鬼还剩4块钱!
只算第一天,酒鬼还剩4块钱!
只算第一天,酒鬼还剩4块钱!
只算第一天,酒鬼还剩4块钱!
只算第一天,酒鬼还剩4块钱!
可以看到,酒鬼最初5天每天都会数一下口袋里的软妹币(call-by-name),得到了每天喝酒花钱之后剩下的软妹币数量,钱越来越少,他深感不能再这么堕落下去了。于是想出了一个聪明的方法,在第六天他将口袋里还剩下的余额数写在了墙上,以后每天看一下墙上的数字(call-by-value),就知道自己还剩多少钱了-___________________-怎么样,这个酒鬼够不够聪明?
两者的比较
传值调用在进入函数体之前就对参数表达式进行了计算,这避免了函数内部多次使用参数时重复计算其值,在一定程度上提高了效率。但是传名调用的一个优势在于,如果参数在函数体内部没有被使用到,那么它就不用计算参数表达式的值了。在这种情况下,传名调用的效率会高一点。讲到这里,有些同学不开心了:你这不是耍我么?函数体内部不使用参数,干嘛还要传进去?别着急,这里有一个例子:
package com.doggie
object WhyAlwaysMe {
var flag: Boolean = true
def useOrNotUse(x: Int, y: => Int) = {
flag match{
case true => x
case false => x + y
}
}
def main(args: Array) =
{
println(useOrNotUse(1, 2))
flag = false
println(useOrNotUse(1, 2))
}
}
参考:http://stackoverflow.com/questions/13337338/call-by-name-vs-call-by-value-in-scala-clarification-neededhttp://www.cnblogs.com/nixil/archive/2012/05/31/2528068.html
引用:http://blog.csdn.net/asongoficeandfire/article/details/21889375
学习了,谢谢分享~ 不错~ 感觉很多都是抄别人的文章
页:
[1]