[转载]scala中的by-name parameter详解

[转载自 Gossip@caterpillar]

个人翻译为简体中文, 供自己学习使用

[b][size=large]scala中的by-name parameter介绍:[/size][/b]

到目前為止,所定义的带参数的函数,必须先根据参数的表达式计算出值,然后才能调用该函数。例如:

def sum(a: Int, b: Int) = a + b

println(sum(1 + 2, 3 + 4)) // 显示10

在调用函数sum之前,1+2和3+4都会先被计算出结果,然后以sum(3, 7)的方式来调用sum函数,a 和 b 这两个参数称为传值参数(By-value paramenter)。

考虑另一种情况, 如果你想开发以下函数:

def unless(cond: Boolean, func: () => Any) = {
if(!cond) {
func()
}
}

unless(false, () => println("XD"))
unless(true, () => println("Orz"))

这个函数的作用是:除非cond为true, 否则就执行传入的函数字面量。所传入的函数字面量是无参的函数,不过 () => 仍然不能省略,也就是说你不能够写成以下形式:

unless(false, println("XD")) // 该形式编译时错误

如果你想要省略()=>, 则可以这样定义该函数:

def unless(cond: Boolean, expr: => Any) = {
if(!cond) {
expr
}
}

unless(false, println("XD"))
unless(true, println("Orz"))

在这个定义下, expr的参数类型是 =>Any , 称之为传名参数 (By-name parameter)。
可以看到, 在调用,unless函数时, 直接写了println("XD")这个表达式,省略了()=>部分。
事实上, 省略()=>并不是这个例子的重点, 重点在于println("XD")表达式,不会被马上执行,真正执行是在cond参数为false时,也就是unless函数中if结果为true时。

传名参数的命名由此而来, 献给予所给定的参数表达式一个名称,然后以这个名称代替参数表达式的执行结果来调用该函数,真正执行该表达式的位置则是在你所定义的函数内部。

需要注意的是,传名参数并不是一个函数字面量,它只是一个表达式的别名。所以你不能按照下面的写法来用:

def unless(cond: Boolean, expr: => Any) = {
if(!cond) {
expr() // 不能有括號
}
}

对于scala中短路的与&&和短路或 || 其实都是方法的名称, 那它们是如何实现短路作用的呢?其实就是用传名参数实现的。以下是模拟短路与 && 的函数:

def and(c1: Boolean, c2: => Boolean) = {
if(c1) c2 else c1
}

println(and(5 > 3, 10 > 3)) // true
println(and(5 > 3, 10 < 3)) // false
println(and(1 > 3, 10 > 3)) // false

以下這個範例可以證明上面的and函式確實有捷徑運算的作用:

def and(c1: Boolean, c2: => Boolean) = {
if(c1) c2 else c1
}
println(and(5 > 3, {print("run.. "); 10 > 3})) // 顯示 run.. false
println(and(1 > 3, {print("run.. "); 10 > 3})) // 顯示 false

由于第一个and函数被调用时,5>3成立,所以必须要检测第二个表达式, 因而会显示run...的信息。而第二个and函数调用时,1>3为false,所以就不需要检测第二个表达式了,直接返回false,所以不会显示run...的信息。

在scala中,没有until的功能,也就是除非条件成立,否则就反复执行一些功能,一下是模拟until功能的函数:

def until(cond: => Boolean, expr: => Unit) {
if(!cond) {
expr
until(cond, expr)
}
}

var count = 10
until(count == 0, {
println(count)
count -= 1
})

如果将第一个参数改为cond:Boolean,那么until函数将永远不会停止, 因为count == 0会先运算为false,在传递给until函数。所以until函数中的!cond将永远为true,所以一直跑下去。直到堆栈溢出为止。

事实上, 如果使用scala中的currying特性, 可以让unless和util函数看起来就像语言的内置控制结构一样。这也是scala支持扩展性的一个方式。

(完)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值