프로그래머의 미덕은 부지런함이 아니라 게으름이라고 합니다.
게으른 프로그래머일수록 더 효율적인 코드를 만들고, 다른 게으른 이들의 인생을 더 편하게 만들어줄 줄 알기 때문입니다.

게으른 프로그래머는 두자리 수 이상의 곱셈을 절대 손으로 하지 않습니다.
게으른 프로그래머는 반복되는 귀찮은 작업을 사람이 직접 손으로 하는 것을 죄악이라 믿습니다.
게으른 프로그래머는 또한 코드 반복이 일어나면 하늘이 무너진다고 믿습니다.

여기 게으른 우리들을 위한 프로그래밍 언어 루비가 있습니다.

루비는 마츠모토 유키히로(서양 쪽에서의 애칭은 Matz입니다.)라는 일본인이 만든 인터프리터 언어입니다. 펄, 파이썬, 스몰토크 등 기존의 스크립트 언어에서 이것저것 장점을 따다가 합쳐 만들어냈습니다. 루비는 코드의 반복을 죄악이라 믿고, 읽기에 귀찮지 않아야 하며, 무엇보다 코딩을 할 때 스트레스를 받지 않아야 한다고 믿는 사람들이 만든 언어입니다.

튜토리얼을 보고 대충 따라오시다 보면 다른 "귀찮은" 언어로는 돌아갈 수 없는 자신을 발견할지도 모릅니다.
다만, 가끔 설명이 좀 과하게 짧아도 이해해 주시기 바랍니다. 저도 게으른 프로그래머랍니다 :)



-1. if (programmer) { goto 0; } else { return; }

The three chief virtues of a programmer are: Laziness, Impatience and Hubris.
  - Larry Wall

시작하기 전에

당신은 프로그래밍 언어에 익숙하신가요? 이 튜토리얼은 당신이 C/C++/Java 가운데 한가지 언어에 익숙하다는 가정 하에 진행됩니다. 당신이 이 언어들에 별로 익숙하지 않다는 얘기는 아예 프로그래밍 언어를 접해본 적이 없다는 얘기겠지요. 그런 분들에게는 초보자를 위한 다른 튜토리얼을 추천합니다. 루비는 프로그래밍을 처음 해보는 사람에게도 훌륭한 안내자가 됩니다만, 이 튜토리얼은 프로그래밍을 해본 사람들에게 루비를 좀더 빠르게 이해시키기 위해 썼습니다.

C/C++/Java 가운데 한가지 언어에 익숙하시다면 아래 튜토리얼에 나온 예제들을 따라해 보세요. 모든 프로그래밍 언어 안내서에서 하는 말이지만, 백문이 불여일견(百問 不如一見)이고 백견이 불여일타(百見 不如一打)입니다. 프로그래밍 언어를 배우는데 직접 쳐보는 것보다 더 빠른 방법은 없습니다.

당신이 혹시 python이나 perl에 익숙하다면 한 2챕터 정도까지는 그냥 훑어만 보셔도 무방합니다. 인터프리터 언어에 이미 익숙한 분들이라면 루비가 정말 재밌어지는 건 한 2~3챕터 지난 다음부터일 겁니다.

그러면 정말로 시작해볼까요.



0. puts "Hello, ruby!"

Zero is the most natural number.
  - Dijkstra

루비를 시작하려면 먼저 인터프리터가 있어야겠죠. 윈도우 사용자라면 여기서 인스톨러를 다운받아서 그냥 설치하시는 게 제일 속편합니다. 안 게으르시다면 인터프리터랑 인터랙티브 쉘을 따로 받으셔도 되고, 아예 소스 코드를 다운받아서 컴파일하셔도 됩니다만, 우리는 게으르니까요. 다른 운영체제의 경우엔 죄송하지만 그냥 넘어가겠습니다. 맥 OS에는 그냥 구입할때부터 설치되어 있고, 리눅스도 데비안이나 레드햇 계열은 아주 쉽게 설치할 수 있다는군요.

설치하셨다면 실행 -> cmd 해서 커맨드 프롬프트를 열어 주세요. 그 다음 irb라고 치면 다음과 같은 프롬프트가 나옵니다.

irb(main):001:0>


irb는 인터랙티브 루비(interactive ruby)의 약자라고 합니다. 루비 커맨드를 실행하면 결과가 바로 돌아오는 편리한 쉘 환경입니다. 단도직입적으로, 여기에 우리의 첫번째 프로그램을 써볼까요.

irb(main):001:0> puts "Hello, ruby!"
Hello, ruby!
=> nil

아하, 쉽습니다. irb에서 나가시려면 exit라고 치면 됩니다.

다른 방법으로는 루비 인터프리터인 ruby를 사용할 수 있습니다. irb는 코드를 저장하고 고치고 할 수 없으니 대개의 루비 프로그램이 이 방법으로 실행되겠죠? 선호하시는 텍스트 에디터를 열어 아까 쓴 코드를 hello.rb라는 이름의 파일로 저장합니다. 그리고 커맨드라인에서 다음과 같이 인터프리터를 부릅니다.

ruby hello.rb

결과는 마찬가지로 Hello, ruby! 를 출력합니다.

Hello, ruby!

통합 개발환경에 더 익숙하신 분은 같이 인스톨러에 딸려오는 통합 개발환경인 SciTE나 FreeRIDE를 함께 사용해 보세요. 코딩하고 F5를 누르면 결과창에 결과가 출력될 겁니다.
eclipse에 익숙하신 분들은 eclipse에서 RDT를 설치하시면 루비를 지원합니다.
추가: 요즘 루비 커뮤니티에서 떠오르고 있는 IDE로 netbeans라는 IDE와 Aptana Studio라는 IDE가 경쟁중입니다. Aptana Studio는 예쁘고 기능이 다양하지만 아직 버그가 많다는 것이 많은 사람들의 의견이고, netbeans가 보다 안정적이고 사용하기도 편하다는군요. 저는 요즘들어 netbeans를 사용하고 있습니다.



1. print "ruby is easy!\n"

Example isn't another way to teach, it is the only way to teach.
  - Albert Einstein

(이 장에서 진행되는 코드는 irb에서 실행하시는 것이 편합니다.)


실행하는 법을 알았으니 방금 실행한 코드가 무엇을 뜻하는지 알아볼까요.

puts는 문자열을 출력하는 루비의 내장 함수입니다. 정확히 말하면, 문자열을 출력하고 뒤에 줄바꿈(\n)을 덧붙입니다. "Hello, ruby!"는 말할 것도 없이 문자열 타입이지요. 그런데 Java나 C/C++에 익숙하신 분이라면 puts의 문법이 좀 이상하게 느껴질 겁니다. 함수를 호출하는데 괄호가 없군요?

루비에서 함수를 호출할 때 괄호는 그냥 옵션입니다. 아래 코드 두 줄이 의미하는 바는 정확히 같습니다.

puts "Hello, ruby!"
puts("Hello, ruby!")


저는 괄호를 붙이지 않는 쪽을 선호합니다. 기존 언어에 익숙하신 분들은 좀 읽기 어려우실지도 모르지만 조금만 익숙해지면 훨씬 읽기 편하다는 것을 알게 될 겁니다.

문자열을 출력하되 뒤에 줄바꿈을 덧붙이지 않으려면 puts 대신 print를 쓰면 됩니다. 줄바꿈을 덧붙이느냐 아니냐의 차이 뿐 두 함수의 동작은 동일합니다.

irb(main):001:0> puts "ruby is easy!"
ruby is easy!
=> nil
irb(main):002:0> print "ruby is easy!"
ruby is easy!=> nil
irb(main):003:0>

문자열만 자꾸 출력하고 있으려니 프로그래밍 언어라는 생각이 안 드는군요. 이제는 진짜 계산을 해봅시다.

irb(main):001:0> 4+3
=> 7
irb(main):002:0> 3-8
=> -5
irb(main):003:0> 3*2
=> 6
irb(main):004:0> 4/3
=> 1
irb(main):005:0> 3**3
=> 27
irb(main):006:0> 2**100
=> 1267650600228229401496703205376


쉽군요. 벌써 짐작하셨겠지만 3**3은 33을 의미합니다. 그런데 잠깐, 마지막 숫자의 크기가 너무 터무니없지 않나요? 2100? 100비트 정수?

루비가 다루는 정수에는 크기 제한이 없습니다. 무제한 크기의 Bignum 타입을 기본으로 지원하기 때문에 "혹시 0x7fffffff보다 커지면 어쩌지..." 이런 고민 없이 그냥 계산하시면 됩니다. "그래도... 혹시라도... 무제한 크기의 정수 타입이 계산 효율이 떨어지면 어쩌죠?" 라고 물으시는 분들은, 그냥 32비트가 넘는 계산을 하지 마세요!

상수 말고 변수도 있겠지요?

irb(main):001:0> a=2
=> 2
irb(main):002:0> b=3.2
=> 3.2
irb(main):003:0> c=a+b
=> 5.2
irb(main):004:0> d="Ruby"
=> "Ruby"
irb(main):005:0> e=" is fun!"
=> " is fun!"
irb(main):006:0> f=d+e
=> "Ruby is fun!"
irb(main):007:0> puts c, f
5.2
"Ruby is fun!"
=> nil

변수를 선언할 때 "int a=2;" 라던가 "float b=3.2;" 같이 타입을 선언해줄 필요는 없습니다. 변수의 타입 정도는 루비가 알아서 처리해 줍니다.

루비의 모든 것은 오브젝트입니다. 숫자도 오브젝트, 문자열도 오브젝트죠. 그래서 숫자나 문자열에서 함수를 호출할 수 있습니다.

irb(main):001:0> -3.abs
=> 3
irb(main):002:0> 5.2.to_i
=> 5
irb(main):003:0> "Hello".length
=> 5
irb(main):004:0> "Hello".reverse
=> "olleH"
irb(main):005:0> "4.3".to_f
=> 4.3
irb(main):006:0> "4".to_i
=> 4
irb(main):007:0> -3.to_s
=> "-3"

슬슬 재밌어지죠?

진짜 프로그래밍 언어라면 배열이 있어야겠죠. 배열은 꺾은 괄호 안에 콤마로 구분해서 표현합니다.

irb(main):001:0> a = [-3, "Array", 2.6]
=> [-3, "Array", 2.6]
irb(main):002:0> a[1]
=> "Array"
irb(main):003:0> a[0] = -4
=> -4
irb(main):004:0> a
=> [-4, "Array", 2.6]
irb(main):005:0> a.reverse
=> [2.6, "Array", -4]
irb(main):006:0> a.length
=> 3
irb(main):007:0> [3, 1, 2].sort
=> [1, 2, 3]

문자열이나 상수와 마찬가지로 타입을 선언해줄 필요가 없습니다.
게다가 보시다시피 배열 안에 들어있는 오브젝트의 타입들이 서로 달라도 개의치 않습니다.
좀더 장난을 쳐볼까요?

irb(main):001:0> a = [-4, "Array", 2.6]
=>
[-4, "Array", 2.6]
irb(main):002:0> a[3] = 2.8
=> [-4, "Array", 2.6, 2.8]

irb(main):003:0> a << 3.0
=> [-4, "Array", 2.6, 2.8, 3.0]
irb(main):004:0> b = a + ["a", "b", "c"]
=> [-4, "Array", 2.6, 2.8, 3.0, "a", "b", "c"]
irb(main):005:0> c = b - ["Array", "b", 2.6, 3.0, -4]
=> [2.8, "a", "c"]
irb(main):006:0> c[2] = [1, 2]
=> [1, 2]
irb(main):007:0> c
=> [2.8, [1, 2], "c"]
irb(main):008:0> c * 3
=> [2.8, [1, 2], "c", 2.8, [1, 2], "c", 2.8, [1, 2], "c"]

보시다시피 크기도 마음대로 변하고, 덧셈, 뺄셈에 곱셈까지 가능합니다! 결과가 직관적이라는 건 말할 것도 없겠지요.

인덱스를 가지고도 장난을 쳐봅시다.

irb(main):001:0> a = ["A", "B", "C", "D", "E"]
=> ["A", "B", "C", "D", "E"]
irb(main):002:0> a[1..3]
=> ["B", "C", "D"]
irb(main):003:0> a[2, 2]
=> ["C", "D"]
irb(main):004:0> a[-1]
=> "E"
irb(main):005:0> a[-2]
=> "D"
irb(main):006:0> a[2..-1]
=> ["C", "D", "E"]
irb(main):007:0> a[1..2] = ["F", "G"]
=> ["F", "G"]

irb(main):008:0> a
=> ["A", "F", "G", "D", "E"]


짐작하셨겠지만 [1..3]은 1에서 3까지의 원소를 가져오고 [2, 2]는 2부터 시작해서 2개의 원소를 가져옵니다.
-1이나 -2 같은 음수를 가지고 인덱싱하는 것은 생소하게 느껴지겠지만 굉장히 편리합니다. 잘 이해가 안되신다면 이렇게 생각하세요

인덱스 크기가 0..4까지 있을 때

실제 인덱스:  0  1  2  3  4  0  1  2  3  4
음수 인덱스: -5 -4 -3 -2 -1  0  1  2  3  4


특히 [2..-1] 같은 인덱싱은 "2에서 맨 끝까지"를 표현할 수 있는 아주 편리한 방법입니다.

문자열도 일종의 배열이므로, 문자열에서 할 수 있는 웬만한 장난은 문자열에서도 다 가능합니다. 다만 주의하실 것이 있습니다.

irb(main):001:0> a = "ruby is fun!"
=> "ruby is fun!"
irb(main):002:0> a[0..3]
=> "ruby"
irb(main):003:0> a[0]
=> 114
irb(main):004:0> a[0, 1]
=> "r"
irb(main):005:0> a[0] == a[0, 1]
=> false
irb(main):006:0> a[0] == ?r
=> true

보시다시피, 문자열의 일부를 잘라내면 여전히 문자열이지만 문자열에서 글자를 하나만 인덱싱하면 하나의 바이트로 취급하기 때문에 정수가 반환됩니다.
그러나 첫번째 글자가 r인지 알기 위해서 꼭 r의 아스키 코드인 114를 알아야 하는 건 아닙니다. 위에서처럼 a[0, 1]로 한 글자 크기의 문자열을 잘라내서 "r"과 비교하는 방법이 있고, 그거 말고도 r이라는 글자 앞에 ? 기호를 붙이면 r에 해당하는 아스키 코드로 자동으로 변환해줍니다.

마지막으로 열어볼 자료형은 해시(Hash)입니다. 해시는 키워드(key)를 넣어주면 값(value)이 돌아오는 자료구조입니다.
C++ STL의 std::map이나, Java의 HashMap, 또는 TreeMap을 생각하시면 됩니다.

irb(main):001:0> a = {"ten" => 10, "hundred" => 100, "thousand" => 1000}
=> {"thousand"=>1000, "hundred"=>100, "ten"=>10}
irb(main):002:0> a["ten"]
=> 10
irb(main):003:0> a["thousand"]
=> 1000
irb(main):004:0> a["sparta"] = 300
=> 300
irb(main):005:0> a
=> {"thousand"=>1000, "hundred"=>100, "ten"=>10, "sparta"=>300}
irb(main):006:0> a[0] = "zero"
=> "zero"
irb(main):007:0> a
=> {0=>"zero", "thousand"=>1000, "hundred"=>100, "ten"=>10, "sparta"=>300}
irb(main):008:0> a["array"] = [1, 2, 3]
=> [1, 2, 3]
irb(main):009:0> a
=> {0=>"zero", "thousand"=>1000, "array"=>[1, 2, 3], "hundred"=>100, "ten"=>10, "sparta"=>300}

간단하지만 편리하죠.

이렇게 해서 루비의 기본 자료형을 전부 다 보셨습니다. 손이 좀 아프지만 보람찬 챕터였군요.