包装Ruby函数
我希望能够完全透明地包装任何Ruby proc(包括我自己没有编写源代码的那些),并记录它的执行时间。
my_proc
也就是说,我想创建一个调用my_proc
保留的proc
- 上下文/接收者
- 争论
- 块。
并在打电话时打印出执行时间。
例如:
my_proc = proc { |*args, &block| p self: self, args: args, block: block } Object.new.instance_eval &my_proc #=> { # :self=>#, # :args=>[#], # :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=># # }
然后我想包装它,它应该表现完全相同:
def wrap(prc) # what does this look like? end wrapped_proc = wrap(my_proc) Object.new.instance_eval(&wrapped_proc) # took 1s #=> { # :self=>#, # :args=>[#], # :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=># # }
它似乎不是一个透明的函数包装器应该很难,但我无法弄清楚这一点。
这里唯一的技巧是处理λ.call
和&λ
传递块的情况,因为在后一种情况下不调用Proc#call
调用 。 骰。
我的第一个[错误]意图是简单地:
def wrap λ λ.singleton_class.prepend(Module.new do def call(*args, &cb) puts "⇓⇓⇓" super puts "⇑⇑⇑" end end) end
但正如我已经说过的那样, specific_eval
不会调用Proc#call
也不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
干得好。