콘텐츠 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11

공식 Ruby FAQ

If you wish to report errors or suggest improvements for this FAQ, please go to our GitHub repository and open an issue or pull request.

이터레이터

이터레이터란 무엇인가요?

이터레이터는 블록 또는 Proc 객체를 받는 메서드입니다. 소스 파일에서 블록은 메서드 호출 직후에 위치합니다. 이터레이터는 사용자 정의 제어 구조, 특히 루프를 생성하는 데 사용됩니다.

이터레이터가 어떻게 작동하는지 예시를 통해 살펴봅시다. 이터레이터는 다음과 같이 컬렉션의 각 요소에 대해 동일한 작업을 반복하는 데 자주 사용됩니다.

data = [1, 2, 3]
data.each do |i|
  puts i
end

결과:

1
2
3

배열 data의 each 메서드에는 do ... end 블록이 전달되어 반복적으로 실행됩니다. 호출할 때마다 블록은 배열의 요소를 차례로 전달받습니다.

블록을 정의할 때 do ... end 대신 { ... }를 쓸 수 있습니다.

data = [1, 2, 3]
data.each { |i|
  puts i
}

결과:

1
2
3

이 코드는 앞의 예제와 동일한 의미를 갖습니다. 그러나 경우에 따라 우선순위 문제로 인해 do ... end{ ... }가 다르게 동작할 수 있습니다.

foobar a, b do ... end  # foobar가 이터레이터.
foobar a, b { ... }     # b가 이터레이터.

이는 { ... }do ... end 블록보다 앞의 표현식에 더 강하게 바인딩되기 때문입니다. 첫 번째 예는 foobar(a, b) do ... end와 같고, 두 번째 예는 foobar(a, b { ... })와 같습니다.

블록을 이터레이터에 전달하려면 어떻게 해야 하나요?

이터레이터 호출 뒤에 블록을 배치하기만 하면 됩니다. Proc을 참조하는 변수나 상수 이름 앞에 &를 붙여 Proc 객체를 전달할 수도 있습니다.

블록은 이터레이터에서 어떻게 사용되나요?

This section or parts of it might be out-dated or in need of confirmation.

이터레이터 메서드에서 블록을 실행하는 방법에는 세 가지가 있습니다. (1) yield 제어 구조, (2) call을 사용하여 (블록으로 만든) Proc 인자를 호출하는 것, (3) Proc.new를 사용한 후 호출하는 것입니다.

yield 문은 블록을 호출하며, 하나 이상의 인수를 전달할 수 있습니다.

def my_iterator
  yield 1, 2
end

my_iterator {|a, b| puts a, b }

결과:

1
2

메서드 정의에 블록 인수가 있는 경우(마지막 형식 매개 변수 앞에 앰퍼샌드(&)가 붙은 경우) Proc 객체로 변환된 첨부된 블록을 받습니다. 이것은 proc.call(args)을 사용하여 호출할 수 있습니다.

def my_iterator(&b)
  b.call(1, 2)
end

my_iterator {|a, b| puts a, b }

결과:

1
2

이터레이터 정의에서 Proc.new(또는 동치인 proc, lambda 호출)를 사용하면 메서드에 주어진 블록을 인수로 받아 프로시저 객체를 생성합니다. (proclambda는 사실상 동의어입니다.)

[업데이트 필요: lambda는 약간 다른 방식으로 작동하며, 블록 없이 Proc 객체를 생성하려고 시도했습니다(tried to create Proc object without a block)라는 경고가 표시됩니다.]

def my_iterator
  Proc.new.call(3, 4)
  proc.call(5, 6)
  lambda.call(7, 8)
end

my_iterator {|a, b| puts a, b }

결과:

3
4
5
6
7
8

놀랍게도 Proc.new와 친구들은 메서드에 연결된 블록을 전혀 소비하지 않으며, Proc.new를 호출할 때마다 동일한 블록에서 새 프로시저 객체를 생성합니다.

메서드에 연관된 블록이 있는지 여부는 block_given?을 호출하여 확인할 수 있습니다.

블록이 없는 Proc.new는 어떤 기능을 하나요?

블록이 없는 Proc.new는 프로시저 객체를 생성할 수 없으며 오류가 발생합니다. 그러나 메서드 정의에서 블록이 없는 Proc.new는 메서드가 호출될 때 블록이 존재한다는 것을 의미하므로 오류가 발생하지 않습니다.

이터레이터를 병렬로 실행하려면 어떻게 해야 하나요?

여기 [ruby-talk:5252]에서 Matz가 스레드를 사용하는 해결책을 가져왔습니다.

require "thread"

def combine(*iterators)
  queues = []
  threads = []

  iterators.each do |it|
    queue = SizedQueue.new(1)
    th = Thread.new(it, queue) do |i, q|
           send(i) {|x| q << x }
         end
    queues  << queue
    threads << th
  end

  loop do
    ary = []
    queues.each {|q| ary << q.pop }
    yield ary

    iterators.size.times do |i|
      return if !threads[i].status && queues[i].empty?
    end
  end
end

def it1
  yield 1; yield 2; yield 3
end

def it2
  yield 4; yield 5; yield 6
end

combine(:it1, :it2) do |x|
  # x is [1, 4], then [2, 5], then [3, 6]
end