4 개의 그룹으로 배열 요소를 작업하는 루비


80

각 요소를 처리해야 할 때 루비 스크립트 배열이 있습니다.

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

그러나 리소스 제한으로 인해 한 번에 네 가지 요소가 더 이상 처리되지 않으면 스크립트가 최적의 방식으로 작동합니다.

아니 나는 각 루프를 덤프하고 변수를 사용하여 4 요소를 계산 한 다음 기다릴 수 있다는 것을 알고 있지만 더 멋진 루비 방법이 있습니까?

답변:


168

배열에 대해 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시 이후에 일어나서 잠을 몇 시간 밖에 안 잤습니다. : /


2
@Rilindo : 이것은 훌륭합니다! 두 줄을 수정했고 나는 잘했다. 감사.
Eli

2
아래의 (내) 솔루션은 작업을 처리하는 데 가변적 인 시간이 소요될 때 더 효율적이어야합니다. 이 솔루션은 각 스레드가 4 개의 요소 목록을 처리하는 데 동일한 시간이 걸린다고 가정합니다.
Andrew Kuklewicz 2012

2
난 그냥 :) 다시 한번 루비와 사랑에 빠졌다 생각
superluminary

Rails를 사용한다면 더 읽기 쉬운 "in_groups_of"... elements.in_groups_of (4) do | group | blah end
Jason

21

내가 올바르게 읽으면 한 번에 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)
SaltedBlowfish

오류가 발생하지 않고 두 가지 버전의 루비를 사용해 보았습니다. 어떤 버전을 사용하고 있습니까?
Andrew Kuklewicz 2018

버전 2.3.1. Rails의 레이크 작업 내에서 실행하고 있었으므로 다른 곳에서 충돌이있을 가능성이 높습니다.
SaltedBlowfish 2018

2

다음 변형이 "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

2

레일에서는 더 읽기 쉬운 형식을 사용할 수 있습니다. 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]

에서 false를 지정 했으므로 마지막 행에는 3 개의 요소 만 in_group_of있습니다. nil 또는 다른 값을 원하면 false를 해당 값으로 바꿀 수 있습니다.


in_groups_of일반 루비와 레일 방법입니다하지 않습니다 일
Subash

1

예,하지만 몇 가지 메서드 재정의를 수행해야합니다. 일반적인 접근 방식은 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]]

2
이러한 기본 클래스에서 메서드를 재정의하는 것은 (이 경우와 같이) 이전에 정의되지 않았더라도 다소 위험하다고 생각합니다. 왜 /그렇지 %않습니까? 다른 개발자 (또는 이것을 구현 한 내가)가 1 ~ 2 년 안에 와서 코드를 이해하기를 원하고 "도대체 Array숫자 로 나눈 것이 실제로 무엇을 의미 하는지"묻는다면 어떻게 될까요?
haslo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.