ruby 疑难点之—— yield 和 yield self

时间:2021-12-14 17:20:01

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} # =>
foo2 {|x| puts x} # =>
foo3 {|x| puts x} # =>

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>
# => # Proc created with the "proc" keyword. Note & syntax when calling.
my_proc = proc { |n| puts n }
foo(&my_proc)
# => Proc
# => #<Proc:0x00000000e0b140@(ruby):12>
# => # Proc creates with Proc.new
my_proc = Proc.new { |n| puts n }
foo(&my_proc) # => 1
# => Proc
# => #<Proc:0x00000000e0b140@(ruby):16>
# => # Proc created with the "lambda" keyword. Nearly same thing.
my_proc = lambda { |n| puts n }
foo(&my_proc)
# => Proc
# => #<Proc:0x00000000e0b140@(ruby):20 (lambda)>
# =>

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} # 为空