yield in ruby

ruby中的关键词yield通常在一个方法中调用,对代码块进行控制,如果没有提供对应的代码块,调用该方法将导致异常。

块(block)是ruby的语法中一部分。
在ruby中当一个block被解析器识别到,该block将被关联到调用它的方法上,并替代方法中的yield关键词。

# mymethod方法中调用yield,所以调用mymethod必须传入一个block参数
def mymethod
  puts "this is mymethod before"
  yield
  puts "this is mymethod after"
end

mymethod
#=> this is mymethod before
#=> LocalJumpError: no block given (yield)

mymethod { puts "this is mymethod"}
#=> this is mymethod before
#=> this is mymethod
#=> this is mymethod after

那如何将mymethod中的block变为可选参数呢?
答案是使用block_given?

def mymethod
  puts "this is mymethod before"
  yield if block_given?
  puts "this is mymethod after"
end

mymethod
#=> this is mymethod before
#=> this is mymethod after

mymethod { puts "this is mymethod"}
#=> this is mymethod before
#=> this is mymethod
#=> this is mymethod after

给yield传参数

def another_method
  a = "1"
  b = "5"
  yield(a, b)
end

another_method {|x, y| puts "First arg is #{x}, Second arg is #{y}"}
#=> First arg is 1, Second arg is 5

给yield传self,即当前对象作为参数

module MyModule
  def self.name
    @name ||= "Tomi"
  end
  
  def self.name=(val)
    @name = val
  end
 
  def self.mymethod
    yield(self)  # self表示MyModule
  end
end

MyModule.mymethod do |config|
  config.name = "Eric"
end

MyModule.name
#=> "Eric"

保存yield返回值

def another_method
  a = 1
  b = 5
  result = yield(a, b)
  puts "The result is #{result}"
end

another_method {|x, y| x + y }
#=> The result is 6

重写map方法

# ruby中Array#map使用方法为: [1,2,3].map{|x| x * 2}  #=> [2,4,6]

class Array
  def mymap
    return self.dup unless block_given?
    arr = []
    self.each do |a|  #self is the receiver array
      arr << yield(a)
    end
    arr
  end
end

为什么ruby中有时传入block,但是有时传入&block?
block是一个local variable,但是&block传入的是一个该block的引用(reference)
那&block的作用是什么呢?

  1. 如果该对象是一个block,它将被转化为一个Proc
  2. 如果该对象是一个Proc,它将被转化为一个block
  3. 如果该对象是其它类型值,他将调用to_proc,然后转化为一个block.

示例:

def my_method(&my_block)
  my_block
end

# 第一种情况
传入的my_block为一个block,通过&符将其转化为一个Proc对象
my_method { "x" }   #=> # # #

那.map(&:something)怎么工作的呢?

大家熟悉的是该写法等价于.map{|x| x.something}。

简单的说它是.map(&:something.to_proc)的缩写,比如当foo是一个带有to_proc的对象,那你可以使用&foo将其传入一个方法,这样将调用foo.to_proc并且将此作为该方法的block使用

使用默认值来初始化对象:

使用yield(self)来声明对象赋值,在initialize的上下文中,self指向被initialized的对象。

class Car
  attr_accessor :color, :doors
  def initialize
     yield(self)
  end
end

car = Car.new do |c|
  c.color = "red"
  c.doors = 4
end

puts "My car's color is #{car.color}, it has #{car.doors} doors"

总结:
你可以认为block就是一段代码,yield帮助你将这些代码注入一个方法中,这意味着你可以让一个方法按照不同的方式运行,这样你可以重用一个方法做不同的事情。

ref: https://mixandgo.com/learn/mastering-ruby-blocks-in-less-than-5-minutes

你可能感兴趣的:(yield in ruby)