将ruby代码注入块内块

时间:2012-08-08 06:25:51

标签: ruby

我反复使用这种模式:

pb= ProgressBar.new("blah_map", wtf.count)
newresult= cache("blah_map") do 
  result.map{ |r| pb.inc; { blah: r[:x] } }
end
pb.finish

pb= ProgressBar.new("blah_group", wtf.count)
newresult= cache("blah_group") do 
  result.group_by{ |r| pb.inc; "#{r[:x]}&#{r[:y]}" }
end
pb.finish

很自然地我希望能够做到

def progress_cache(name, count, &block)
  pb= ProgressBar.new(name, count)
  inject_pb_inc(block) # how??
  # do some caching with yield if cache doesn't exist, don't mind this
  pb.finish
end

并按原样使用:

newresult= progress_cache("lol", result.count) do 
  result.map do |r| 
    # progress_cache magically inserted a pb.inc here for pretty animation!  
    r[:omg] 
  end 
end

问题是,如何将pb.inc调用注入progress_cache块内的块(map,group_by等)?

编辑:改述问题

1 个答案:

答案 0 :(得分:2)

有几种方法可以实现这一目标,在表达方面有各种权衡:

  1. 将进度条作为块参数发送

    def progress_cache(name, count &block)
      pb = ProgressBar.new(name, count)
      result = block.call(pb)
      pb.finish
      result
    end
    

    并像

    一样使用它
    newresult = progress_cache("lol", result.count) do |pb|
      result.map{|r| pb.inc; r[:omg]}
    end
    
  2. 创建一个新的地图功能,自动增加进度条(你可以直接覆盖result.map,或提供result.map_with_progress,但我会把它留给你)

    def map_with_progress(container, name, &block)
      pb = ProgressBar.new(name, container.count)
      result = container.map{|obj| block.call(obj)}
      pb.finish
      result
    end
    

    然后像

    一样使用它
    newresult = map_with_progress(result, "lol") { |r| r[:omg] }
    

    当然,既然你要同时做地图和group_by,你必须在这里有两个辅助方法,这可能会开始变得混乱。

  3. 使用高阶函数

    def function_with_progress(obj, func_name, name, count, &block)
      pb = ProgressBar.new(name, count)
      result = obj.__send__(func_name) do |param|
        pb.inc
        block.call(param)
      end
      pb.finish
      result
    end
    

    然后像

    一样使用它
    newresult = function_with_progress(result, "map", "lol", result.count) do |r|
      r[:omg]
    end
    

    但我不推荐这种方法,因为它太抽象了。它可以在javascript或clojure等函数式语言中运行,但我认为它不适合ruby。