ruby中的&操作符,以及各种闭包、proc的调用研究

时间:2021-01-13 22:45:32
 
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()方法和&操作符。