2. There is more than one way to do it

저는 국민학교 때 본 정체불명의 컴퓨터 학습만화를 통해 처음 GW-BASIC이라는 프로그래밍 언어가 있다는 것을 알게 되었습니다. (2진수에 대해서도 덤으로 알게 되었지만 GW-BASIC과 2진수 사이에 도무지 무슨 관련이 있는지는 한참동안 알지 못했습니다.) GW-BASIC은 함수도 없고, 함수가 없으니 당연히 지역변수도 없고, (모든 변수는 전역변수였습니다.) 배열 외에는 쓸만한 자료구조도 없고, 끔찍한 줄번호와, GOTO, IF, FOR 만으로 모든 것을 헤쳐나가야 하는 언어였습니다. 그때 제가 책을 참고해가며 사흘을 낑낑거려 처음 짠 코드는 바로 이거였습니다.

10 REM "용서하세요. 그땐 아무도 코드 들여쓰기를 가르쳐주지 않았답니다!"
20 SUM = 0
30 FOR I = 1 TO 10
40 SUM = SUM + I
50 NEXT I
60 PRINT SUM

문법이 맞는지 지금은 가물가물하군요. BASIC을 배워본 적이 없는 행운아분들을 위해 이 코드를 간단히 설명하자면, for문으로 I를 1부터 10까지 증가시켜 가며 SUM에 그 값을 더해서 출력하는 간단한 코드입니다. 이 코드를 실행시키면 55라는 답이 나왔습니다.

BASIC이라는 언어는 1963년에 개발되었습니다. Matz가 처음 Ruby를 발표한 것은 1995년이지요. 30년이라는 시간이 흘렀지만 for 문의 사용법은 크게 변하지 않은 것처럼 보이는군요.

# 이제는 들여쓰기를 배웠지요!
sum = 0
for i in 1..10
    sum = sum + i
end
puts sum

C 스타일 for 문과는 많이 다릅니다만 BASIC의 for 문과는 거의 똑같아 보이는군요.

그러나 똑같은 코드를 루비에서는 이렇게도 짤 수 있습니다.

# 변수 i 주변을 둘러싼 괄호는 shift + \ 를 누르면 나오는 세로줄 기호입니다.
sum = 0
(1..10).each { |i| sum = sum + i }
puts sum

Smalltalk에 익숙한 분이 아니라면 정말 생소한 코드일 겁니다. 저도 처음 봤을 땐 뭐 이런 코드가 있어? 싶었으니까요. 그러나 루비에 조금만 익숙해지면 for 문 따위 아예 잊어버리게 되실 겁니다. each가 훨씬 재밌거든요!

똑같은 코드를 조금만 풀어 써 봅시다. 위 코드는 사실 다음 코드와 동일한 의미를 갖습니다.

sum = 0
list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
list.each { |i| sum = sum + i }
puts sum

그러면 이제 이 코드가 대체 뭘 하자는 건지 조금 알게 될 것 같습니다. 1..10 은 사실 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]을 나타내는 방법 가운데 하나입니다. (사실 두 가지 표현은 약간 다른 의미를 갖지만 지금은 중요하지 않으니 넘어갑시다.) { |i| sum = sum + i } 이 부분은 변수 i를 받아서, sum = sum + i 를 계산하는 코드 조각이지요. (코드 조각이 대체 뭐하는 거야? 하는 의문은 조금만 미뤄둡시다.) 그러면 list.each 의 역할은?
"list 객체의 모든 원소에 대해 { ... } 안에 들어있는 코드 조각을 각각 실행해라" 라는 의미를 갖는 멤버 함수입니다.

감이 잘 안오시나요? 그럼 이렇게 해보죠.

# ["R", "u", "b", "y"]의 원소들을 각각 str로 받아 puts str로 출력합니다.
["R", "u", "b", "y"].each { |str| puts str }
# 루비의 for 문은 기존의 언어에 익숙한 사람들을 위한 일종의 서비스입니다.
# 사실 루비의 for 문은 each를 이용해 구현되었기 때문에
# 다음 코드의 의미는 완전히 같습니다.
for str in ["R", "u", "b", "y"]
    puts str
end
R
u
b
y

짜잔. 리스트의 내용을 전부 출력하는 아주 간단한 코드가 만들어졌군요.

for 루프를 도는 두 가지 방법을 배웠습니다. (사실 더 있지만 여러분의 머리를 복잡하게 만들고 싶지 않군요. :P ) if문은 어떨까요?

# 제 나이가 이렇다는 건 아니구요 :P
age = 19

if age >= 40
    puts "대선에 출마할 수 있습니다"
elsif age >= 19
    puts "투표할 수 있어요"
else
    puts "투표할 수 없어요"
end
#=> 투표할 수 있어요

#if문과 실행문을 한줄에 붙여 쓰려면 사이에 then을 넣어줘야 합니다.
if age >= 18 then puts "술을 마실 수 있어요" end
#=> 술을 마실 수 있어요

아하. 간단합니다. C의 전통적인 { ... } 괄호 대신 if .. (then) .. elsif .. else .. end로 블록을 구분한다는 점이 다르군요.

그러나 여기부턴 또 새로운 세계입니다.

age = 19
# 조건문과 실행문의 위치를 이렇게 뒤집을 수도 있어요.
# 한줄에 붙여쓰고 싶다면 사실 이게 더 편리합니다.
# end 문을 안 넣어도 된다는 장점이 있거든요.
puts "술을 마실 수 있어요" if age >= 18
#=> 술을 마실 수 있어요

# 루비의 모든 문장은 항상 반환값을 갖습니다. 심지어 if문도 반환값을 갖지요.
# 여기서는 if문이 string을 반환하기 때문에 puts가 값을 받아서 출력할 수 있습니다.
puts (if age >= 19 then "투표할 수 있어요" else "투표할 수 없어요" end)
#=> 투표할 수 있어요

if 문을 사용하는 방법도 한두가지가 아니군요. 뭐가 이렇게 복잡한가요?

그건 루비의 모토가 "There is more than one way to do it" 이기 때문입니다. 어떤 일을 하는데 방법은 한 가지가 아니라는 거죠.

지금까지 제가 접해본 언어들은 대부분 한 가지 일을 하는데 한가지, 많아야 두어가지 정도의 방법밖에 없었습니다. 루비는 좀 다릅니다. 입으로 말을 할 때 단어를 고르는 것처럼, 코딩을 할 때도 더 간단하게, 더 짧게, 더 재미있게, 더 우아하게 등등등 가운데 취향에 맞는 걸로 골라서 코딩할 수 있습니다.
물론 너무 코드를 간단하고 우아하게 만드는데 재미를 붙이다 보면 오히려 알아볼 수 없는 코드를 만들어내는 경우도 생기고, 정작 할일을 하는데 오히려 방해가 되는 경우도 생기죠. 사실 요즘 인기있는 또다른 언어인 파이썬은 그런 이유로 전통적인 "There is only one way to do it" 을 고수하고 있습니다.
마음에 드는 쪽으로 고르시면 됩니다. 그치만 루비 쪽이 좀더 재미있지 않나요?

루비에는 물론 while문도 있습니다.

sum = 0
i = 1
while i <= 10
    sum = sum + i
    i += 1
end
puts sum

# => 55

while을 실행문 뒤에 붙이는 것도 가능합니다. begin ... end 문을 사용하고 뒤에 while을 붙이면 됩니다.

sum = 0
i = 1
# begin ... end 는 C/Java의 { } 블록처럼 블록을 나타내는 루비의 방법입니다.
begin
    sum = sum + i
    i+=1
end while i <= 10
puts sum

# => 55

마지막으로 case .. when .. else .. end 만 살펴보고 지겨운 조건문들은 건너뛰도록 하죠.

i = 3

case i
when 1
    # i가 1이라면 1을 출력합니다.
    puts "1"
when 2, 3, 4
    # 여러 조건을 지정해 줄 수 도 있습니다.
    puts "2..4"
when 5..7
    # 범위도 지정해 줄 수 있구요
    puts "5..7"
when 8, 9..10
    # 여러 개의 범위나 조건을 같이 쓸 수도 있지요
    puts "8..10"
else
    # case문에서 else는 C/Java의 default와 같은 역할을 합니다.
    puts "1..10 사이의 수가 아니네요"
end

#=> 2..4

C/Java에서처럼 break 문을 쓰지 않아도 된다는 점이 편리한데, 이걸 혹시 단점이라고 생각하실 분이 있을지도 모르겠습니다. C/Java의 switch 문은 break 문을 쓰지 않고 여러 조건을 이어서 써 주면 여러 개의 조건에서 동일하게 동작하도록 코드를 짤 수 있지요. 그러나 루비는 여러 조건을 콤마로 구분해서 같이 지정해 주면 의도는 같지만 더 알기 쉬운 코드를 짤 수 있습니다. 루비의 승리네요 +ㅅ+