【问题标题】:Injecting ruby code into block inside block将红宝石代码注入块内的块中
【发布时间】:2012-08-05 05:24:43
【问题描述】:

我反复使用这种模式:

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 等)中?

编辑:改写问题

【问题讨论】:

标签: ruby


【解决方案1】:

有几种方法可以实现这一点,在表现力方面进行各种权衡:

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

    当然,由于您同时使用 map 和 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​​。

【讨论】:

  • of course, since you're doing both a map and a group_by, you'd have to have two helper methods here, which might start getting messy. 没错,这就是为什么我想注入任何块,而不仅仅是 map 和 group_by
  • 插入 pb 作为块调用参数是一个好的开始,谢谢,我会接受这一点,直到我看到更好的答案。
  • 好的,我添加了另一个示例(可能有一些语法错误),它显示了如何通过传递方法名称来做到这一点。你也可以传递 lambda,但由于函数不是 ruby​​ 中的一阶原语,它很快就会变得混乱。
  • 对于这种用法 block.call 不是惯用的,请使用 yield
  • @tokland 我同意,根据stackoverflow.com/questions/1410160/ruby-proccall-vs-yield,产量更快。尽管如此,有一个隐式块可能会令人困惑,因此对于说明性示例,我有时更愿意明确说明正在调用哪个 proc。
猜你喜欢
  • 2012-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-06
  • 1970-01-01
  • 2013-02-03
  • 2017-06-06
相关资源
最近更新 更多