美文网首页
yield in ruby

yield in ruby

作者: EvanCui | 来源:发表于2019-04-13 17:09 被阅读0次

    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" }   #=> #<Proc:0x00007
    
    #第二种情况
    传入一个Proc对象
    my_proc = Proc.new { "x" }
    my_method(my_proc)  #ArgumentError: wrong number of arguments
    my_method(&my_proc)  #=> #<Proc:0x00007
    
    #第三种情况
    传入一个既不是Proc也不是block的参数,而是一个符号 :even?
    my_method(&:even?)  #=> #<Proc:0x00007
    等价于my_method(&:even?.to_proc)
    

    那.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

          本文链接:https://www.haomeiwen.com/subject/larrwqtx.html