_If you wish to report errors or suggest improvements for this FAQ,
please go to our
[GitHub repository](https://github.com/ruby/www.ruby-lang.org/)
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` 객체를 전달할 수도 있습니다.

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

<div class="not-prose bg-ruby-100 dark:bg-ruby-800 rounded-lg p-8 my-6">
  <div class="flex items-start gap-3">
    <svg class="w-6 h-6 text-ruby-700 dark:text-ruby-200 flex-shrink-0 mt-0.5" fill="currentColor" viewBox="0 0 20 20">
      <path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd"/>
    </svg>
    <p class="text-sm text-ruby-700 dark:text-ruby-200 italic">
      This section or parts of it might be out-dated or in need of confirmation.
    </p>
  </div>
</div>


이터레이터 메서드에서 블록을 실행하는 방법에는 세 가지가 있습니다.
(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` 호출)를 사용하면
메서드에 주어진 블록을 인수로 받아 프로시저 객체를 생성합니다. (`proc`과
`lambda`는 사실상 동의어입니다.)

_[업데이트 필요: `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\]][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
~~~

[ruby-talk:5252]: https://blade.ruby-lang.org/ruby-talk/5252
