Я хочу добавить одноэлементный метод с закрытием к объекту Ruby - PullRequest
2 голосов
/ 17 июля 2009

Я хочу добавить одноэлементный метод к определенному объекту. Мне бы хотелось, чтобы при первом вызове метода экземпляра объекта он выполнял некоторую работу, а затем создавал одноэлементный метод для указанного объекта с тем же именем (который содержит работу). При всех последующих вызовах указанного объекта синглтон-метод будет затенять метод экземпляра и будет вызываться.

Я знаю, как создать одноэлементный метод, моя проблема в том, что я хочу, чтобы созданный одноэлементный метод вызывал лямбду (l в данном случае). def не создает замыкание, поэтому я не могу ссылаться на переменную l (код ниже) при последующем вызове метода (l.call() закомментирован в этом примере). Я хочу знать, как создать замыкание при создании одноэлементного метода в конкретный объект. Любая помощь будет оценена. Спасибо.

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()

Дает следующие результаты при запуске: (я изменил '<' на '#', чтобы они отображались в html) </p>

Я говорю за все, я класс Метод

Это метод экземпляра, на который ссылается объектом Thing #Thing: 0x1d204>

Это метод экземпляра, на который ссылается объектом Thing #Thing: 0x1d1dc>

Это одноэлементный метод в Объект вещи #Thing: 0x1d204>

Это одноэлементный метод в Объект Thing #Thing: 0x1d1dc>

Ответы [ 3 ]

2 голосов
/ 19 марта 2011

Теперь, когда вышел 1.9, вы можете использовать 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
2 голосов
/ 17 июля 2009

Вы можете определить метод с помощью блока, используя define_method.

Пример:

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

Вот реализация метода define_singleton_method:

class Object
  def define_singleton_method(name, &block)
    eigenclass = class<<self; self end
    eigenclass.class_eval {define_method name, block}
  end
end
1 голос
/ 18 июля 2009

Ну, один из способов сделать это - упаковать его в переменную экземпляра:

(К вашему сведению, вы можете просто сделать class Thing, чтобы открыть Thing (это немного короче, чем использование #class_eval, и вам не нужно #instance_eval для определения методов внутри метода).

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

Это переопределит #speak, но только для этого экземпляра Thing. Другие экземпляры Thing будут по-прежнему иметь первоначальное определение.

Альтернативой, как указал Чак, является использование одноэлементного класса (он же метакласс, он же собственный класс), связанного с экземпляром. Класс singleton - это объект, в котором хранятся все одноэлементные методы, связанные с объектом. Вы можете получить контекст для оценки одноэлементного класса, используя забавный синтаксис class <<object ; ... ; end (аналогично контексту, заданному #class_eval обычными классами).

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...