Pregunta

Deseo agregar un método singleton a un objeto en particular. Deseo que cuando se llame por primera vez un método de instancia en un objeto, realice algo de trabajo y luego cree un método Singleton para dicho objeto del mismo nombre (que contiene el trabajo). En todas las llamadas posteriores en dicho objeto, el método Singleton borraría el método de instancia y se llamaría.

Sé cómo crear un método singleton, mi problema es que quiero que el método singleton creado para llamar a una lambda (l en este caso). Def no crea un cierre, por lo que no puedo hacer referencia a la variable L (código a continuación) cuando el método se llama posteriormente (l.call() se comenta en este ejemplo) Deseo saber cómo puedo crear un cierre al crear un método singleton en un objeto en particular. Cualquier ayuda sería apreciada. Gracias.

class Thing
end

t = Thing.new
t2 = Thing.new

Thing.instance_eval() do
  def speak
    puts "I speak for all Things, I am a class method"
  end
end

Thing.class_eval() do
  def speak
    puts "This is the instance method referenced by the Thing object #{self}"
    r = "something I wish to hold on to, maybe expensive to calculate"
    l = lambda {puts r}
    instance_eval() do
      def speak()
        puts "This is the singleton method in the Thing object #{self}"
        #l.call() # I want this to work! How?
      end
    end
  end
end

Thing.speak()
t.speak()
t2.speak()
t.speak()
t2.speak()

Da los siguientes resultados cuando se ejecuta: (cambié '<' a '#' para que aparezcan en HTML)

Hablo por todas las cosas, soy un método de clase

Este es el método de instancia mencionado por el objeto de la cosa #Thing: 0x1d204>

Este es el método de instancia mencionado por el objeto de la cosa #thing: 0x1d1dc>

Este es el método Singleton en el objeto Thing #thing: 0x1d204>

Este es el método Singleton en el objeto Thing #thing: 0x1d1dc>

¿Fue útil?

Solución

Puede definir un método con un bloque usando define_method.

Ejemplo:

class Object
  def eigenclass
    class <<self; self end
  end
end

a = "Hello"
other_word = "World"
a.eigenclass.class_eval do
  define_method(:cliche) {"#{self} #{other_word}"}
end
a.cliche # => "Hello World"
"Goodbye".cliche # => NoMethodError: undefined method `cliche' for "Goodbye":String

Aquí hay una implementación de un define_singleton_method método:

class Object
  def define_singleton_method(name, &block)
    eigenclass = class<<self; self end
    eigenclass.class_eval {define_method name, block}
  end
end

Otros consejos

Ahora que 1.9 está fuera, puede usar define_singleton_method:

jruby --1.9 -S irb
irb(main):019:0> fn = -> { length * 10 }
=> #<Proc:0x77cb8e0f@(irb):19 (lambda)>
irb(main):020:0> s.define_singleton_method :length_times_ten, fn
=> #<Proc:0x77cb8e0f@(irb):19 (lambda)>
irb(main):021:0> s
=> "a string"
irb(main):022:0> s.length_times_ten
=> 80

Bueno, una forma de hacerlo sería empacarlo en una variable de instancia:

(FYI puedes hacer class Thing reabrir Thing (es un poco más corto que usar #class_eval, #instance_eval para definir métodos dentro de un método).

class Thing
  def speak
    puts "This is the instance method referenced by the Thing object #{self}"
    r = "something I wish to hold on to, maybe expensive to calculate"
    @l = lambda {puts r}
    instance_eval do 
      def speak()
        puts "This is the singleton method in the Thing object #{self}"
        @l[]
      end
    end
  end
end

Esto redefinirá #speak, pero solo para ese caso de Thing. Otros casos de Thing todavía tendrá la definición original.

La alternativa es, como señaló Chuck, usar la clase Singleton (también conocida como MetaClass, también conocida como Eigenclass) asociada con la instancia. La clase Singleton es el objeto que almacena todos los métodos singleton asociados con un objeto. Puede obtener el contexto para la evaluación de la clase Singleton utilizando el divertido class <<object ; ... ; end Sintaxis (similar al contexto dado por #class_eval por clases normales).

class Thing
  def speak
    puts "This is the instance method referenced by the Thing object #{self}"
    r = "something I wish to hold on to, maybe expensive to calculate"
    singleton_class = class <<self # open singleton class context for current instance
      # in this context, self now refers to the singleton class itself
      self
    end
    l = lambda {puts r}
    singleton_class.class_eval do
      # since we used #class_eval, local variables are still in scope
      define_method(:speak) do 
        puts "This is the singleton method in the Thing object #{self}"
        # since we used #define_method, local variables are still in scope
        l[]
      end
    end
  end
end
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top