如何向后运行Ruby函数?

时间:2010-11-11 18:22:29

标签: ruby metaprogramming lazy-evaluation

我希望有一个像foo.method1.method2.method3那样向后运行函数的类,我希望函数运行method3 method2然后是method1。但现在它已经进入了1 2 3。我认为这被称为懒惰评估,但我不确定。

我对Ruby几乎一无所知所以请原谅这个问题,如果它很简单,我应该已经知道了。

5 个答案:

答案 0 :(得分:5)

当然可以做到。这听起来像是在正确的路径上思考您只需要使用运行排队方法的方法结束每个方法调用列表的惰性求值。

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

答案 1 :(得分:4)

也许您可以链接方法调用,构建评估堆栈并稍后执行。这要求您调用额外的方法来评估堆栈。您可以使用私有方法进行实际实现。

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.

更新

看起来我设法错过Pete's answer并发布了几乎完全相同的答案。唯一的区别是能够将参数传递给内部堆栈。

答案 2 :(得分:3)

你真正想要的是一个代理类,它捕获消息,反转它们,然后将它们转发到实际的类:

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

答案 3 :(得分:0)

我不认为这是可能的。原因如下:

  • method3正在对method2
  • 的结果进行操作
  • method2正在对method1
  • 的结果进行操作
  • 因此method3要求method2已完成
  • 因此method2要求method1已完成
  • 因此,执行顺序必须是method3 - &gt; method2 - &gt; method1

我认为你唯一的选择就是把它写成:

foo.method3.method2.method1

作为旁注,延迟评估只是推迟计算直到需要结果。例如,如果我有一个数据库查询:

@results = Result.all

在我执行以下操作之前,延迟评估不会执行查询:

puts @results

答案 4 :(得分:0)

这是最有趣和最有用的;我认为确实可以使用代理模式。 我在阅读你关于IBM首次提供允许执行前向和后向调试器的初始帖子时认为(我认为这是针对java的eclipse)。

我看到OCAML有这样一个调试器:http://caml.inria.fr/pub/docs/manual-ocaml/manual030.html

不要忘记关闭确实会弄乱或帮助解决这个问题。