[转载自 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支持扩展性的一个方式。
(完)
个人翻译为简体中文, 供自己学习使用
[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支持扩展性的一个方式。
(完)