ruby中的&操作符,以及各种闭包、proc的调用研究
class Array def iterate!(&code) #注意这里用了&符号 self.each_with_index do |n,i| self[i] = code.call(n) end end end arr = [1,2,3,4] arr.iterate! do |n| n ** 2 end #[1, 4, 9, 16]
今天读代码的时候,被这个&符号给蒙住了。ruby语言中时不时蹦出各种奇怪的符号,而且作用不明。还不好查得。
于是认真研究了一下。
&操作符的真正含义:proc对象和块之间的切换符号。&code 这是一个块, code 这是一个proc对象。简单的去掉&操作符,我们就能再次得到一个Proc对象。
===================================================
呃,其实如果没有上下文,完全说不清楚这段。还是看下面的例子吧,运行一趟就知道了。
def math_by_anonymouse_block(a,b) yield a,b end def math_by_proc(a,b,a_proc) a_proc.call(a,b) end def math_by_named_block(a,b,&namedblock) puts "math_by_proc a,b,namedblock" puts namedblock.class # &namedblock 是一个区块,namedblock (去掉&)是一个proc,&操作符是个切换开关。 puts math_by_proc a,b,namedblock puts "=======" #lambda mylambda = lambda &namedblock puts mylambda.class puts math_by_anonymouse_block a,b,&mylambda #哈哈,下面这样也可以 puts "math_by_proc a,b,mylambda" puts math_by_proc a,b,mylambda puts "=======" #proc myproc = proc &namedblock puts myproc.class puts math_by_anonymouse_block a,b,&myproc #哈哈,下面这样也可以 puts "math_by_proc a,b,myproc" puts math_by_proc a,b,myproc puts "=======" #puts &namedblock.class #区块不能直接赋值给一个变量。但是可以通过方法来传递 #myblock = &namedblock #运行不通 #puts myblock.class #所以我们用这种方式 puts "math_by_anonymouse_block" puts math_by_anonymouse_block a,b,&namedblock puts "=======" end math_by_named_block(2,3) {|x,y| x*y}
运行结果:
---------- ruby run ---------- math_by_proc a,b,namedblock Proc 6 ======= Proc 6 math_by_proc a,b,mylambda 6 ======= Proc 6 math_by_proc a,b,myproc 6 ======= math_by_anonymouse_block 6 ======= 输出完成 (耗时 0 秒) - 正常终止
区块就像是方法的额外匿名参数,任何方法在调用时,都可以在后面跟一个区块。只不过如果这个方法中有yield语句,它就会调用区块,如果没有yield语句,就无视这个区块。
比如:
def my_noyield puts "leave me alone. no yield !" end def my_yield puts "I call you! come on" yield end my_noyield { puts "I'm coming!"} my_yield { puts "I'm coming!"}
这是我们来定义一个带&操作符参数的方法,这就等于把之前的匿名参数变成了一个 &变量名 的参数。这个参数是一个也必然必须是一个 区块(不是proc,去掉&才是proc)
def my_third(&myproc) puts "I call you! come on" yield end my_third { puts "I'm coming!"}
这里 &myproc 是个区块,而myproc是个proc。对于proc,我们应该可以直接调用proc.call.
所以我们来试试第4个方法:不用yield,而在方法中直接call 这个proc
def my_fourth (&myproc) puts "I call you! come on" myproc.call end my_fourth { puts "I'm coming!"}
其实 &就是个切换开关,加上这个开关,就可以在proc和block之间切换。proc/lambda 也是一个开关(这两个是内核方法:Kernel#proc :Creates a new procedure object from the given block. Equivalent to Proc.new
.)
def my_fivth (&myproc) puts "I call you! come on" #&&myproc.call 这个不行,不能反转两次? c_proc = proc &myproc c_proc.call end my_fivth { puts "I'm coming!"} def my_sixth (&myproc) puts "I call you! come on" c_proc = lambda &myproc c_proc.call end my_sixth { puts "I'm coming!"}
补充一个小结:
可调用对象,有以下几种形式:块,proc,lambda,方法。不同类的可调用对象有细微区别。
但仍然可以通过以下方法在它们之间转换:包括Proc.new()方法,Methrod#to_proc()方法和&操作符。