Scala 中允许无参数的函数作为另一函数的参数传递进去,也就是传名参数(call-by-name)。函数在 Scala 中是第一类公民(first-class citizen),其实函数也是一种特殊的对象,既然实质上是对象,当然也就能够作为另一个函数的参数传递进去了。
传名参数也可以简单理解为不含参数的函数被传递给另一个参数。或许你可以看看我的另一篇相关文章:Scala:用传名参数实现断言机制及其特点 。
我们可以用传名参数以及尾递归来实现一个很熟悉的语法结构 while 循环,代码如下面的 myWhile 函数:
def myWhile(cond: => Boolean)(body: => Unit): Unit = {
if(cond){
body // 方法体内必须包含循环条件的修改
myWhile(cond)(body)
}
}
var num = 10
myWhile(num > 0){
println(num)
num -= 1 // 此句修改循环的条件
}
注意我们在上面的myWhile 函数中的 body 必须含有驱使循环趋向结束的语句,即函数体中包含修改 cond 条件的语句。否则,在myWhile(cond)(body) 中则可能会出现每次if(cond) 都为 true 的情况,那就成了毫无用处的死循环了。
myWhile 中有两个参数列表(使用了 Curring 技巧),都是传名参数的形式,即“名称:=> Unit”的格式,本来无参的函数我们也可以写成“名称:( ) => Unit”的格式,但是这两者在调用发是有所不同的,请看Scala:用传名参数实现断言机制及其特点 中的前面部分。
前面说了 Scala 中的函数也是对象,那么传名参数所对应传递进来的无参函数也将被 Scala 编译器构建成一个对象。得特别注意的是,传名参数(无参函数)在传入另一函数直到在函数体中被具体调用前,是不会被计算的。这也可以看一下Scala:用传名参数实现断言机制及其特点 中后面部分的一点点解释。
我们都很熟悉其他编程语言中的 while 循环结构,但是具体它们是不是被设计成尾递归的形式得以实现的,我也不清楚。不过书上说 Scala 中的尾递归调用会被 Scala 编译器优化成实际跟常见的 while 循环。因此,我觉得其他语言中的 while 循环结构应该不是用上面这种方法实现的。
觉得 Scala 中这种可以构建自己所需的语言结构确实挺有趣,也比较启发习惯于命令式编程的我。呵呵…
同样,既然可以类似的实现 while 循环结构,我们也可以实现 do-while 循环,代码如下:
class DoWhile(body: => Unit){
// 相当于do-while中的while
def doWhile(cond: => Boolean){
body
if(!cond) doWhile(cond) // 都传入cond且真值刚好相反
}
}
// 相当于do,返回DoWhile类实例
def myDo(body: => Unit): DoWhile = new DoWhile(body)
// 测试
var num = 10
myDo{
print(num + " ")
num -= 1
} doWhile(num == 0) //myDo返回DoWhile实例,再调用其doWhile函数
在上面这个例子中,之所以要设计出一个DoWhile 类,并且定义了myDo函数的返回类型为DoWhile 类实例,是为了让返回的DoWhile 类实例直接调用其doWhile 函数。这样一来,就非常类似于常见的 do-while 循环结构了。
同样,myDo 函数中 body 也必须且将会修改 cond 以使循环趋向结束。此外还应该注意if(!cond) doWhile(cond) 一句中判断是否继续调用doWhile 函数与传入doWhile 函数的 cond 是刚好相反的。
本文转自 xxxx66yyyy 51CTO博客,原文链接:http://blog.51cto.com/haolloyin/388232,如需转载请自行联系原作者