Enumerable이란 무엇인가?
Enumerable은 반복(iteration) 메서드들의 집합입니다. Ruby의 모듈(module)로서, Ruby가 훌륭한 프로그래밍 언어로 자리 잡는 데 크게 기여하는 핵심 요소라고 할 수 있습니다.
Enumerable에는 다음과 같은 유용한 메서드들이 포함되어 있습니다:
mapselectinject
Enumerable의 메서드들은 블록(block)을 전달하는 방식으로 동작합니다. 블록 안에서 각 요소에 대해 수행하고 싶은 작업을 지정하기만 하면 됩니다.
예를 들어:
[1,2,3].map { |n| n * 2 }
이 코드를 실행하면 모든 숫자가 두 배가 된 새로운 배열을 얻을 수 있습니다.
실제로 어떤 일이 일어나는지는 사용하는 메서드에 따라 달라집니다. map은 모든 값을 변환(transform)할 때 유용하고, select는 리스트를 필터링할 수 있으며, inject는 배열 안의 모든 값을 합산하는 데 활용할 수 있습니다.
Ruby에는 20개가 넘는 Enumerable 메서드가 존재합니다. 그럼 지금부터 그중 하나를 자세히 살펴보겠습니다.
each_cons 메서드
최근 제가 가장 좋아하게 된 Enumerable 메서드는 바로 each_cons입니다!
그 이유는 다음과 같습니다:
이 메서드는 매우 실용적입니다. n-gram을 추출하거나, 다른 Enumerable 메서드인 all?과 결합하여 숫자 시퀀스가 연속적인지 확인하는 등 다양하게 응용할 수 있습니다.

each_cons는 크기가 n인 부분 배열(sub-array)들을 반환합니다. 예를 들어 [1,2,3] 배열에 each_cons(2)를 호출하면 [[1,2], [2,3]]이라는 결과를 얻게 됩니다.
예시를 통해 확인해 보겠습니다:
numbers = [3,5,4,2]
numbers.sort.each_cons(2).all? { |x,y| x == y - 1 }
이 코드는 먼저 숫자를 정렬한 뒤 each_cons(2)를 호출해 Enumerator 객체를 반환받고, 이어서 all? 메서드를 호출해 모든 요소가 조건을 만족하는지 검사합니다.
또 다른 예제로, each_cons를 사용하면 특정 문자가 같은 문자로 둘러싸여 있는지(xyx 패턴처럼) 손쉽게 확인할 수 있습니다.
str = 'abcxyx'
str.chars.each_cons(3).any? { |a,b,c| a == c }
여기서 끝이 아닙니다!
true/false 대신 이 패턴이 몇 번 나타나는지 세고 싶다면, any?를 count로 바꿔주기만 하면 됩니다.
더욱 흥미로운 점은 each_cons 메서드의 내부 구현 방식입니다.
array = [] each do |element| array << element array.shift if array.size > n yield array.dup if array.size == n end
참고: 이 코드는 Rubinius의
Enumerable구현에서 발췌한 것으로, 원본 소스 코드는 공개 저장소에서 확인할 수 있습니다.
구현 로직을 살펴보면, 먼저 빈 Ruby 배열을 생성한 후 each로 각 요소를 순회합니다. 여기까지는 아주 표준적인 코드입니다. 그다음 배열에 현재 요소를 추가하고, 배열의 크기가 원하는 값보다 커지면 Array#shift를 사용해 앞부분을 잘라냅니다.
여기서 말하는 '크기'는 each_cons에 전달된 인자입니다. 그리고 배열이 요청한 크기와 같아지면, 배열의 복제본(dup)을 yield합니다.
배열 인덱스를 직접 다루는 번거로운 작업 없이도 enumerable 객체 위에서 '슬라이딩 윈도우(sliding window)' 효과를 우아하게 구현했다는 점에서, 저는 이 구현이 정말 천재적이라고 생각합니다.
더 알아두면 좋은 Enumerable 메서드들
| 메서드 | 설명 |
|---|---|
| count | 이름 그대로, 블록 내에서 true로 평가되는 요소의 개수를 셉니다 |
| group_by | 블록의 반환값을 기준으로 요소들을 그룹화합니다. 해시(hash)를 반환합니다 |
| partition | 요소를 두 그룹으로 분할합니다. 2차원 배열을 반환합니다 |
| any? | 블록이 하나의 요소라도 true를 반환하면 true를 반환합니다 |
| all? | 블록이 모든 요소에 대해 true를 반환하면 true를 반환합니다 |
| none? | all?의 반대 개념입니다 |
| cycle(n) | 모든 요소를 n번 반복합니다. 예: [1,2].cycle(2) → [1,2,1,2] |
| find | select와 비슷하지만, 조건을 만족하는 첫 번째 요소만 반환합니다 |
| inject | 이전 블록의 결과값을 누적해 다음 블록에 전달합니다. 합계 계산에 유용합니다 |
| zip | 두 개의 enumerable 객체를 묶어 병렬로 처리할 수 있게 해줍니다. 요소 비교 및 해시 생성에 유용합니다 |
| map | enumerable 객체의 모든 요소를 변환하고, 새로운 결과를 배열로 반환합니다 |
마무리
지금까지 살펴본 것처럼, Enumerable은 반드시 익혀둘 가치가 있는 모듈입니다. 공식 문서를 찾아보면서 Enumerable이 여러분의 코드에 어떤 도움을 줄 수 있는지 직접 확인해 보세요!