ruby - snippet - title-tag




Wie kann ich eine Referenz zu einer Methode erhalten? (3)

Ist es in Ruby möglich, einen Verweis auf Methoden eines Objekts zu erhalten (ich würde gerne wissen, ob dies ohne procs / lambdas möglich ist), zum Beispiel den folgenden Code berücksichtigen:


class X
  def initialize
    @map = {}
    setup_map
  end

  private
  def setup_map
    # @map["a"] = get reference to a method
    # @map["b"] = get reference to b method
    # @map["c"] = get referebce to c method
  end

  public
  def call(a)
    @map["a"](a) if a > 10
    @map["b"](a) if a > 20
    @map["c"](a) if a > 30
  end

  def a(arg)
     puts "a was called with #{arg}"
  end

  def b(arg)
     puts "b was called with #{arg}"
  end

  def c(arg)
    puts "c was called with #{arg}"
  end
end

Ist es möglich, so etwas zu tun? Ich möchte procs / lambdas vermeiden, weil ich in der Lage sein möchte, das Verhalten von A, B, C durch Unterklassen zu ändern.


Ruby-Methoden sind keine erstklassigen Objekte. Es implementiert OO mit Nachrichtenübergabe.

class X
  def call(a)
    self.send(:a, a) if a > 10
    self.send(:b, a) if a > 20
    self.send(:c, a) if a > 30
  end

  def a(arg)
     puts "a was called with #{arg}"
  end

  def b(arg)
     puts "b was called with #{arg}"
  end

  def c(arg)
    puts "c was called with #{arg}"
  end
end

Oder rufe sie einfach direkt an:

def call(a)
  self.a(a) if a > 10
  self.b(a) if a > 20
  self.c(a) if a > 30
end

Sie können dies mit lambdas tun, während Sie die Fähigkeit beibehalten, das Verhalten in Unterklassen zu ändern:

class X
  def initialize
    @map = {}
    setup_map
  end

  private
  def setup_map
    @map["a"] = lambda { |a| a(a) }
    @map["b"] = lambda { |a| b(a) }
    @map["c"] = lambda { |a| c(a) }
  end

  public
  def call(a)
    @map["a"].call(a) if a > 10
    @map["b"].call(a) if a > 20
    @map["c"].call(a) if a > 30
  end

  def a(arg)
     puts "a was called with #{arg}"
  end

  def b(arg)
     puts "b was called with #{arg}"
  end

  def c(arg)
    puts "c was called with #{arg}"
  end
end

Sie wollen Object#method :

---------------------------------------------------------- Object#method
     obj.method(sym)    => method
------------------------------------------------------------------------
     Looks up the named method as a receiver in obj, returning a Method 
     object (or raising NameError). The Method object acts as a closure 
     in obj's object instance, so instance variables and the value of 
     self remain available.

        class Demo
          def initialize(n)
            @iv = n
          end
          def hello()
            "Hello, @iv = #{@iv}"
          end
        end

        k = Demo.new(99)
        m = k.method(:hello)
        m.call   #=> "Hello, @iv = 99"

        l = Demo.new('Fred')
        m = l.method("hello")
        m.call   #=> "Hello, @iv = Fred"

Jetzt wird dein Code:

private
def setup_map
  @map = {
    'a' => method(:a),
    'b' => method(:b),
    'c' => method(:c)
  }
  # or, more succinctly
  # @map = Hash.new { |_map,name| _map[name] = method(name.to_sym) }
end

public
def call(arg)
  @map["a"][arg] if arg > 10
  @map["b"][arg] if arg > 20
  @map["c"][arg] if arg > 30
end




reference