开发者

How can I run a Ruby functions backwards?

I want to have a class that runs functions backwards like foo.method1.method2.method3 and I want the funcitons to run metho开发者_Python百科d3 method2 then method1. But it goes 1 2 3 now. I think this is called lazy evaluation but I'm not sure.

I know next to nothing about Ruby so please excuse this question if its simple and I should know this already.


Sure you can do it. It sounds like you were on the right path thinking of the lazy evaluation you just need to end every list of method calls with a method that runs the queued methods.

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


Maybe you could chain method calls, build an evaluation stack and execute later. This requires that you call an extra method to evaluate the stack. You could use private methods for the actual implementations.

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

so that you can do something like

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.

Update

Looks like I managed to miss Pete's answer and posted almost exactly the same answer. The only difference is the ability to pass on the arguments to the internal stack.


What you really want here is a proxy class that captures the messages, reverses them, and then forwards them on to the actual class:

# 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]


I don't think that this is possibl. Here is why:

  • method3 is operating on the result of method2
  • method2 is operating on the result of method1
  • therefore method3 requires that method2 has completed
  • therefore method2 requires that method1 has completed
  • thus, the execution order must be method3 -> method2 -> method1

I think your only option is to write it as:

foo.method3.method2.method1

As a side note, lazy evaluation is simply delaying a computation until the result is required. For example, if I had a database query that went:

@results = Result.all

Lazy evaluation would not perform the query until I did something like:

puts @results


This is most interesting and useful; I think that the proxy pattern can indeed be used. I thought as I read your initial post about a debugger that IBM first provided that allowed executing forward and backward (I think that it was on eclipse for java).

I see that OCAML has such a debugger: http://caml.inria.fr/pub/docs/manual-ocaml/manual030.html.

Don't forget that closures can indeed mess or help this issue.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜