包装Ruby函数

时间:2016-11-18 05:28:18

标签: ruby

我希望能够完全透明地包装任何Ruby proc(包括我自己没有编写源代码的那些),并记录其执行时间。

my_proc

也就是说,我想创建一个调用my_proc保留

的过程
  1. 上下文/接收器
  2. 参数
  3. 块。
  4. 并打印出调用时的执行时间。

    例如:

    my_proc = proc { |*args, &block| p self: self, args: args, block: block }
    
    Object.new.instance_eval &my_proc
    #=> {
    #  :self=>#<Object:0x007fd4c985f3e0>,
    #  :args=>[#<Object:0x007fd4c985f3e0>],
    #  :block=>nil
    # }
    
    Object.instance_exec '5', &my_proc
    #=> {
    #  :self=>Object,
    #  :args=>["5"],
    #  :block=>nil
    # }
    
    my_proc.call(1, 2) { }
    #=> {
    #  :self=>main,
    #  :args=>[1, 2],
    #  :block=>#<Proc:0x007fd4c985e9b8>
    # }
    

    然后我想把它包起来,它应该表现得完全一样:

    def wrap(prc)
      # what does this look like?
    end
    
    wrapped_proc = wrap(my_proc)
    
    Object.new.instance_eval(&wrapped_proc)
    # took 1s
    #=> {
    #  :self=>#<Object:0x007fd4c985f3e0>,
    #  :args=>[#<Object:0x007fd4c985f3e0>],
    #  :block=>nil
    # }
    
    Object.instance_exec '5', &wrapped_proc
    # took 2s
    #=> {
    #  :self=>Object,
    #  :args=>["5"],
    #  :block=>nil
    # }
    
    wrapped_proc.call(1, 2) { }
    # took 3s
    #=> {
    #  :self=>main,
    #  :args=>[1, 2],
    #  :block=>#<Proc:0x007fd4c985e9b8>
    # }
    

    看起来透明的函数包装器应该很难,但我无法解决这个问题。

1 个答案:

答案 0 :(得分:3)

这里唯一的技巧是处理λ.call传递阻塞案例,因为在后一种情况下 Proc#call未被调用。 SIC。

我的第一个[错误]意图是:

def wrap λ
  λ.singleton_class.prepend(Module.new do
    def call(*args, &cb)
      puts "⇓⇓⇓"
      super
      puts "⇑⇑⇑"
    end
  end)
end

但正如我已经说过的那样,specific_eval不会调用Proc#call也不调用Proc#to_proc而我放弃了。

另一方面,我们可能只是在接收器的上下文中instance_exec包裹了λ,但是没有办法将块作为参数传递给instance_exec因为它本身已经接收到了λ。

情节变浓。由于我们无法将块作为参数传递给instance_exec,因此我们的包装器的使用者也不能。是的,这解决了任务:

def wrap λ
  -> (*args, &cb) do
    puts "⇓⇓⇓"
    (cb ? λ.call(*args, &cb) : instance_exec(*args, &λ)).tap do |result|
      puts result.inspect
      puts "⇑⇑⇑"
    end
  end
end

你走了。