프로그래밍에서 반복은 코드 블록을 여러 번 실행하는 과정을 말해요.
while문이나 until문 같은 개념을 사용해서 할 수도 있고요.
자신을 호출하는 함수인 재귀를 사용해서 할 수도 있어요.
하지만 대부분은 Array, Range, String처럼 항목들의 컬렉션을 반복하고 싶을 때가 많아요.
Crystal의 Array와 Range 클래스는 모두 Enumerable 모듈을 포함하고 있는데, 이 모듈은 원소를 반복 처리하는 여러 메서드를 제공해요.
한편 String 클래스는 문자를 반복 처리하는 자체 메서드들을 가지고 있어요.
Crystal에는 다른 언어들에 있는 for문도 없어요.
대신 반복에 사용할 수 있는 여러 메서드가 있어요.
컬렉션을 반복하는 가장 흔한 방법은 each 메서드를 사용하는 거예요. 이 메서드는 컬렉션의 각 원소를 블록에 넘겨줘요.
Range로도 쉽게 할 수 있어요.
1부터 3까지 반복하고 싶다면 each 메서드로 범위를 반복할 수 있어요.
(1..3).each do |n|
puts n
end
# Output:
# 1
# 2
# 3
더 간단하게, 그냥 몇 번 반복하고 싶다면 Int 클래스에 있는 times 메서드를 사용할 수 있어요.
3.times do |n|
puts n
end
# Output:
# 0
# 1
# 2
String 반복하기String은 문자의 나열이고 Enumerable 모듈에 속하지 않아요. 그래서 문자를 반복 처리하는 자체 메서드들을 가지고 있어요.
String을 반복하는 가장 흔한 방법은 each_char 메서드를 사용하는 거예요. 이 메서드는 String에 있는 각 문자를 블록에 넘겨줘요.
each_char 메서드는 블록에 String 객체가 아니라 Char 객체를 넘겨줘요.
str = "hello"
str.each_char { |char| puts char }
# Output:
# h
# e
# l
# l
# o
String을 반복하는 또 다른 방법은 each_line 메서드를 사용하는 거예요.
이 메서드는 주로 파일을 한 줄씩 읽을 때 사용해요.
str = "hello\nworld"
str.each_line do |line|
puts line
end
# Output:
# hello
# world
Enumerable 모듈을 포함하는 객체 반복하기Enumerable 모듈은 컬렉션의 원소를 반복 처리하는 여러 메서드를 제공해요.
Enumerable 모듈을 포함하는 컬렉션에는 Array, Range, Hash, Set 등이 있고, 뒤에 나오는 것들은 뒤의 개념에서 다뤄요.
Array를 반복하는 가장 흔한 방법은 each 메서드를 사용하는 거예요. 이 메서드는 Array의 각 원소를 블록에 넘겨줘요.
arr = [5, 2, 3]
arr.each do |element|
puts element
end
# Output:
# 5
# 2
# 3
map 메서드는 Array를 반복하는 또 다른 방법이에요. 각 원소에 블록을 적용한 결과를 담은 새 Array를 반환해요.
each 메서드와 비슷하지만, 변환된 원소들로 이루어진 새 Array를 반환한다는 점이 달라요.
변환은 컬렉션의 각 원소에 함수를 적용하고 싶을 때 아주 유용한데, 이는 흔히 하는 작업이에요.
arr = [1, 2, 3]
new_arr = arr.map do |element|
element * 2
end
new_arr
# => [2, 4, 6]
if 같은 제어 흐름을 함께 쓰면 어떤 원소를 변환할지 걸러낼 수도 있어요.
arr = [1, 2, 3]
new_arr = arr.map do |element|
if element.odd?
element * 2
else
element
end
end
new_arr
# => [2, 2, 6]
반복 중인 원소의 인덱스를 알아야 할 때가 있는데, 그럴 때는 each_with_index나 map_with_index 메서드를 사용할 수 있어요.
이 메서드는 각 원소와 그 인덱스를 블록에 넘겨주고, 시작 인덱스를 지정하는 선택적 인자도 받아요.
arr = [1, 2, 3]
arr.each_with_index(4) do |element, index|
puts "Element: #{element}, Index: #{index}"
end
# Output:
# Element: 1, Index: 4
# Element: 2, Index: 5
# Element: 3, Index: 6
String 객체에도 each_char_with_index 메서드를 사용할 수 있다는 점을 기억해 둬요.
sum 메서드는 컬렉션에 있는 모든 원소의 합을 반환해요. 합을 구하기 전에 원소를 변환하는 선택적 블록도 받고, 합의 초깃값을 지정하는 선택적 인자도 받아요.
arr = [1, 2, 3]
arr.sum
# => 6
arr.sum(2) { |n| n * 2 }
# => 14
reduce는 다른 언어에서 fold라고도 알려져 있는데, 결합 방식과 초깃값을 가지고 컬렉션의 모든 원소를 결합하는 메서드예요.
reduce 메서드에는 블록에 전달되는 누산기가 있는데, 누산기는 이전 반복의 결과예요.
이것은 컬렉션의 모든 원소를 결합하는 재귀적인 과정이 돼요.
arr = [1, 2, 3]
arr.reduce(0) do |acc, n|
acc + n
end
# => 6
reduce는 sum과 비슷해 보일 수 있지만, 누산기의 초깃값과 결합 방식을 지정할 수 있어서 더 유연해요.
reduce로 sum, count 같은 다른 메서드도 구현할 수 있어요.
Spellbound Steel이라는 카드 게임에서는 warrior, mage, rogue 같은 서로 다른 캐릭터 카드나 fireball, ice storm, lightning bolt 같은 서로 다른 주문 카드로 덱을 구성해요. 게임에는 두 명에서 네 명의 플레이어가 참여하고, 목표는 더 높은 파워 레벨의 카드를 내서 다른 플레이어를 물리치는 거예요.
카드의 파워 레벨을 계산하고 정렬하고 비교하는 다양한 메서드를 만들어 게임에서 우위를 점하고 싶어요.
어떤 카드가 덱에 있는지 없는지 확실하지 않아요. 그래서 찾고 있는 종류의 카드가 덱에서 처음 어디에 있는지 알려주는 메서드가 필요해요. 카드가 덱에 없다면 그 사실도 알고 싶어요.
String 카드들의 Array와 찾을 String 카드를 받는 SpellboundSteel.find_card? 메서드를 구현해요.
이 메서드는 찾는 카드와 일치하는 덱의 첫 번째 카드의 인덱스를 반환해야 해요.
카드가 덱에 없으면 메서드는 nil을 반환해야 해요.
cards = ["Warrior", "Mage", "Rogue", "Fireball", "Ice Storm", "Lightning Bolt", "Mage"]
SpellboundSteel.find_card?(cards, "Mage")
# => 1
카드를 더 읽기 쉽게 만들기 위해 카드 이름의 모든 글자를 대문자로 바꾸고 싶어요. 그래야 카드를 낼 때 카드 이름을 쉽게 알아볼 수 있어요.
String 카드들의 Array를 받아 모든 글자를 대문자로 만든 카드를 반환하는 SpellboundSteel.capitalize_cards 메서드를 구현해요.
cards = ["warrior", "mage", "rogue", "fireball", "ice storm", "lightning bolt"]
SpellboundSteel.capitalize_cards(cards)
# => ["Warrior", "Mage", "Rogue", "Fireball", "Ice Storm", "Lightning Bolt"]
게임에서는 모든 카드의 파워 레벨을 계산하는 것이 중요해요. 모든 카드의 파워는 다음 표에서 볼 수 있어요.
| 카드 유형 | 파워 레벨 |
|---|---|
| Warrior | 10 |
| Mage | 20 |
| Rogue | 30 |
| Fireball | 15 |
| Ice Storm | 25 |
| Lightning Bolt | 35 |
String 카드들의 Array를 받아 모든 카드의 총 파워 레벨을 반환하는 SpellboundSteel.calculate_power_level 메서드를 구현해요.
cards = ["Warrior", "Mage", "Rogue", "Fireball", "Ice Storm", "Lightning Bolt"]
SpellboundSteel.calculate_power_level(cards)
# => 135
게임에는 저주받은 카드라고 불리는 카드가 있어요. 이 카드는 카드를 낸 플레이어만 해독할 수 있는 숨겨진 카드예요. 카드를 해독하려면 카드 이름에서 두 번째 글자마다 하나씩 제거해야 해요. 그러면 카드의 이름이 나와요.
String 카드를 받아 해독된 카드를 반환하는 SpellboundSteel.decode_card 메서드를 구현해요.
SpellboundSteel.decode_card("Wbalrrrlimoqr")
# => "Warrior"
Exercism에 가입하고 Crystal 트랙을 개념 26개연습 문제 142개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.