scala中的call-by-name和call-by-value
scala中的call-by-name和call-by-valuevar/def/val/lazy val
例子scala> def f = {println("hello"); 1.0}
f: Double
scala> f
hello
res3: Double = 1.0
scala> f
hello
res4: Double = 1.0
scala> f
hello
res5: Double = 1.0
这里使用的是def,则每次使用的时候相当于获取一个新的函数。 scala> val f = { println("hello"); 1.0}
hello
f: Double = 1.0
scala> f
res6: Double = 1.0
scala> f
res7: Double = 1.0
这里使用val,则只有一次绑定,所以后面不再打印出hello。如果觉得迷糊,可以继续看下去。 scala> lazy val f = { println("hello"); 1.0}
f: Double = <lazy>
scala> f
hello
res8: Double = 1.0
scala> f
res9: Double = 1.0
这里使用的是lazy val,即为惰性求值的。可以看到在进行绑定的时候并没有打印出hello,也看不到其值,是因为只是定义了这个值。 在第二次进行使用的时候则会真正去计算这个值(类似于val),第一次会打印出hello,之后就不再打印出了 函数定义中的call-by-name和call-by-value看了上面的讲解还觉得迷糊的,就是不太理解call-by-name和call-by-value。 scala的call by name 和call by value最大的区别就是: call-by-name在调用的时候会重新根据name做计算,而call-by-value预先计算,然后保留计算值后一直使用这个value。 纯函数例子[1]一个Stack Overflow的例子。 def something() = {
println("calling something")
1 // return value
}
def callByValue(x: Int) = {
println("x1=" + x)
println("x2=" + x)
}
def callByName(x: => Int) = {
println("x1=" + x)
println("x2=" + x)
}
scala> callByValue(something())
calling something
x1=1
x2=1
scala> callByName(something())
calling something
x1=1
calling something
x2=1
这是因为在调用函数之前,call-by-value会先计算传入的表达式的值,因此每次都访问相同的值,且也不会再次调用。 但是,call-by-name,每次应用的时候,都会重新计算传入的表达式的值。 不纯函数的例子[2]上的都是纯函数,下面写一个非纯函数可能会更加明显。 举一个例子,假设有一只酒鬼,他最初有十元钱,每天喝酒都会花掉一元钱。设他有一个技能是数自己的钱,返回每天他口袋里钱的最新数目。 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[String]) = {
printByName(count)
printByValue(count)
}
}
/* 每天算一算,酒鬼还剩9块钱! 每天算一算,酒鬼还剩8块钱! 每天算一算,酒鬼还剩7块钱! 每天算一算,酒鬼还剩6块钱! 每天算一算,酒鬼还剩5块钱! 只算第一天,酒鬼还剩4块钱! 只算第一天,酒鬼还剩4块钱! 只算第一天,酒鬼还剩4块钱! 只算第一天,酒鬼还剩4块钱! 只算第一天,酒鬼还剩4块钱! */
可以看到,酒鬼最初5天每天都会数一下口袋里的软妹币(call-by-name),得到了每天喝酒花钱之后剩下的软妹币数量。 他觉得麻烦,于是想出了一个聪明的方法,在第六天的时候,他将口袋里还剩下的余额数写在了墙上(call-by-value),以后每天看一下墙上的数字,就知道自己还剩多少钱了。 死循环例子我们可以写出一个函数递归的”循环”。 如果在call-by-name下会停止,并不表示着在call-by-value下会停止。 // 这是一个死循环
def loop: Boolean = loop
// 用val定义时会做call-by-value,以下语句会block住
val x = loop
// 用def定义时,是做的call-by-name。故以下语句暂时不会执行,在用到y的时候才做evaluation
def y = loop
两者的比较call-by-value在进入函数体之前就对参数表达式进行了计算,这避免了函数内部多次使用参数时重复计算其值,在一定程度上提高了效率。 但是call-by-name的一个优势在于,如果参数在函数体内部没有被使用到,那么它就不用计算参数表达式的值了。在这种情况下,call-by-name的效率会高一点。 object Main {
def uSEOrNotUse(flag: Boolean,x: Int,y: => Int) = {
if (flag) {
x
}
else {
x + y
}
}
def main(args: Array[String]) = {
val flag = true
println(uSEOrNotUse(flag,1,2))
val flag = false
println(uSEOrNotUse(flag,2))
}
}
参考资料
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- scala – SBT项目发布2个工件的惯用方法是什么?
- Angular2 RC5强制一个将组件导入移动到NgModule声明 – 为什
- scala – 使用SecureSocial进行播放:在单独的线程池中运行
- shell脚本进阶一(for,while,continue,break,select等等
- bootstrap自定义样式-bootstrap侧边导航栏的实现
- bash – 一个grep(或sed或awk)命令,用于选择文件中特定起始
- angularjs – ocLazyLoad中的模块如何加载,并行或按顺序加载
- Bash:简单if语句
- 如何用yum安装两个不同版本的相同包
- bash-set -e和短期测试