Ruby는 끊임없이 발전하고 있습니다!
2019년 12월 25일에 출시된 Ruby 2.7은 다양한 새로운 기능과 메서드, 그리고 성능 개선을 함께 선보입니다.
Matz(마츠모토 유키히로)에 따르면...
2.7은 2.x 시리즈의 마지막 버전입니다. 내년에는 드디어 Ruby 3.0이 출시되기 때문입니다!
하지만 그 전에...
버전 업그레이드를 준비할 수 있도록 Ruby 2.7의 주요 변경 사항과 신규 기능을 하나씩 살펴보겠습니다.
Enumerable#tally
tally는 배열의 모든 요소를 세어서 각 요소의 개수를 해시 형태로 반환하는 새로운 Ruby 메서드입니다.
물론 직접 구현할 수도 있지만, 이 tally 메서드를 사용하면 불필요한 작업을 줄일 수 있습니다.
예제:
%w(a a a b b c).tally
실행 결과:
{"a"=>3, "b"=>2, "c"=>1}
아주 유용한 기능입니다!
블록 넘버드 파라미터(Numbered Parameters) [실험적]
Ruby 개발자들이 오랫동안 바라왔던 흥미로운 기능이 추가되었습니다. 바로 블록 파라미터에 기본 이름을 부여하는 것입니다.
기존에는 파라미터가 하나 있는 블록을 이렇게 작성했습니다:
[1,2,3].each { |n| puts n }
여기서 |n|은 파라미터이며, 사용하려면 반드시 직접 정의해야 했습니다.
그런데 기본 이름이 있다면 어떨까요?
Ruby 2.7에서 제공되는 기능 중 하나지만, 아직 실험적 기능(experimental feature)이라는 점에 유의하세요. 이 기능을 활용하면 타이핑 양을 크게 줄일 수 있습니다.
실제 모습은 다음과 같습니다:
[1,2,3].each { puts _1 }
_1은 첫 번째 파라미터를 가리킵니다. 두 번째 파라미터가 필요하면 _2, 세 번째라면 _3처럼 순차적으로 사용할 수 있습니다.
꽤 멋지지 않나요?
여기에 재미있는 배경 지식 하나 덧붙이자면, 이 기능은 처음에 @1 형태로 제안되었으나 인스턴스 변수와 너무 비슷하게 보인다는 의견이 나오면서 논의 끝에 _1로 확정되었습니다.
Array#intersection
이번에 추가된 것은 새로운 메서드이지만, 완전히 새로운 기능은 아닙니다.
사실...
이것은 일종의 별칭(alias)입니다!
Ruby 2.6에서는 Array#|와 Array#-에 대응하는 더 직관적인 이름으로 union과 difference 메서드가 도입되었습니다.
하지만 빠져 있던 연산이 바로 intersection이었고, 원래는 외우기 어려운 축약형 문법을 사용해야 했습니다.
바로 Array#& 메서드죠.
자, 그렇다면 이 메서드들은 무엇을 하는 것이며 어떻게 동작할까요?
예제:
[1, 2, 3].intersection([2, 3, 4]) # [2, 3] [1, 2, 3] & [2, 3, 4] # [2, 3]
메서드 이름에서 알 수 있듯이, intersection은 두 배열 간의 교집합을 찾습니다. 즉, 공통으로 포함된 요소들을 반환합니다.
| 축약형 | 풀네임 |
|---|---|
| & | intersection |
| | | union |
| - | difference |
솔직히 말하면, 저도 이 세 가지 메서드가 각각 무슨 역할을 하는지 자주 헷갈렸습니다!
하지만 새로운 메서드 이름들은 이름 자체가 의미를 담고 있기 때문에 훨씬 기억하기 쉽습니다.
덧붙여 말하자면, 좋은 코드를 작성하는 방법에 대한 훌륭한 교훈이기도 합니다.
Enumerable#filter_map
filter_map 메서드는 select와 map을 하나로 결합하려는 시도입니다.
왜 필요할까요?
목록을 먼저 필터링한 후 남은 요소들에 매핑을 적용하는 작업은 매우 흔하기 때문입니다.
기존에는 두 가지 방식으로 처리했습니다:
(1..8).select(&:even?).map { |n| n ** 2 }
# 또는
(1..8).map { |n| n ** 2 if n.even? }.compact
두 방식 모두 사용해 봤지만, 의도가 더 명확하게 드러나는 첫 번째 방식을 주로 선호했습니다.
이제 Ruby 2.7의 filter_map 메서드는 이 모든 과정을 한 번의 패스로 처리합니다.
다음과 같이 말이죠:
(1..8).filter_map { |n| n ** 2 if n.even? }
# [4, 16, 36, 64]
블록 안에 if 문을 넣는 스타일을 크게 선호하지는 않지만, 때로는 작업을 완료하기 위해 필요할 때가 있습니다.
그리고 반드시 알아두어야 할 사항이 있습니다.
filter_map 연산은 map + compact와 동일하게 동작하지 않습니다. filter_map은 false 객체까지 제거하지만, compact는 nil만 제거하기 때문입니다.
꼭 기억해 두세요!
Enumerator#produce
흥미로워 보이는 또 다른 새 메서드가 있습니다. 다만 이를 최대한 활용하려면 약간의 창의력이 필요합니다.
바로 Enumerator#produce 메서드입니다.
이 메서드는 무엇을 할까요?
기능 제안서에 따르면:
"이 메서드는 무한 시퀀스를 생성하며, 각 다음 요소는 이전 요소에 블록을 적용하여 계산됩니다."
그렇습니다.
끝없이 이어지는 시퀀스를 만드는 또 하나의 방법입니다!
예제:
Enumerator.produce(1, &:next).take(5) # [1, 2, 3, 4, 5]
이 예제에서 take(10)을 하든 take(10_000)을 하든 상관없이, 무한히 많은 값을 얻을 수 있습니다.
참고로 1은 초기값이며, &:next는 시퀀스의 다음 요소를 produce(생성)하기 위해 해당 값에 호출되는 메서드입니다.
IRB의 새단장
최근 IRB(Interactive Ruby)는 꾸준히 개선되어 왔으며, Ruby 2.7도 그 흐름을 이어갑니다.
새롭게 추가된 기능은 다음과 같습니다:
- 멀티라인 편집 지원
- 구문 강조(Syntax Highlighting)
- 내장 RDoc 통합
- 명령 히스토리 기본 활성화
- 자동 완성 기본 활성화
사실 이 변경은 매우 큰 변화라서, Ruby 2.7에서 irb를 실행하면 경고 메시지가 표시됩니다.
기존 버전의 irb를 계속 사용하고 싶다면 irb --legacy 명령으로 여전히 가능합니다. 개인적으로 새 IRB에서 아쉬운 점은 줄 편집 기능입니다. 기존 IRB(또는 Pry)에서는 ALT 키를 누른 상태에서 왼쪽 화살표를 눌러 커서를 빠르게 이동할 수 있었지만, 새 IRB에서는 이 기능이 동작하지 않습니다.
직접 사용해 보고 본인에게 맞는지 확인해 보시기 바랍니다.
Ruby 패턴 매칭(Pattern Matching) [실험적]
많은 개발자들이 오랫동안 요청해 온 기능인 패턴 매칭이 2.7에 도입되었습니다.
실험적 기능으로 표시되어 있습니다.
그렇다면 패턴 매칭이란 무엇이며, 어떤 이점을 제공할까요?
제가 이해하기로는, 패턴 매칭은 데이터 구조(Array/Hash)를 위한 정규 표현식 같은 것입니다.
예제:
[1,2,3] in [a,b,c] # true [1,2,3] in [a] # false
매치가 성공하면 변수 이름(예: a)에 접근할 수 있는 지역 변수로 바인딩됩니다.
해시 예제:
{ a: 1, b: 2, c: [] } in { a: a, b: b, c: [] }
# true
p a
# 1
여기서는 in 키워드를 사용합니다.
in은 새로운 키워드는 아니지만, Ruby 2.7 이전에는 for 루프(Ruby에서 권장되지 않는 문법)의 일부로만 사용되었기 때문에 잘 쓰이지 않았습니다. 이제 이 키워드를 유용한 목적으로 재활용하게 된 셈입니다.
더 좋은 점은...
변수 이름이 무엇이든 상관없다는 것입니다. a,b,c가 동작하듯이 f,g,h도 마찬가지로 동작합니다.
중요한 것은 패턴 자체입니다!
기타 변경 사항
언급할 만한 추가 변경 사항들입니다.
- 새로운 Compaction GC가 메모리 단편화를 개선
- 메서드 인자
**nil을 통해 해당 메서드가 키워드 인자를 받지 않음을 명시 가능 - 메서드 인자
...로 인자 전달(argument forwarding) 메커니즘 추가 (괄호 필수, 예:make_salad(...)) - Fiber 개선. 새로운 Fiber 객체 풀(pool)을 통해 Fiber 생성이 더욱 효율적
- Thread 개선. 더 최적화된 스레드 생성 방식으로 멀티스레딩 속도 향상
이 변경 사항들에 대해 어떻게 생각하시나요?
2.7의 NEWS 파일에는 Fiber와 Thread 생성 모두에서 10배의 성능 향상이 언급되어 있습니다. 아직 직접 테스트해 보지는 못했지만, 테스트해 보신다면 결과를 공유해 주세요.
마치며
이제 Ruby 프로그래밍 언어의 최신 변경 사항을 모두 파악하셨습니다! IRB의 변화, 새로운 메서드들, 그리고 패턴 매칭 같은 실험적 기능까지 살펴보았습니다.
유용한 정보였다면 주변에 공유해 주세요!
읽어 주셔서 감사합니다 🙂