· KLDP.org · KLDP.net · KLDP Wiki · KLDP BBS ·
Humble Little Ruby Book/Chap1


1. 1 루비의 세계에 온 것을 환영합니다.


이 절에서는 여러분이 루비 언어를 공부하기 위해 필요한 루비 언어의 문법적인 사항(syntactic sugar)들과 언어적인 제한 사항(linguistic misfortunes)들을 최대한 간략하게 소개하려고 합니다. 만약 여러분이 자신을 루비 구루라고 생각하거나, 어떤 이유로는 언어 강좌를 싫어한다거나, 지난 밤에 Holiday Inn Express에서 묵으셨다면(또는 그러려고 했지만 자금 문제로 포기했던 경우에도) 이 절을 건너뛰어도 좋습니다.

1.1. 루비의 기본 개념

루비는 객체 지향 언어이지만, 여러분이 C++ 혹은 다른 부정확한 객체 지향 언어(unfortunate excuse for an object-oriented language)를 사용해 왔었기 때문에 이 절에서 설명할 내용을 모두 이해하고 있을 것이라고 판단하여 이 절을 건너뛰기 전에 잠시만 멈춰 최소한 다음 한 문장 만이라도 읽어주길 바랍니다. 루비에서는 여러분이 조작하는 모든 것은 객체입니다. 모든 것이라는 말은 객체 간의 연산에 의한 결과 조차도 객체라는 것을 뜻합니다. 이러한 접근법은 원시 자료형(primitive types)을 가지거나 아무런 값도 반환하지 않는 문장(statement)을 가진 C++나 자바와는 다른 것입니다.

만약 여러분이 객체 지향 프로그래밍(혹은 프로그래밍 자체)에 익숙하지 않다면, 이것은 전혀 다른 이야기입니다. 루비 코드를 작성할 때나 일반적인 객체 지향 코드를 작성하는 경우에는, 여러분이 코드에서 처리하고 싶은 과정을 표현하기 위한 모델을 작성하는 것이 중요합니다. 예를 들어 요리법을 알려주기 위한 프로그램을 작성하는 경우라면, 여러가지 조리법에 대한 목록을 작성하고 싶을 것입니다(내 추론이 놀랍지 않나요?). 그리 객체 지향적이지 않은 방식으로 모델링하게 되면, 여러분은 아마도 여러 가지 데이터들을 저장하기 위한 몇 가지 종류의 목록들을 사용할 것이며 이들 각각의 데이터에 대한 위치를 파악하기가 매우 힘들어 질 것입니다. 객체 지향 프로그래밍은 이러한 작업을 간편하게 처리해 주며, 필요한 요소들을 모델링하기 위해 클래스와 객체들을 생성할 수 있도록 해 줍니다. 위의 예제에서 여러분은 문자열 타입의 name 속성과 author 속성을 가지며, 해시 혹은 배열 타입의 ingredients 속성을 가진 Recipe 클래스를 생성할 수 있습니다. 클래스의 목적은 여러분이 작성할 프로그램 내의 어떤 모델링하는 것입니다. 클래스는 프로그램 내의 명사(객체)를 위한 기본 틀을 생성합니다. 클래스의 인스턴스(instance) 혹은 객체(이 두 가지 용어는 같은 의미로 사용할 수 있습니다)는 이 기본 틀을 이용하여 실제 데이터를 처리합니다. 이 예제에서 목록 내의 각 조리법에 대한 객체들은 Recipe 클래스의 인스턴스가 될 것이며, 이들은 데이터를 저장하거나, 조리법과 관련된 연산(예를 들면, 재료의 목록을 저장하거나, 목록에 재료를 추가하는 등의 작업)을 수행하거나, 일반적인 조리법에 적용되는 제한 사항(예를 들면 재료의 양을 말할 때는 숫자만을 사용한다든지 조리법에는 이름이 있어야 한다는 등의 사항)들을 적용하는 것이 가능합니다.

1.2. 루비의 자료형

앞에서 루비 내의 모든 것은 객체라고 했던 말은, 모든 것이 (특화된 기능이 없는) 일반적인 것이라든가, 내장(built-in) 클래스가 존재하지 않는다는 말은 아닙니다. 루비는 여러분의 응용 프로그램 내의 모든 요소들을 구성하는 데 사용할 수 있는 몇 가지 내장 클래스들을 제공합니다. 이러한 타입들은 Object클래스라는 한가지 클래스로부터 파생된다는 점에서 다른 언어들과 차이를 보입니다. 이러한 구조는 C 언어와 같이 몇 가지의 기본 타입을 가진다는 것이 아니라 오직 하나의 "기본" 타입만이 존재하는 것을 의미합니다. 다음은 이러한 타입들의 특징과 사용법에 대해서 알아보기로 합니다.

1.2.1. 문자열

가장 먼저 살펴볼 자료형은 문자열 타입입니다. 이것은 단순히 일련의 문자들을 표현하기 위한 바이트의 순차적인 모임입니다. 문자열은 여러 가지 방식으로 구성될 수 있지만, 가장 일반적인 형태는 문자열 상수(string literal)를 사용하는 것입니다. 문자열 상수는 작은 따옴표나 큰 따옴표로 둘러싸인 문자들의 집합을 말합니다. 예를 들면 다음과 같습니다.

puts 'Hello, Darling.' → Hello, Darling.
puts 'What\'s up?' → What's up?
puts "A\tTab." → A tab.

잠시만요! 저 안에 사용된 역슬래시는 무엇일까요? 이들은 예외 문자(escape sequence)들로 역슬래시 뒤에 영문자 하나를 붙여 일반적으로는 출력할 수 없는 문자(예를 들어, 위의 예제에서 \t는 탭 문자를 생성하기 위해 사용했지만 다른 것을 이용하면 줄바꿈 문자(new line)나 수직 탭과 같은 것들도 생성할 수 있습니다)를 생성하기 위해 사용합니다. 제가 출력할 수 없는 문자라고 했던 것은 두 번째 문장에서 작은 따옴표를 출력하기 위해 \'를 사용했기 때문입니다. 일반적으로 작은 따옴표 문자는 (작은 따옴표를 통해 구성된 문자열에서) 문자열의 끝을 나타내는데 사용되므로 출력할 수 없습니다.

이미 알아챈 분들도 있을테지만, 위의 예제에서 어떤 문자열에는 작은 따옴표를 사용했고 다른 것에는 큰 따옴표를 사용했습니다. 이 두 가지 표기법에는 차이점이 있습니다. 작은 따옴표를 통해 생성된 문자열은 좀 멍청해서 매우 적은 종류의 예외 문자들만을 사용할 수 있으며 (사실은 역슬래시+작은 따옴표만을 사용할 수 있답니다), 여러분이 성능을 향상시키는 데 주안점을 두고 있는 것이 아니라면 (큰 따옴표로 구성된 문자열을 작은 따옴표로 바꾸는 작업은 아마도 성능 개선 시에 시도해 볼 만한 마지막 수단일 것입니다.) 일반적으로 자주 사용되지 않습니다. 반면에 큰 따옴표를 통해 생성된 문자열은, 문자열 치환?(interpolation)을 통해 더 많은 기능을 제공합니다. 먼저, 큰 따옴표 문자열은 훨씬 더 많은 예외 문자들을 지원합니다. 위에서 보았듯이 줄바꿈 문자를 생성하기 위해 \n을, 탭 문자를 생성하기 위해 \t 등을 사용할 수 있습니다. 아래는 큰 따옴표 문자열 내에서 사용할 수 있는 모든 예외 문자를 정리해 둔 표입니다(그리 많지 않네요).

예외 문자
\a 벨 알람 \f 폼 피드(form feed)
\??? 8진수 값 \n 줄바꿈
\x?? 16진수 값 \r 리턴
#{???} Ruby 표현식 ???의 값 \s 공백
\e 이스케이프(escape) \t
\c?
\C-?
컨트롤-? \v 수직 탭
\M-? 메타-? \b 백 스페이스
\M-\C-? 메타-컨트롤-?

표를 보시면 아마 큰따옴표로 묶인 문자열이 또 다른 흥미로운 기능을 제공한다는 사실을 알아채실지도 모르겠습니다 그것은 바로 (expression interpolation) <- 이겁니다... 이 얘기를 통해 간단하게 다음과 같은 생각을 해볼 수 있겠군요. 문자열에 바로 값을 집어넣을 수 있는것... (PHP에서는 간접적으로 가능했지만 루비에서는 정말 정곡 제대로 찔러주는군요) 다시 말해두겠지만 루비의 모든 요소는 다 객체이고 심지어는 결과도 객체입니다. 결국 다음과 같은것도 가능하다는 얘기지요 :)

"Inches/yard: #{12*3}" → Inches/yard: 36
"#{"Tora! "*3}" → Tora! Tora! Tora!
두번째 예제에서는, 루비 코드상에 존재하는 모든 것이 객체라는 것을 알아차릴 때까지는 당황스럽게 만들 것입니다. (말 그대로, 문자열 조차도 객체입니다. 문자열은 문자열 클래스나 다름 없죠). 문자열이 문자열 클래스 객체로 만들어지기 전에는 이 객체를 다른 객체처럼 부려먹을수도 있습니다. 위와 같은 경우에는, 3으로 문자열을 곱한다는것에 대해 이렇게 생각하실수도 있습니다 : 문자열을 세번 나타나도록 하는 공식...!?

한편으로는, 문자열을 만들어내는, 드물지만 근사한 방법중에 하나가 바로 구분자인 %Q 또는 %q를 사용하는 방법입니다. 이 생성자를 통한 방법으로 동작하는 방법은 알파벳 숫자나 멀티바이트 문자를 사용하지 않은 %Q 나 %q를 활용하는 것인데 다음을 보시면 이해가시리라 믿습니다.

%q{Hoagies & grinders!} → Hoagies and grinders!
%Q;#{"Navy beans! "*3}; → Navy beans! Navy beans! Navy beans!
주의할 점이라면 %q 는 작은 따옴표가 붙은 문자열같이 다룬다는 것이고 %Q 는 큰따옴표로 묶인 문자열처럼 다룬다는 것입니다. 한마디로 문자의 크기에 따라 다르다는거죠.. 정리하자면: 작은 q는 작은따옴표, 큰 Q는, 큰따옴표입니다.

루비에서 문자열을 생성하는 또다른 방법으로는 here documents 혹은 "heredocs"라고 하는 것이 있습니다(펄 프로그래머들이여 기뻐합시다!). 이 방식은 문자열이 시작됨을 알리기 위해 << 기호 뒤에 구분자를 지정하고 문자열의 맨 끝에 지정된 구분자를 넣는 방식입니다. 예를 들면 아래와 같습니다:

my_string = <<MY_STRING
This is a simple string that is
pre-formatted, which means that the
way it is formatted here including
tabs and newlines will be duplicated
when I print it out.
MY_STRING

마지막으로, 문자열 인스턴스를 만들 수 있는 방법에는 객체의 to_s 메소드를 사용하는 방법이 있습니다. 많은 객제들이 이 메소드를 호출하면 일관된 반환값을 돌려주지만(예를들면 클래스이름과 인스턴스 id 같은것이죠), 몇 몇 클래스는 좀 쓸모있는 것을 돌려줍니다. 예를 들자면 Fixnum은 의미없는 루비 데이터가 아니라, 숫자값으로 된 문자열을 넘겨줄 것입니다

1.2.2. 수치형

두번째 자료형은 수치형(numbers)입니다. 수치형은 루비에서 내장(built-in) 클래스로 만들어져 있으며 숫자를 다루기 위해 사용됩니다. 수치형에는 Fixnum 과 Bignum 두가지가 있습니다. 수치형 객체가 만들어 질때 (-2^30)에서 (2^30-1) 사이의 값으로 만들어지면 Fixnum의 인스턴스가 생성되고, 이 범위를 벗어나는 값이 들어오면 Bignum 의 인스턴스가 생성됩니다. 루비는 이러한 객체 할당에 대한 동작을 알아서 잘 처리해 주므로 가계부 프로그램을 만들 때, (필자와 같이) 은행잔고가 항상 -2^30 밑으로 내려가 있는 경우에도 아무런 걱정을 할 필요가 없습니다.

따옴표로 감싸지 않은 숫자는 정수형으로 인식됩니다. 기본적으로 10진수를 사용하며 용도에 따라 8진수,16진수,2진수 등도 지원합니다.

-123456789 → -123456789 # Fixnum
0d123456789 → 1234567890 # Fixnum
1234323424231 → 1234323424231 # Bignum
0x5C1 → 1473 # Hex
01411 → 777 # Octal
1_90_33 → 19033 # Fixnum

루비는 숫자에 있는 언더바(_)를 무시합니다(어떤 사람들은 긴 숫자를 쓸때, 203_323_323과 같이 콤마 자리에 언더바를 사용한다고 합니다). 2진수를 사용할때는 앞에 0b를 붙입니다(0b10110). 8진수를 사용할때는 0을 붙이고(0732), 16진수를 사용할때는 0x를 붙입니다(0xed8f). 아무것도 안붙이면 10진수입니다. 정 뭔가 붙이고 싶으면 0d를 붙여도 됩니다(0d39824).

루비는 정수형 말고 부동소수점형도 지원합니다. 부동소수점은 . 으로 소수점을 표현하고, e 로 승수를 표현합니다(각 부분은 십진수로 표현됩니다). 예를 들면

1.5 → 1.5
1.0e5 → 100000.0
1.e5 → !NoMethodError

소수점의 양쪽은 반드시 숫자가 있어야 합니다. 과학적 표기법으로 부동소수점을 쓸 때는 소수점 옆에 반드시 0을 붙여야 합니다. 그렇지 않으면 e5 와 같은것을 Fixnum 객체의 메소드로 인식해 실행하려 할것입니다.

수치형은 객체입니다(루비의 모든것은 객체입니다). 그렇기 때문에 수치형에도 어떤 동작을 수행하는 메소드들이 있습니다. 숫자의 크기를 받는 size 메소드라던가, 숫자를 문자열로 바꿔주는 to_s 메소드 라던가 하는 많은 메소드 들이 들어가 있습니다.

-4.abs → 4
6.zero? → false

위에 있는 메소드이름은 참 직관적입니다(abs 는 절대값을 받아오는 메소드이고 zero?는 이게 0 이 맞냐고 물어보는 메소드입니다. 0이면 true 리턴합니다.) 물론 더 많은 메소드가 존재하며, 루비 API 문서에 더 많은 정보가 있으니 살펴보기 바랍니다.

숫자는 그냥 보기에 메소드 같이 생기지 않은 메소드도 제공합니다. 산술연산자 같은 것들도 사실은 메소드 입니다.

2 + 2 → 4
6 / 3 → 2
-4 * 2 → -8

연산자의 전체 목록은 아래에 있습니다. 다른 프로그래밍 언어를 사용해 본적이 있다면 산술연산자의 사용법은 동일하므로 어려울 것이 없습니다.

산술 연산자
+ 더하기
- 빼기
/ 나누기(몫)
* 곱하기
() 연산자 우선순위 (즉, 먼저 수행해야 할 연산을 지정)
% 나머지

1.3. 컬렉션

데이터들을 단일한 형태로 모아둘 수 있다면 편리할 것입니다. but everyone knows that collections are where the party is at (at least that's what MTV says). 아마도 신께서 데이터를 따로 두는 것이 좋지 않다고 말씀하셔서, 루비에서는 데이터를 모아두기 위한 몇 가지 방법을 제공하는 것 같습니다.

컬렉션(때로는 컨테이너라는 표현을 쓰기도 합니다)은 관련된 객체들의 집합을 저장하는 객체입니다. 이러한 객체들은 (모든 문자열 객체의 집합과 같이) 형식에 따라, (사람 이름의 집합과 같이) 목적에 따라, 혹은 (좋아하는 치즈의 집합 - 필자는 프로볼로네(provolone)를 좋아합니다..^^ - 과 같이) 임의의 기준에 따라 분류될 수 있습니다. 컬렉션은 여러 데이터들을 저장하고, 정렬하는 등의 작업은 물론 컬렉션 내의 모든 멤버들에게 특정 연산을 수행하는 작업도 처리할 수 있습니다. 컬렉션 내의 각각의 멤버(혹은 원소(element))들도 물론 개별적으로 연산을 처리할 수 있는 공개된(visible) 객체입니다 (즉, 멤버들도 메소드를 호출하거나 추가 및 삭제등이 가능합니다).

1.3.1. 범위(Range)

먼저 소개할 것은 가장 기본적인 타입인 범위(range) 객체입니다. 범위 객체는 1과 9 사이의 모든 수, A부터 Z까지의 모든 문자 등과 같이 어떤 값들의 순차적인 집합을 저장합니다. 범위 객체는 범위의 시작값과 끝값 사이에 점(.) 문자를 여러번 쓰는 방식으로 생성할 수 있습니다. 예를 들어, 여러분이 롤플레잉 게임을 만들고 있는 경우에 각 종족에 따라 가질 수 있는 키의 범위를 설정하고 싶으면 아래와 같이 입력할 수 있습니다.

human = 48..81
elf = 40...68
grotesquely_huge_guy = 120..132

범위 객체를 생성할 때 점 문자를 2번 혹은 3번 사용할 수 있습니다. 점 문자를 2번 사용한 경우에는 범위의 시작값과 끝값을 모두 포함하며, 3번 사용한 경우에는 시작값은 포함하지만 끝값은 제외됩니다. 얼핏 보기에는 표기법이 반대로 된 것이 아닌가 생각될 수도 있지만, 사실은 세번째 점 문자가 너무 공간을 많이 차지해서 범위의 끝값을 밀어내 버린 것이랍니다. 이건 농담이 아니에요. 의심스러운 분은 직접 디버거를 띄워서 확인해 보셔도 좋습니다. 예를 들어 0...7의 범위 객체는 다음과 같은 값들을 저장하게 됩니다.

HLRB-fig1.png
[PNG image (5.44 KB)]

반면에, 0..7의 범위 객체는 아래와 같습니다.

HLRB-fig2.png
[PNG image (6.11 KB)]

이렇게 생성된 범위 객체를 통해 값을 가져오거나, 실질적인 작업을 하고 싶을 것입니다. 범위 객체는 자신을 테스트하고 비교할 수 있는 몇 가지 방법들을 제공합니다. 먼저, == 연산자(이 외에도 나중에 설명할 다른 비교 연산자들이 있습니다) 혹은 eql? 메소드를 사용하면 범위 객체를 다른 범위 객체와 비교할 수 있습니다. 만약 여러분이 빵가게의 매상을 관리하는 소프트웨어를 작성한다면 (제가 듣기론, 요즘 소프트웨어 산업에서는 이 분야가 뜨는 중이라는군요), 오븐에서 구워낸 쿠키들 중에 잘된 것과 실패한 것이 나올 확률을 테스트하기 위한 코드를 작성해야 할 것입니다.

good_cookies = 1...3
bad_cookies = 1..3
burnt_cookies = 1..3
                                                                    
puts(good_cookies == bad_cookies)       → false                       
puts(good_cookies.eql?(burnt_cookies))  → false
puts(bad_cookies == burnt_cookies)      → true

범위 객체는 시작값과 끝값이 같은 경우에 동일한(equal) 것으로 취급됩니다. 하지만 위의 코드에서 good_cookies 객체와 bad_cookies 객체가 같은 시작값과 끝값을 공유하고 있지만, 그 값은 다릅니다. 왜냐하면 포함 플래그(점 문자의 개수에 대한 설명을 기억하시죠?)로 인해 값이 변경되었기 때문입니다. 위의 예제에서 good_cookies 객체의 값은 1,2 이지만, bad_cookies 객체의 값은 1,2,3 입니다. 범위 객체는 또한 === 연산자나 include? 메소드를 통해 특정값이 범위에 포함되어 있는지 검사하는 방법을 제공합니다. 예를 들어 여러분과 여러분의 동업자가 잘 구워진 쿠키의 개수를 예측했지만, 실제로 잘 구워진 쿠키의 개수가 예측한 범위 내에 있는지를 알고 싶다면 다음과 같은 방법을 사용할 수 있습니다:

my_guess = 2
his_guess = 19
                                                                    
puts(good_cookies === my_guess)         → true puts(good_cookies.include?(my_guess))   → true
puts(good_cookies === his_guess)        → false


include? 메소드는 범위 객체 내에 포함된 어떠한 값에 대해서도 결과를 반환할 것입니다 (즉, bad_cookies 객체에 대해 2.44564 라는 값을 검사해도 참값을 반환합니다). 이 이름이 잘 와닿지 않는다면, include? 메소드의 다른 이름(alias)인 member? 메소드를 사용할 수도 있습니다.

1.3.2. 배열

두번째 내장 컬렉션 타입은 배열입니다. 배열은 정수형의 인덱스를 가진 순서가 있는 컬렉션입니다. 만약 컴퓨터 과학의 기초 코스를 수강한 적이 있다면, 배열의 개념은 잘 알고 있을테지만 루비의 배열은 약간 어색하게 느낄 수도 있을 것입니다. 시작하는 C/C++/Java에서처럼 인덱스가 0부터 시작하는 것은 동일하지만, 배열의 각 요소들이 모두 같은 타입일 필요도 없으며 배열 객체를 사용하기 전에 배열의 타입을 선언하지 않아도 됩니다. 따라서 타입에 대한 고려를 하지 않고 다음과 같은 배열을 생성할 수도 있습니다.

HLRB-fig3.png
[PNG image (39.08 KB)]

루비에서 문자 배열은 여러 가지(흥미로운) 방식으로 생성 및 할당될 수 있습니다.

its_so_empty = []
oh_so_empty = Array.new
hello = ['ni hao', 'bonjour', 'hi', 'howdy']
random_types = [13, 'napkin', (1336 + 1).to_s]

배열은 변수, 메소드의 반환 값, 큰따옴표를 이용한 문자열과 같은 어떤 타입의 값으로도 초기화될 수 있으며 심지어 아무런 값이 없어도 됩니다(이 경우 빈 배열이 생성됩니다).

1.3.3. 해시(Hash)


1.4. 변수




sponsored by andamiro
sponsored by cdnetworks
sponsored by HP

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2007-05-06 15:40:04
Processing time 0.0116 sec