想象一下,我有一些带有run方法的资源对象,它在为该资源持有的锁下执行block参数。例如,像这样:
r = Resource("/tmp/foo")
r.run { ... }
如何编写一个ruby方法,该方法获取一系列资源并在为所有资源保存的锁下执行其block参数,例如:
def using_resources(*res, &block)
r[0].run do; r[1].run do; r[2].run do ...
yield;
end; end; end; ...
end
有可能吗?
答案 0 :(得分:8)
在我看来,最好使用递归
以下是代码:
def using_resources(*res, &block)
first_resource = res.shift
if res.length > 0
first_resource.run do
using_resources(*res, &block)
end
else
first_resource.run do
block.call
end
end
end
并像这样使用它:
using_resources Resource.new('/tmp'), Resource.new('http://stackoverflow.com') do
do_some_processing
end
你说,“这需要一系列资源。”如果您已经Array
并且需要使用它,则可以在呼叫之外展开Array
:
using_resources *my_array do
do_some_processing
end
或者在方法定义中,您可以使用Array
或Resource
列表来调用它:
def using_resources(*res, &block)
res = [*res]
# rest as above
end
答案 1 :(得分:7)
您也可以使用#inject
:
def using_resources(*resources, &block)
(resources.inject(block){ |inner,resource| proc { resource.run(&inner) } })[]
end
当您单步执行数组时,将每个资源的前一个Proc的调用包装在新的Proc中,然后将其传递给下一个资源。这会以相反的顺序获取锁定(给定的最后一个资源是第一个解锁的资源),但可以使用resources.reverse.inject ...
答案 2 :(得分:0)
对于run()
产生您想在最里面的块中使用的值的情况,我想在@rampion的解决方案中发布扩展名,如下所示:
def using_resources(*res, &block)
r[0].run do |v0|; r[1].run do |v1|; r[2].run do |v2| ...
yield [v0, v1, v2];
end; end; end; ...
end
例如,如果run
与File.open
类似,则会出现这种情况,它会产生可在给定块中使用的资源(例如,文件对象),但在使用后将其拆除您的封锁完成了。
功能如下:
def nested_do( args, func, &block )
args.reverse.inject(block) do |inner, a|
Proc.new do |acc|
func.call(a) do |v|
acc.append(v)
inner.call(acc)
end
end
end
.call([])
end
要嵌套对run
的调用并收集其产生的值,您可以这样做:
res = [ Resource('a'), Resource('b'), Resource('c') ]
func = Proc.new {|r,&b| r.run(&b)}
nested_do( res, func ) do |vals|
puts("Computing with yielded vals: #{vals}")
end
这里的功能类似于File.open
,但它会打开“秘密”资源,
呼叫者永远无法在呼叫者的范围内使用的原因,因为
在yield()返回后将其拆除:
def open_secret(k)
v = "/tmp/secret/#{k}"
puts("Setup for key #{k}")
yield v
puts("Teardown for key #{k}")
end
以下是如何动态嵌套对open_secret
的调用:
nested_do([:k1,:k2,:k3], Proc.new {|k,&b| open_secret(k,&b)} ) do |rs|
puts("Computing with 'secret' resources SIMULTANEOUSLY: #{rs}")
end
打印:
Setup for key k1
Setup for key k2
Setup for key k3
Computing with 'secret' resources SIMULTANEOUSLY: ["/tmp/secret/k1", "/tmp/secret/k2", "/tmp/secret/k3"]
Teardown for key k3
Teardown for key k2
Teardown for key k1
事后看来,使用inject
需要大量的脑力劳动。这是使用递归的高级实现:
def nested_do(args, func, acc=[], &block)
return block.call(acc) if args.size == 0
a = args.pop
func.call(a) do |v|
acc.append(v)
nested_do( args, func, acc, &block)
end
end