루비에는 블록과 함께 호출했을 때 블록에 값을 하나씩 넘겨주면서 블록을 반복 실행해 주는 이터레이터(iterator)가 많이 있다.
배열이나 해시, 범위 등의 객체에서 사용할 수 있는 each 메서드와 정수 객체에서 사용할 수 있는 times, upto, downto 메서드 등 기본적인 이터레이터부터 Enumerable 모듈을 인클루드했을 때 사용할 수 있게 되는
map, select, reject, find, reduce, sort 메서드 등 다양한 이터레이터가 존재한다.
아래 그림을 보면 몇몇 이터레이터에 대한 간단한 예제를 볼 수 있다.
이러한 이터레이터는 내가 블록을 제공하면 정해진 목적(변환, 검색, 필터링, 정렬 등)에 따라 요소들을 순회하면서 블록을 반복 실행하고 결괏값을 만들어 낸다.
그런데, 내가 직접 요소들을 순회하면서 좀 더 세밀하게 제어하고 싶다면 어떻게 해야 할까?
이때는 Enumerator를 사용하면 되는데, 배열이나 해시 등의 객체에 대해 to_enum 메서드를 호출하면 Enumerator 객체를 생성해 돌려준다.
그리고 몇몇 이터레이터는 호출 시 블록을 전달하지 않으면 Enumerator 객체를 반환하기도 한다.
아래 그림을 보면 sort와 reduce 메서드를 제외하고는 모두 Enumerator 객체를 잘 생성해서 반환해 준다.
sort 메서드는 블록 없이 호출하면 기본 정렬 방법에 따라 정렬을 수행한 결과를 돌려주고 reduce 메서드는 블록이 없으면 예외가 발생하는 것을 볼 수 있다.
아래 그림을 보면 Enumerator 클래스도 Enumerable 모듈을 인클루드한 것을 볼 수 있다.
Enumerator 역시 기본적으로 요소들을 순회할 수 있고(each 메서드를 정의해 놓았다.), 앞에서 살펴본 것처럼 배열이나 해시로부터 Enumerator 객체를 생성할 수 있기 때문에
Enumerator 객체를 통해서도 동일하게 Enumerable 모듈이 제공하는 이터레이터를 사용할 수 있도록 한 것은 라이브러리의 사용성 측면에서 좋은 것 같다.
이제 요소 순회를 좀 더 세밀하게 제어할 수 있게 해주는 Enumerator 클레스의 메서드 몇 개를 살펴보자.
아래 그림을 보면 next와 rewind 메서드를 사용한 예를 볼 수 있다.
next 메서드는 호출할 때마다 요소를 하나씩 반환해 주는데, 더 이상 반환할 요소가 없으면 StopIteration 예외를 발생시킨다.
그리고 rewind 메서드는 다시 처음부터 요소를 가져올 수 있도록 Enumerator 객체의 내부 상태 값(순회 위치)을 초기화한다.
peek 메서드는 next 메서드와 달리 내부 상태 값(순회 위치)을 변경시키지 않고 다음 요소가 뭔지를 알려 준다.
그래서 peek 메서드를 여러 번 반복해서 호출해도 바로 다음 요소의 값이 똑같이 나온다.
next 메서드를 호출하면 순회 위치가 다음으로 바뀌기 때문에, 비로소 그때 peek 메서드를 호출해 다음 요소의 값을 확인할 수 있게 된다.
그리고 peek 메서드 역시 마지막 요소까지 모두 순회한 경우 peek 메서드를 다시 호출하면 StopIteration 예외를 발생시킨다.
아래는 '단어 검색기 만들기' 글에서 만들어 본 프로그램을 사용해서 'peek'의 뜻을 확인해 본 건데, 메서드의 기능과 잘 어울리는 이름을 고른 것 같다.
See you again~~