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 ofmethod2
method2
is operating on the result ofmethod1
- therefore
method3
requires thatmethod2
has completed - therefore
method2
requires thatmethod1
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.
精彩评论