ruby 疑难点之—— yield 和 yield self
yield
所有的"方法(methods)"隐式跟上一个"块(block)"参数。
块参数也可以明确给定,形式就是在参数前面加一个"&",比如 def fn(arg1, arg2, &block) end,其中的 &block 就是明确给定的块参数。
块参数的动作,可以通过调用 call() 方法执行,还可以用 yield 来执行 —— yield 其实就是一个语法糖。
所以以下几种写法常常是等价的:
#method receives an invisible block argument def foo1() yield 1 end #specify it explicitly def foo2(&block) yield 1 end #yield is equal to block.call def foo3(&block) block.call(1) end #function call foo1 {|x| puts x} # => 1 foo2 {|x| puts x} # => 1 foo3 {|x| puts x} # => 1
Proc
前面说到所有方法都可以隐式或显式指定一个块参数,那么块参数到底是什么呢?
答案是 Proc 对象,一个具有 call 方法的对象。
Proc 对象的定义有几种形式:
- 直接使用 {}
- 使用 Proc.new {}
- 使用 proc {}
- 使用 lambda {}
#yield is equal to block.call def foo(&block) puts block.class puts block.to_s yield 1 end #function call # Proc created using {} syntax foo {|x| puts x} # => Proc # => #<Proc:0x00000000e0b140@(ruby):9> # => 1 # Proc created with the "proc" keyword. Note & syntax when calling. my_proc = proc { |n| puts n } foo(&my_proc) # => Proc # => #<Proc:0x00000000e0b140@(ruby):12> # => 1 # Proc creates with Proc.new my_proc = Proc.new { |n| puts n } foo(&my_proc) # => 1 # => Proc # => #<Proc:0x00000000e0b140@(ruby):16> # => 1 # Proc created with the "lambda" keyword. Nearly same thing. my_proc = lambda { |n| puts n } foo(&my_proc) # => Proc # => #<Proc:0x00000000e0b140@(ruby):20 (lambda)> # => 1
yield self
在一个对象中,self 表示是一个当前对象的引用。
所以,常见的 yield self if block_given? 中的 self 就和其它地方使用 self 一样,没什么特殊的。
class C1 def foo(&block) puts block.class puts block.to_s yield self if block_given? yield "AAAAAAAAA" end end class C2 def foo(&block) puts block.class puts block.to_s yield self if block_given? yield "BBBBBBBBB" end def to_s "XXXXXXXXXX" end end c1 = C1.new c1.foo {|x| puts x} # => Proc # => #<Proc:0x00000001c84aa0@(ruby):23> # => #<Context::C1:0x00000001c84af0> # => AAAAAAAAA c2 = C2.new c2.foo {|x| puts x} # => Proc # => #<Proc:0x00000001c842f8@(ruby):26> # => XXXXXXXXXX # => BBBBBBBBB
注意事项
method 定义中 &block 参数必须在最后
# 正确示例 def foo(arg1, arg2, &block) puts block end #function call block = proc {|x| puts x} foo( 1, 2, &block) # => #<Proc:0x000000011f3aa0@(ruby):14> #错误示例 def foo(arg1, &block, arg2) # => (ruby): syntax error puts block end
yield 相当于是 block.call() 方法的调用,所以参数个数也需要对应
def foo() yield 1,2,3 # 这里的 1 2 3 就是传递的参数 end #function call foo {|x| puts x} # => 1 foo {|x,y,z| puts z} # => 3 foo {|x,y,z,k| puts k} # 为空