예전에 아래 코드처럼 로또 번호 6개를 랜덤하게 생성하는 기능을 만들었었다.
def gen_lotto_num
lotto = []
while true
num = rand(45) + 1
if !lotto.include?(num)
lotto.push(num)
end
break if lotto.size == 6
end
return lotto
end
코드를 다시 살펴보면 1부터 45까지의 숫자에서 랜덤하게 6개를 뽑는 과정에서 중복을 없애기 위해 새로 뽑은 번호가 지금까지 뽑은 번호들 중에 포함되어 있는지를 반복해서 검사하고 있다.
뭔가 좀 이상하다. TV에서 실제로 로또 당첨 번호를 뽑을 때는 저런 방식이 아니기 때문이다. TV에서 보면 상자 안에 1부터 45까지 번호가 적혀있는 공이 45개가 있고, 거기서 랜덤하게 공 6개를 꺼내 1등 당첨 번호를 결정한다.
그 과정에서 당연히 꺼낸 공을 상자에 다시 넣지 않는다.
즉, 1부터 45까지의 숫자가 담긴 배열에서 그냥 랜덤하게 6개를 꺼내면 되는데, 이때 배열의 delete_at 메서드를 사용해서 요소를 꺼내면 배열에서 해당 요소는 삭제가 되기 때문에 같은 숫자의 공을 다시 뽑지 않았는지 검사할 필요가 없다.
delete_at 메서드는 인수로 받은 인덱스의 요소를 삭제한 후 해당 요소를 반환한다.
이 아이디어를 적용한 코드가 아래 있다.
def gen_lotto_num
numbers = (1..45).to_a
lotto = []
6.times { lotto << numbers.delete_at(rand(numbers.size)) }
lotto.sort
end
새롭게 수정한 코드를 보면 Integer 클래스의 인스턴스 메서드인 times를 사용하여 반복 횟수 제어를 위한 추가적인 변수 없이도 6번 반복을 쉽게 처리하는 걸 볼 수 있다.
이미 알고 있겠지만, rand 메서드는 인수로 0보다 큰 정숫값을 주면 0부터 해당 정숫값보다 1 작은 정숫값 중에서 랜덤하게 숫자 하나를 뽑아 돌려준다.
그래서 앞의 코드에서처럼 rand 메서드의 인수로 특정 배열의 크기(size 메서드 호출 값)를 주면 결국, 해당 배열의 인덱스 중 하나를 받게 된다.
제일 처음에 배열에 45개의 요소가 있으므로 numbers.size는 45가 되고 rand 메서드는 0부터 44까지의 숫자 중 하나를 랜덤하게 돌려준다.
그리고 그 인덱스의 요소가 delete_at 메서드로 인해 삭제가 되므로 다음 블록 실행때는 numbers.size는 44가 되고 rand 메서드는 0부터 43까지의 숫자 중 하나를 랜덤하게 돌려주게 된다.
gen_lotto_num 메서드의 마지막 코드에서는 랜덤으로 뽑은 6개의 숫자를 보기 좋게 정렬해서 반환한다.
그런데 이 코드도 나쁘진 않지만 Array 클래스의 new 메서드를 사용하면 코드를 조금 더 깔끔하게 다듬을 수 있다.
Array 클래스의 new 메서드를 사용해서 배열을 생성할 때 인수를 생략하면 빈 배열([])을 돌려주고, 정수 값을 주면 해당 개수만큼의 nil이 채워진 배열을 돌려준다.
또한 인수(요소 개수)와 함께 블록을 주고 new 메서드를 호출하면 요소의 개수만큼 블록을 반복해서 실행하고, 각 블록의 실행 결괏값을 요소로 하는 배열을 돌려준다.
아래 그림을 보면 각각의 경우에 대해 실제 동작하는 것을 볼 수 있다.
Array 클래스의 new 메서드를 사용하여 gen_lotto_num 메서드의 코드를 아래처럼 수정해 보았다.
def gen_lotto_num
numbers = (1..45).to_a
Array.new(6) { numbers.delete_at(rand(numbers.size)) }.sort
end
코드의 길이가 꽤 줄어들었다. 그렇다고 짧은 코드가 무조건 좋은 코드는 아니다. 코드도 간결하지만 읽기도 쉽고 이해하기도 쉬워야 좋은 코드이다.
물론, 좋은 코드의 기준도 어떠한 용도의 프로그램이냐에 따라 달라질 수 있다.
메모리나 CPU 등의 자원을 최소한으로 사용하면서도 최대의 성능을 내야 하는 상황이라면, 비록 사람이 읽기 어려운 코드라 할지라도 해당 상황에서 최적으로 돌아가도록 작성된 코드가 좋은 코드일 수 있다.
그럼에도 불구하고, 가독성이 좋고 수정하기 쉬운 코드를 작성하는 훈련이 먼저 되어야 한다고 생각한다.
See you again~~