Как я могу запустить функции Ruby в обратном направлении? - PullRequest
2 голосов
/ 11 ноября 2010

Я хочу иметь класс, который запускает функции в обратном направлении, например, foo.method1.method2.method3, и я хочу, чтобы функции запускали method3 method2, а затем method1.Но это идет 1 2 3 сейчас.Я думаю, что это называется отложенной оценкой, но я не уверен.

Я почти ничего не знаю о Ruby, поэтому, пожалуйста, извините этот вопрос, если он прост, и я должен знать это уже.

Ответы [ 5 ]

5 голосов
/ 11 ноября 2010

Конечно, вы можете сделать это.Звучит так, как будто вы на правильном пути, думая о ленивой оценке, вам просто нужно завершить каждый список вызовов методов с помощью метода, который запускает методы в очереди.

class Foo

  def initialize
    @command_queue = []
  end

  def method1
    @command_queue << :_method1
    self
  end

  def method2
    @command_queue << :_method2
    self
  end

  def method3
    @command_queue << :_method3
    self
  end

  def exec
    @command_queue.reverse.map do |command|
      self.send(command)
    end
    @command_queue = []
  end

  private

  def _method1
    puts "method1"
  end

  def _method2
    puts "method2"
  end

  def _method3
    puts "method3"
  end

end

foo = Foo.new
foo.method1.method2.method3.exec


method3
method2
method1
4 голосов
/ 11 ноября 2010

Может быть, вы могли бы связать вызовы методов, построить стек оценки и выполнить позже.Это требует, чтобы вы вызвали дополнительный метод для оценки стека.Вы можете использовать частные методы для реальных реализаций.

class Weirdo
  def initialize
    @stack = []
  end

  def method_a
    @stack << [:method_a!]
    self #so that the next call gets chained
  end

  def method_b(arg1, arg2)
    @stack << [:method_b!, arg1, arg2]
    self
  end

  def method_c(&block)
    @stack << [:method_c!, block]
    self
  end

  def call_stack
    while @stack.length > 0 do
      send *@stack.pop
    end
  end

  private

  # actual method implementations
  def method_a!
    # method_a functionality
  end

  def method_b!(arg1, arg2)
    # method_b functionality
  end

  def method_c!(&block)
    # method_c functionality
  end
end

, чтобы вы могли сделать что-то вроде

w = Weirdo.new
w.method_a.method_b(3,5).method_c{ Time.now }
w.call_stack # => executes c first, b next and a last.

Обновить

Похоже, мне удалось пропустить Ответ Пита и опубликован почти точно такой же ответ.Единственная разница заключается в возможности передавать аргументы во внутренний стек.

3 голосов
/ 12 ноября 2010

Что вам действительно нужно, так это прокси-класс, который захватывает сообщения, переворачивает их, а затем перенаправляет их в реальный класс:

# This is the proxy class that captures the messages, reverses them, and then forwards them
class Messenger
  def initialize(target)
    @obj = target
    @messages = []
  end

  def method_missing(name, *args, &block)
    @messages << [name, args, block]
    self
  end

  # The return value of this method is an array of the return values of the invoked methods
  def exec
    @messages.reverse.map { |name, args, block| @obj.send(name, *args, &block) }
  end
end

# this is the actual class that implements the methods you want to invoke
# every method on this class just returns its name
class Test
  def self.def_methods(*names)
    names.each { |v| define_method(v) { v } }
  end

  def_methods :a, :b, :c, :d
end

# attach the proxy, store the messages, forward the reversed messages onto the actual class
# and then display the resulting array of method return values
Messenger.new(Test.new).a.b.c.exec.inspect.display  #=> [:c, :b, :a]
0 голосов
/ 16 ноября 2010

Это самое интересное и полезное; Я думаю, что шаблон прокси действительно может быть использован. Я думал, читая ваш первоначальный пост об отладчике, который IBM впервые представила, который позволял выполнять прямую и обратную перемотку (я думаю, что это было затмение для Java).

Я вижу, что в OCAML есть такой отладчик: http://caml.inria.fr/pub/docs/manual-ocaml/manual030.html.

Не забывайте, что замыкания действительно могут запутать или помочь решить эту проблему.

0 голосов
/ 11 ноября 2010

Я не думаю, что это возможно. Вот почему:

  • method3 работает на результат method2
  • method2 работает на результат method1
  • поэтому method3 требует, чтобы method2 завершил
  • поэтому method2 требует, чтобы method1 завершил
  • Таким образом, порядок выполнения должен быть method3 -> method2 -> method1

Я думаю, что ваш единственный вариант - написать так:

foo.method3.method2.method1

В качестве примечания, ленивая оценка просто откладывает вычисление до тех пор, пока не потребуется результат. Например, если у меня был запрос к базе данных, который пошел бы:

@results = Result.all

Ленивая оценка не будет выполнять запрос, пока я не сделаю что-то вроде:

puts @results
...