【问题标题】:ruby working on array elements in groups of fourruby 处理四个一组的数组元素
【发布时间】:2010-12-31 14:10:05
【问题描述】:

当每个元素都需要处理时,我有一个 ruby​​ 脚本数组:

threads = []
elemets.each do  |element|
    threads.push(Thread.new{process(element)}}
end
threads.each { |aThread|  aThread.join }

然而,由于资源限制,如果一次不再处理四个元素,脚本会以最佳方式运行。

不,我知道我可以转储每个循环并使用变量来计算 4 个元素,然后等待 但是有没有更酷的 ruby​​ 方法呢?

【问题讨论】:

    标签: ruby


    【解决方案1】:

    您可以为数组枚举 4 个一组:

    >> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].each_slice(4) {|a| p a}
    [1, 2, 3, 4]
    [5, 6, 7, 8]
    [9, 10, 11, 12]
    

    所以你可以尝试类似的东西

    elements.each_slice(4) do | batch |
        batch.each do | element |
            threads.push(Thread.new{process(element)}}
    
        end
        (do stuff to check to see if the threads are done, otherwise wait )
    end
    

    不过,这可能不是您所需要的 - 我从凌晨 3 点就起床了,我只睡了几个小时。 :/

    【讨论】:

    • @Rilindo:这太棒了!修改了两行,我很高兴。谢谢。
    • (我的)解决方案在任务需要可变时间来处理时应该更有效。此解决方案假定每个线程将花费相同的时间来处理 4 个元素的列表。
    • 我想我又重新爱上了 Ruby :)
    • 如果你使用 Rails,还有更易读的 "in_groups_of"... elements.in_groups_of(4) do |group|废话结束
    【解决方案2】:

    如果我没看错的话,你希望一次处理的线程不超过 4 个。

    在我看来,您应该只启动 4 个线程,并让它们全部从共享队列(标准线程库的一部分)中读取以处理元素。

    您可以在队列为空时结束线程。

    将数组分割成 4 个相等的数组,并让每个线程处理 1/4 的元素,假设每个元素同时处理。如果某些线程比其他线程花费更长的时间,那么您的某些线程将提前完成。

    使用队列,在共享队列为空之前不会停止线程,所以我认为这是一个更有效的解决方案。

    这是一个基于您的代码的工作程序来演示:

    require 'thread'
    
    elements = [1,2,3,4,5,6,7,8,9,10]
    
    def process(element)
        puts "working on #{element}"
        sleep rand * 10
    end
    
    queue = Queue.new
    elements.each{|e| queue << e }
    
    threads = []
    4.times do
        threads << Thread.new do
          while (e = queue.pop(true) rescue nil)
            process(e)
          end
        end
    end
    
    threads.each {|t| t.join }
    

    【讨论】:

    • 这个解决方案对我来说非常接近完美,除了它在完成时会引发错误:ArgumentError: tried to create Proc object without a block 似乎它不喜欢while (e = queue.pop(true) rescue nil)
    • 我没有收到那个错误,尝试了 2 个版本的 ruby​​ - 你使用的是什么版本?
    • 版本2.3.1。我在 Rails 的 rake 任务中运行它,所以很可能在其他地方存在冲突。
    【解决方案3】:

    rails(不是 Ruby)中,可以使用更易读的形式 in_groups_of

    arr= [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
    arr.in_groups_of(4, false) {|a| p a}
    

    结果:

    [1, 2, 3, 4]
    [5, 6, 7, 8]
    [9, 10, 11]
    

    最后一行只有 3 个元素,因为我们在 in_group_of 中指定 false 作为第二个参数。如果您想要 nil 或任何其他值,您可以将 false 替换为该值。

    【讨论】:

    • in_groups_of 是一种 Rails 方法,不适用于普通 ruby​​
    【解决方案4】:

    不确定以下变体是否算作仅使用“计算 4 个元素的变量”,或者认为很酷,但它为您提供了一个大小不超过 4 个元素的切片数组:

    x = (1..10).to_a
    0.step(x.size - 1, 4) do |i|
        # Choose one
        p x.slice(i, 4)
        p x[i, 4]
    end
    

    【讨论】:

      【解决方案5】:

      是的,但是你需要做一些方法覆盖。通常的方法是像这样覆盖 Array 的“/”:

      class Array
        def / len
          a = []
          each_with_index do |x,i|
            a << [] if i % len == 0
            a.last << x
          end
          a
        end
      end 
      

      有了这个定义,您现在可以轻松做到:

      foo = [1,2,3,4,5,6]
      foo / 2
      # Result is [[1,2], [3,4], [5,6]]
      

      【讨论】:

      • 我认为覆盖这些基本类的方法是相当危险的——即使(就像在这种情况下)它们之前没有定义。为什么是/ 而不是%?如果另一位开发人员(或者我实现了这个)在一两年后出现并想要理解代码,问“Array 除以一个数字到底是什么意思”?
      猜你喜欢
      • 1970-01-01
      • 2022-10-21
      • 1970-01-01
      • 2014-08-06
      • 1970-01-01
      • 1970-01-01
      • 2021-11-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多