'프로그래밍'에 해당되는 글 25건

  1. 2009.02.06 [루비 사용자 가이드] 이 가이드에 대해
  2. 2009.02.06 [루비 사용자 가이드] 기본적인 것들 (1)
  3. 2009.02.06 [루비 사용자 가이드] 객체 초기화
  4. 2009.02.06 [루비 사용자 가이드] 접근자(Accessors)
  5. 2009.02.06 [루비 사용자 가이드] 예외 처리: ensure
  6. 2009.02.06 [루비 사용자 가이드] 예외 처리: rescue
  7. 2009.02.06 [루비 사용자 가이드] 클래스 상수
  8. 2009.02.05 [루비 사용자 가이드] 지역 변수
  9. 2009.02.05 [루비 사용자 가이드] 인스턴스 변수
  10. 2009.02.05 [루비 사용자 가이드] 전역 변수

[루비 사용자 가이드] 이 가이드에 대해

루비/레일스 프로그래밍/루비 사용자 가이드 2009.02.06 10:48

가이드에 대해

이 입문용 사용자 가이드는 여러 곳에 여러 언어로 미러링되어 있습니다. 현재 최신 영어 버전은 rubyist.net에 있습니다. 만약 오래된 버전을 보신다면, 호스트 관리자에게 요청해 최신 버전으로 업데이트 하십시오.

문서 역사

  • 최초 일본어 버전은 matz(루비 언어 창시자)에 의해 작성되었습니다.
  • 첫 영어 번역GOTO Kentaro 과 Julian Fondren에 의해 이루어졌습니다.
  • 재 번역 및 내용 추가가 Mark Slagell에 의해 이루어졌습니다.
  • 한글 번역은 오현석에 의해 이루어졌습니다.

한글버전 저작권

Copyright (c) 2009- 오현석


이 한글 가이드의 저작권은 아래 표시된대로 크리에이티브 커먼즈 라이선스를 따릅니다.


영문버전 저작권

Copyright (c) 2005-2008 Mark Slagell

Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.

A copy of the license is included in the section entitled "GNU Free Documentation License."

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 1 : Comment 0

[루비 사용자 가이드] 기본적인 것들

루비/레일스 프로그래밍/루비 사용자 가이드 2009.02.06 10:47

여기서는 몇 가지 실제적인 내용을 다룹니다.

문(Statement) 구분자(delimiters)

몇몇 언어에서는 프로그램의 각 문장을 구분하기 위해 구두점(예:;)을 요구하곤 합니다. 루비는 그런 방식 대신 shcsh같은 셀이 사용하는 방식을 따릅니다. 한 줄에 여러 문장을 쓸 경우에는 세미콜론으로 구분을 해야 하지만, 줄이 바뀔 경우에는 줄바뀜(라인피드,linefeed)이 세미콜론 처럼 다뤄집니다. 만약 어떤 줄이 역슬래시(\)로 끝나면, 역슬래시 다음에 오는 줄바뀜은 무시됩니다. 이 기능을 사용해서 논리적으로는 한 줄인 프로그램을  여러 줄에 걸쳐서 짤 수 있습니다.

주석(Comments)

주석을 달아야 하는 이유가 무엇일까요? 잘 쓰여진 프로그램은 자기 자신을 잘 설명해주는 문서로써의 기능도 하는 경향이 있긴 합니다만(self-documenting), 여백에 힌트를 써 놓는것이 도움이 되곤 합니다. 또한, 다른 사람들이 여러분의 코드를 보고, 여러분과 동일하게 그 코드를 이해할 수 있다고 믿는 것은 잘못일 겁니다. 또, 실제적인 면에서는, 여러분 자신도 며칠 후에는 예전의 자신이 아니게 됩니다. 예전에 자기가 작성한 프로그램을 수정하거나 개선하려고 할 때, '내가 이걸 쓴건 맞는데, 도데체 뭔 뜻이지?'라고 말한 경험이 없는 사람은 별로 없을겁니다.

몇몇 경험많은 프로그래머는 코드와 모순되거나, 최신 사항을 반영하지 못하는 주석이 문제가 될 수 있다는 점을 지적하기도 하며, 그 지적은 확실히 맞는 말입니다. 분명 주석이 읽기 쉬운 코드를 대신할 수는 없습니다. 만약 여러분의 코드가 분명하지 않다면, 그 코드에 많은 버그가 있을 가능성도 높습니다. 루비를 배우는 동안에는 더 많은 주석을 달 필요가 있음을 발견할 지 모르겠습니다. 하지만, 자신의 생각을 단순하고 우아하고 읽기 쉬운 코드로 잘 표현하게 됨에 따라, 주석은 덜 필요하게 됩니다.

루비는 일반적인 스크립팅 언어처럼 파운드 기호(#)를 주석의 시작을 표시하기 위해 사용합니다. # 다음부터 줄의 끝까지 오는 모든 내용은 인터프리터가 무시합니다.

또한, 큰 주석 덩어리를 작성하는 것을 편하게 하기 위해 "=begin"로 시작하는 줄에서 시작해서 "=end"로 끝나는 줄까지의 모든 내용을 루비 인터프리터는 무시합니다.

#!/usr/bin/env ruby

=begin
**********************************************************************
  This is a comment block, something you write for the benefit of
  human readers (including yourself).  The interpreter ignores it.
  There is no need for a '#' at the start of every line.
**********************************************************************
=end

코드 조직하기

루비의 높은 동적 특성은 클래스, 모듈, 그리고 메소드는 각각을 정의하는 코드가 실행된 다음에야 존재할 수 있다는 것을 의미합니다. 만약 여러분이 더 정적인 언어에서 프로그램을 짜는 분이었다면, 이런 루비의 동적 특성에 놀랄 수도 있습니다.

# The below results in an "undefined method" error:

puts successor(3)

def successor(x)
  x + 1
end

비록 인터프리터가 스크립트 파일의 내용을 실행하기 전에 파일 전체의 문법을 체크하지만, def successor ... end 코드가 실제로 실행된 다음에야 successor 메소드가 생깁니다. 따라서, 스크립트를 나열하는 순서가 문제가 됩니다.

얼핏 생각해 보면, 이런 성격 때문에, 반드시 코드를 엄격하게 밑바닥에서부터 실행되도록 구성(즉, 다른 곳에서 필요한 정의를 먼저 실행되도록 구성)해야 할 것 같습니다. 하지만, 인터프리터가 메소드 정의를 처리할 때, 각각의 참조(변수,메소드,상수 등)에 대한 정의가 실제 메소드가 호출되는 시점 이전에만 된다는 것만 확실히 해 주면, 자유롭게 참조를 사용할 수 있습니다.(역주:아래를 보면 f_to_c가 정의되는 시점에서 scale이 보이지는 않지만, 실제 f_to_c가 호출될 때는 scale이 보인 다음입니다.)

# Conversion of fahrenheit to celsius, broken
# down into two steps.

def f_to_c(f)
  scale(f - 32.0)  # This is a forward reference, but it's okay.
end

def scale(x)
  x * 5.0 / 9.0
end

printf "%.1f is a comfortable temperature.\n", f_to_c(72.3)

이런 방식이 Perl이나 Java보다는 불편해 보이지만, 실제로는 C언어를 프로토타입(prototype) 없이 사용하는 경우보다 더 제약이 적은 상황입니다(C언어에서 프로토타입을 사용하지 않는 경우에는, 모든 참조에 대해 참조 관계를 명확히 해서, 정의가 참조 이전에 오게 작성해야 합니다). 탑레벨 코드를 소스 파일의 맨 나중에 넣는 것은 항상 잘 동작합니다. 프로그램이 원하는데로 동작하도록 하는 가장 현명하고 쉬운 방법은 main 함수를 파일의 맨 앞에서 정의하고, 파일의 맨 마지막에 그 함수를 호출하는 것입니다.

#!/usr/bin/env ruby

def main
  # Express the top level logic here...
end

# ... put support code here, organized as you see fit ...

main # ... and start execution here.

또한, 루비는 복잡한 프로그램을 읽기 쉽고 재사용하기 쉬우며 논리적으로 연관성이 있는 덩어리로 나눠 사용할 수 있도록 돕는 도구를 제공합니다. 우리는 모듈 억세스를 위한 include를 이미 살펴보았습니다. 여러분은 또한 loadrequire가 유용하다는 것을 알게 될 겁니다. load는 마치 파일의 내용을 복사해서 넣은 것처럼 동작합니다(C의 #include 전처리 명령과 유사합니다). require는 조금 더 복잡해서, 코드가 최대 한번, 필요할 경우에만 읽히도록 제한 합니다.

이제 끝입니다...

이 가이드는 여러분이 루비로 프로그램을 작성하는데 필요한 최소한입니다. 더 깊은 의문이 떠오를 때면 사용자 커뮤니티나 온라인 리소스, 출판된 문서에서 정보를 얻으실 수 있습니다.

행운을 빕니다. 재미있게 코딩하세요~~

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 1

[루비 사용자 가이드] 객체 초기화

루비/레일스 프로그래밍/루비 사용자 가이드 2009.02.06 10:45

예전에 살펴봤던 Fruit 클래스는 두개의 인스턴스 변수가 있습니다. 하나는 과일의 종류를 저장하며, 다른 하나는 과일의 상태를 저장합니다. 이 두개의 특성이 과일에서 빠져서는 안된다는 것(즉 둘 다 Fruit클래스의 인스턴스 객체에서 반드시 초기화 되어야만 한다는 것)을, 우리는 우리 자신만의 inspect 메소드를 작성해 보고 난 후에 알 수 있었습니다. 운좋게도, 루비는 인스턴스 변수가 항상 초기화될 수 있도록 할 수 있는 방법을 제공합니다.

initialize 메소드

루비가 새로운 객체를 생성할 때마다, initialize라는 이름의 메소드를 찾아서 실행하게 됩니다. 따라서, 우리는 간단히 initialize를 사용해 인스턴스 변수에 디폴트 값을 저장하도록 해서, inspect 메소드가 인스턴스 변수의 값을 제대로 볼 수 있도록 해 줄 수 있습니다.

ruby> class Fruit
    |   def initialize
    |     @kind = "apple"
    |     @condition = "ripe"
    |   end
    | end
   nil
ruby> f4 = Fruit.new
   "a ripe apple"

가정을 요구사항으로 바꾸기

디폴트 값이 그리 적절한 의미를 담지 못하는 경우가 있습니다. 예를 들어 과일의 디폴트 종류는 무엇일까요? 각각의 과일이 생성시 자신이 어떤 종류인지를 명시하도록 요구하는게 바람직할 것입니다. 이를 위해 우리는 initialize 메소드에 형식 인자(formal argument)를 추가할 수 있습니다. 여기서는 자세히 살펴보지 않겠지만, 어떤 이유로 인해서, 여러분이 new를 할 때 지정하는 인자들이 실제로는 initialize로 전달됩니다.

ruby> class Fruit
    |   def initialize( k )
    |     @kind = k
    |     @condition = "ripe"
    |   end
    | end
   nil
ruby> f5 = Fruit.new "mango"
   "a ripe mango"
ruby> f6 = Fruit.new
ERR: (eval):1:in `initialize': wrong # of arguments(0 for 1)

유연성있는 초기화

위에서 initialize 메소드에 한 번에 하나의 인자만 연관된 경우를 살펴보았습니다. 이 인자를 빼먹게 되면 에러가 발생합니다. 좀 더 사용자에게 사려깊게 코딩하려면, 인자가 주어진 경우에는 그 값을 사용하고, 그렇지 않은 경우에는 디폴트 값을 사용할 수 있습니다.

ruby> class Fruit
    |   def initialize( k="apple" )
    |     @kind = k
    |     @condition = "ripe"
    |   end
    | end
   nil
ruby> f5 = Fruit.new "mango"
   "a ripe mango"
ruby> f6 = Fruit.new
   "a ripe apple"

initialize 뿐 아니라, 아무 메소드에서나 디폴트 인자값을 지정할 수 있습니다. 이때 디폴트 값이 지정되는 인자가 인자 목록의 맨 끝 쪽으로 오도록 배치해야만 합니다.

때때로 객체를 초기화 하는 여러 방법을 제공하는 것이 유용할 때가 있습니다. 이 가이드에서 설명할 범위는 아니지만, 루비는 객체 리플렉션(reflection,역주:동적으로 객체 구조를 다루고, 객체/클래스를 생성하며, 메소드를 호출할 수 있도록 하는 기능)을 지원하고, 가변 길이 인자 목록을 지원합니다. 이 두 가지를 함께 활용하면 메소드 오버로딩의 효과를 낼 수 있습니다.

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 0

[루비 사용자 가이드] 접근자(Accessors)

루비/레일스 프로그래밍/루비 사용자 가이드 2009.02.06 10:44

접근자(Accessor)란 무엇인가?

예전에 인스턴스 변수에 대해 간략히 살펴보았지만, 자세히 다루지는 않았습니다. 객체의 인스턴스 변수는 객체의 어트리뷰트입니다. 즉, 인스턴스 변수는 어떤 객체를 동일한 클래스에 속하는 다른 객체와 구분하게 하는 것 중 하나입니다. 이러한 어트리뷰트를 읽고 쓸 수 있는 것은 매우 중요합니다. 그러한 작업(어트리뷰트 읽고 쓰기)에는
어트리뷰트 접근자(attribute accessors)라는 메소드가 필요합니다. 잠시 후에 반드시 접근자 메소드를 사용자가 작성해야 하는 것은 아님을 보게 되겠지만, 지금은 모든 작업을 직접 해 봅시다. 두 가지 종류의 접근자는 쓰기접근자(writers)읽기접근자(eaders)입니다.

ruby> class Fruit
    |   def set_kind(k)  # a writer
    |     @kind = k
    |   end
    |   def get_kind     # a reader
    |     @kind
    |   end
    | end
   nil
ruby> f1 = Fruit.new
   #<Fruit:0xfd7e7c8c>
ruby> f1.set_kind("peach")  # use the writer
   "peach"
ruby> f1.get_kind           # use the reader
   "peach"
ruby> f1                    # inspect the object
   #<Fruit:0xfd7e7c8c @kind="peach">

아주 단순하지요! 우리가 보고 있는 과일의 종류가 어떤 것인지 정보를 저장하고, 읽어올 수 있습니다. 하지만, 여기서 사용한 메소드 이름은 너무 복잡합니다. 아래와 같이 쓰는 것이 더 간단하면서 더 일반적일 겁니다.

ruby> class Fruit
    |   def kind=(k)
    |     @kind = k
    |   end
    |   def kind
    |     @kind
    |   end
    | end
   nil
ruby> f2 = Fruit.new
   #<Fruit:0xfd7e7c8c>
ruby> f2.kind = "banana"
   "banana"
ruby> f2.kind
   "banana"

inspect 메소드

잠깐 딴 이야기를 해 봅시다. 객체를 직접 들여다 볼 때, #<anObject:0x83678>처럼 암호같은 결과를 보게 됩니다. 이것은 단지 기본값으로 설정되어 있는 기능입니다. 여러분은 이 기능을 마음대로 바꿀 수 있습니다. 필요한 것은 inspect라 불리는 메소드를 정의하는 것 하나 뿐입니다. 이 메소드는 객체의 인스턴스 변수 전체(혹은 일부)의 상태를 포함하는 객체를 잘 설명할 수 있는 스트링을 반환해야 합니다.

ruby> class Fruit
    |   def inspect
    |     "a fruit of the #{@kind} variety"
    |   end
    | end
   nil
ruby> f2
   "a fruit of the banana variety"

앞의 메소드와 관련된 또 하나의 메소드는 to_s (스트링으로 변환하기)입니다. to_s는 객체를 출력할 때 쓰입니다. 일반적으로 inspect는 프로그램 작성과 디버깅을 위한 도구이고, to_s는 프로그램의 출력을 위한 방법입니다. eval.rbinspect를 결과 값을 표시하기 위해 사용합니다. p 메소드를 사용하면, 프로그램에서도 디버깅 출력을 얻을 수 있습니다.

# These two lines are equivalent:
p anObject
puts anObject.inspect

접근자를 만드는 쉬운 방법

많은 인스턴스 변수에 대해 접근자 메소드가 필요하기 때문에, 루비는 표준적인 형태로 접근자를 작성하는 쉬운 방법을 제공합니다.

Shortcut Effect
attr_reader :v def v; @v; end
attr_writer :v def v=(value); @v=value; end
attr_accessor :v attr_reader :v; attr_writer :v
attr_accessor :v, :w attr_accessor :v; attr_accessor :w

위 기능을 사용해 과일의 신선도를 추가해 봅시다. 우선, 자동으로 읽기접근자와 쓰기접근자를 작성하도록 할 수 있습니다. 이제 새로 추가된 정보를 inspect에도 넣어줍시다.

ruby> class Fruit
    |   attr_accessor :condition
    |   def inspect
    |     "a #{@condition} #{@kind}"
    |   end
    | end
   nil
ruby> f2.condition = "ripe"
   "ripe"
ruby> f2
   "a ripe banana"

과일로 장난하기

아무도 과일을 먹지 않으면, 우리가 만든 과일이 시간이 지남에 따른 변화를 가지도록 할 수 있습니다.

ruby> class Fruit
    |   def time_passes
    |     @condition = "rotting"
    |   end
    | end
   nil
ruby> f2
   "a ripe banana"
ruby> f2.time_passes
   "rotting"
ruby> f2
   "a rotting banana"

하지만, 이렇게 클래스를 변경하다 보니, 작은 문제가 발생하게 되었습니다. 만약 우리가 새로 과일을 만들게 되면 어떤 일이 벌어질까요? 인스턴스 변수는 값이 대입되기 전까지 존재하지 않는 다는 것을 기억에서 떠올려보십시오.

ruby> f3 = Fruit.new
ERR: failed to convert nil into String

여기서 오류가 발생한 부분은 inspect 메소드입니다. 이 오류는 정당한 것입니다. 우리는 어떤 과일의 종류와 상태를 알려달라고 요청을 했습니다. 하지만, 아직 f3 객체는 어떤 어트리뷰트에도 값을 대입하지 않은 상태입니다.
원한다면 inspect 메소드에서 defined? 메소드를 사용해서 인스턴스 변수가 정의되어 있는지를 확인한 후, 정의된 인스턴스 변수만 보고하도록 할 수도 있을 겁니다. 하지만 그런식으로 처리하는 것은 그리 유용하지 않을 겁니다. 모든 과일은 종류와 상태를 어트리뷰트로 가지고 있기 때문에, 우리는 항상 그 두 어트리뷰트가 정의되어 있다는 것을 확실히 할 수 있어야 할 것입니다. 그게 바로 다음에 다룰 내용입니다.

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 0

[루비 사용자 가이드] 예외 처리: ensure

루비/레일스 프로그래밍/루비 사용자 가이드 2009.02.06 10:42

메소드가 작업을 끝내고 나서 정리 작업을 실행해야 할 경우가 있습니다. 열려있는 파일을 닫거나, 버퍼에 남아있는 데이터를 플러시하는 일과 같은 것들입니다. 만약 모든 메소드마다 메소드에서 나가는 곳이 한군데 씩이라면, 이런 정리 코드를 그 부분에 넣고, 정리코드가 실행될 것을 확신할 수 있을 겁니다. 하지만, 메소드에서 반환될 수 있는 장소는 여러 곳일 수 있습니다. 게다가, 예외가 발생하게 되면 우리가 의도했던 정리 작업이 실행되지 못하고 메소드가 종료될 수도 있습니다.

begin
  file = open("/tmp/some_file", "w")
  # ... write to the file ...
  file.close
end

위의 예에서 파일을 쓰는 부분에서 예외가 발생한다면, 파일이 열린채로 메소드가 종료될 것입니다. 또한, 우리는 다음과 같이 코드를 중복해서 정리작업을 처리하고 싶지도 않습니다.

begin
  file = open("/tmp/some_file", "w")
  # ... write to the file ...
  file.close
rescue
  file.close
  fail # raise an exception
end

위와 같이 하는 것은 보기 좋지 않을 뿐 아니라, 코드가 복잡해질 경우 제대로 다룰 수 없습니다. 왜냐하면, 모든 returnbreak에 대해 정리 작업 코드를 넣어야 하기 때문입니다.

이런 이유로, 예외 처리를 위한 "begin...rescue...end" 방식에 또 하나의 키워드를 추가했습니다. 그것은 ensure입니다. ensure 코드는 begin 블럭이 정상적으로 처리가 완료되건 되지 않았건 간에 수행됩니다.

begin
  file = open("/tmp/some_file", "w")
  # ... write to the file ...
rescue
  # ... handle the exceptions ...
ensure
  file.close   # ... and this always happens.
end

ensurerescue없이 사용할 수도 있고, 거꾸로도 가능합니다. 하지만, 한 begin...end 블럭에서 두 가지 모두 사용하는 경우에는 rescue가 먼저 오고 ensure가 나와야 합니다.

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 0

[루비 사용자 가이드] 예외 처리: rescue

루비/레일스 프로그래밍/루비 사용자 가이드 2009.02.06 10:41

프로그램을 실행하다 보면 예기치 못한 문제에 부딪칠 경우가 있습니다. 읽고자 하는 파일이 존재하지 않거나, 데이터를 저장하려 했는데 디스크가 꽉 차 있을 수도 있습니다. 또, 사용자가 잘못된 입력을 프로그램에 넣을 수도 있습니다.

ruby> file = open("some_file")
ERR: (eval):1:in `open': No such file or directory - some_file

튼튼한 프로그램은 이러한 상황을 민감하게 처리하면서 부드럽게 처리할 수 있어야 할 것입니다. 이런 요구 사항을 만족시키는 것은 고생스럽고 귀찮은 일일 수 있습니다. C프로그래머는 실패할 수 있는 모든 시스템 호출의 반환값을 체크해서 어떤 처리를 할지 결정해야만 합니다.

FILE *file = fopen("some_file", "r");
if (file == NULL) {
  fprintf( stderr, "File doesn't exist.\n" );
  exit(1);
}
bytes_read = fread( buf, 1, bytes_desired, file );
if (bytes_read != bytes_desired ) {
  /* do more error handling here ... */
}
...

이러한 작업은 성가시기 때문에, 프로그래머들은 점점 이런 처리를 무시하거나 부주의하게 되기 쉽습니다. 그 결과 프로그램은 예외상황을 제대로 처리하지 못하게 됩니다. 한편, 이런 예외에 대한 처리를 철저히 제대로 하게 되면 예외사항을 처리하는 코드가 너무 많이 여기저기 있어서, 프로그램을 읽기가 어려워집니다.

많은 현대 언어와 마찬가지로, 루비에서는 코드 덩어리에 대한 예외사항을 프로그램 본래 흐름과 별개의 부분으로 구성된 코드로 처리할 수 있습니다. 따라서 오류를 다루는 것이 효과적으로 이루어지면서도, 프로그램을 작성하는 사람이나 나중에 작성된 코드를 읽는 사람에게 과도한 짐을 지우지 않게 됩니다. begin으로 시작하는 코드 덩어리는 예외사항이 발생할 때 까지 실행되며, 예외가 발생하면 컨트롤이 에러 처리 코드쪽으로 넘어가게 됩니다. 이 에러 처리 코드는 rescue로 표시됩니다. 만약 아무 예외사항도 발생하지 않는다면 rescue 코드는 사용(실행)되지 않습니다. 다음 메소드는 텍스트파일에서 첫번째 줄을 읽어서 반환하며, 예외가 발생하면 nil을 반환합니다.

def first_line( filename )
  begin
    file = open("some_file")
    info = file.gets
    file.close
    info  # Last thing evaluated is the return value
  rescue
    nil   # Can't read the file? then don't return a string
  end
end

때로 문제가 발생하면 창의적으로 작업을 진행해야 할 경우가 있습니다. 아래 예에서는 대상 파일이 없는 경우에 표준 입력을 대신 사용합니다.

begin
  file = open("some_file")
rescue
  file = STDIN
end

begin
  # ... process the input ...
rescue
  # ... and deal with any other exceptions here.
end

retryrescue 코드 내부에서 begin 코드를 다시 시작하고 싶을 경우 사용합니다. retry를 사용해서 위 코드는 다음과 같이 더 단순하게 표현할 수 있습니다.

fname = "some_file"
begin
  file = open(fname)
  # ... process the input ...
rescue
  fname = "STDIN"
  retry
end

하지만, 위 코드에는 한가지 단점이 있습니다. 잘못해서 fname에 존재하지 않는 이름을 지정한다면 위 코드는 무한루프를 돌게 됩니다. 예외 처리시 retry를 사용할 때는 이런 함정에 빠지지 않도록 주의를 기울여야 합니다.

모든 루비 라이브러리는 오류가 발생시 예외를 발생시킵니다. 여러분도 자신의 코드에서 예외를 명시적으로 발생시킬 수 있습니다. 예외를 발생시키기 위해서는 raise를 사용합니다.  raise는 인자로 예외를 설명하는 스트링을 하나 받도록 되어 있습니다. 이 인자는 생략할 수 있긴 하지만, 생략해서는 안될 것입니다. 특수 전역 변수인 $!를 사용하면 맨 마지막에 raise에 전달된 인자값을 알 수 있습니다.

ruby> raise "test error"
   test error
ruby> begin
    |   raise "test2"
    | rescue
    |   puts "An error occurred: #{$!}"
    | end
An error occurred: test2
   nil
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 1 : Comment 0

[루비 사용자 가이드] 클래스 상수

루비/레일스 프로그래밍/루비 사용자 가이드 2009.02.06 10:40

상수는 대문자로 이름이 시작합니다. 상수에는 최대 한 번만 값을 대입해야만 합니다. 현재의 루비 구현에서 상수에 두번 이상 대입하면 에러가 발생하지 않고 경고 메시지가 표시됩니다.

ruby>fluid=30
   30
ruby>fluid=31
   31
ruby>Solid=32
   32
ruby>Solid=33
   (eval):1: warning: already initialized constant Solid
   33

상수는 클래스 내부에서 정의될 수도 있습니다. 이 경우 인스턴스 변수와 달리 클래스 밖에서도 억세스 할 수 있습니다.

ruby> class ConstClass
    |   C1=101
    |   C2=102
    |   C3=103
    |   def show
    |     puts "#{C1} #{C2} #{C3}"
    |   end
    | end
   nil
ruby> C1
ERR: (eval):1: uninitialized constant C1
ruby> ConstClass::C1
   101
ruby> ConstClass.new.show
101 102 103
   nil

상수는 모듈 안에서 정의될 수도 있습니다.

ruby> module ConstModule
    |   C1=101
    |   C2=102
    |   C3=103
    |   def showConstants
    |     puts "#{C1} #{C2} #{C3}"
    |   end
    | end
   nil
ruby> C1
ERR: (eval):1: uninitialized constant C1
ruby> include ConstModule
   Object
ruby> C1
   101
ruby> showConstants
101 102 103
   nil
ruby> C1=99  # not really a good idea
   99
ruby> C1
   99
ruby> ConstModule::C1
   101
ruby> ConstModule::C1=99   # .. this was not allowed in earlier versions
   (eval):1: warning: already initialized constant C1
   99
ruby> ConstModule::C1  # "enough rope to shoot yourself in the foot"
   99
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackbacks 10 : Comment 0

[루비 사용자 가이드] 지역 변수

루비/레일스 프로그래밍/루비 사용자 가이드 2009.02.05 23:41

지역 변수는 이름이 소문자나 밑줄(_)로 시작하는 변수입니다. 지역변수는 전역변수나 인스턴스 변수와는 다르게 초기화 하기 전에 nil값을 가지고 있지 않습니다(역주:아래처럼 '미정의된 변수 또는 메소드'라는 에러가 납니다. 더 정확하게는 NameError라는 예외가 발생합니다.)

ruby> $foo
   nil
ruby> @foo
   nil
ruby> foo
ERR: (eval):1: undefined local variable or method `foo' for main(Object)

지역 변수에 가장 처음 대입을 하면, 선언과 같은 작용을 합니다. 만약 여러분이 초기화 되지 않은 지역변수를 사용한다면, 루비 인터프리터가 그 이름이 잘못된 변수를 가리키는 지 결정할 수 없습니다. 예를 들어, 그 이름은 변수명이 아니라 메소드 이름을 잘못 입력한 것일 수 있습니다. 위와 같은 에러 메시지가 나오게 되는 이유는 그때문입니다.

일반적으로 지역변수의 영역은 다음 중 하나입니다.

  • proc{ ... }
  • loop{ ... }
  • def ... end
  • class ... end
  • module ... end
  • 전체(위의 어떤 영역도 적용 가능하지 않은 경우)

다음 예에서 defined?는 어떤 식별자가 정의되어 있는지를 체크하는 연산자입니다. 만약 어떤 식별자가 정의되어 있다면 그 식별자에 대한 설명이 반환되고, 정의되어 있지 않으면 nil이 반환됩니다. 아래에서 보듯 bar의 범위는 loop 루프 안으로 제한됩니다.즉, 루프에서 빠져나오면 bar는 정의되지 않은 변수가 됩니다.

ruby> foo = 44; puts foo; defined?(foo)
44
   "local-variable"
ruby> loop{bar=45; puts bar; break}; defined?(bar)
45
   nil

동일한 영역 안에서 살아있는 프로시저 객체들은 그 영역 내의 지역 변수를 공유합니다. 아래 예에서 지역변수 barmain(탑레벨 객체)과 프로시저 객체 p1p2사이에 공유됩니다.

ruby> bar=nil
   nil
ruby> p1 = proc{|n| bar=n}
   #<Proc:0x8deb0>
ruby> p2 = proc{bar}
   #<Proc:0x8dce8>
ruby> p1.call(5)
   5
ruby> bar
   5
ruby> p2.call
   5

맨 앞의 "bar=nil"을 생략해서는 안됩니다. 이 문장이 있어야 변수 bar의 범위가 p1p2를 둘러싸고 있있게 됩니다. 만약 그 문장이 없다면 p1p2는 각각 각자의 지역변수 bar를 가지고 있는 것이 되고, p2를 호출하는 것은 "미정의된 지역변수 또는 메소드" 오류를 발생시킵니다. 물론 bar=0로 초기화할 수도 있습니다만, nil을 사용하는 것은 이 코드를 읽는 다른 사람들의 이해를 돕기 위해서입니다. nil로 초기화 하는 것은 변수에 의미 없는 값을 대임함으로써, 이 대입문이 변수의 범위를 정하기 위한 것임을 표시합니다.

프로시저 객체가 메소드 인자로 전달될 수 있는 것과 더불어 강력하고 유용한 특징이 있는데, 그것은 공유된 지역변수가 그 변수의 원래 영역의 바깥으로 가더라도 유효하게 된다는 것입니다(역주:이런 경우를 클로저(closure)라 합니다.).

ruby> def box
    |   contents = nil
    |   get = proc{contents}
    |   set = proc{|n| contents = n}
    |   return get, set
    | end
   nil
ruby> reader, writer = box
   [#<Proc:0x40170fc0>, #<Proc:0x40170fac>]
ruby> reader.call
   nil
ruby> writer.call(2)
   2
ruby> reader.call
   2

루비는 변수의 영역을 잘 처리합니다. 위 예에서 contents변수는 readerwriter 사이에 공유됩니다. 하지만 또한 위에 정의된 box를 사용해서 여러개의 reader와 writer를 얻어올 수 있습니다. 각각의 reader, writer쌍은 하나의 contents 변수를 공유합니다. 하지만, 각각의 reader, writer쌍이 공유하는 contents 변수들은 모두 별개입니다.

ruby> reader_1, writer_1 = box
   [#<Proc:0x40172820>, #<Proc:0x4017280c>]
ruby> reader_2, writer_2 = box
   [#<Proc:0x40172668>, #<Proc:0x40172654>]
ruby> writer_1.call(99)
   99
ruby> reader_1.call
   99
ruby> reader_2.call  # nothing is in this box yet
   nil

이런 방식의 프로그래밍은 약간 특이한 작은 객체지향 프레임워크로 생각할 수도 있습니다. box메소드는 클래스와 유사한 작용을 합니다. getset은 메소드처럼 동작합니다(메소드와 다른 점은 각각의 box 인스턴스마다 이름이 달라질 수 있다는 것입니다). 그리고 contents는 유일한 인스턴스 변수라 할 수 있습니다. 물론 루비가 공식적으로 제공하는 클래스 프레임워크를 사용하면 훨씬 더 읽기 쉬운 코드를 만들 수 있습니다.

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 0 : Comment 0

[루비 사용자 가이드] 인스턴스 변수

루비/레일스 프로그래밍/루비 사용자 가이드 2009.02.05 23:05

인스턴스 변수는 @로 시작하는 이름을 가지며, 변수 영역은 self가 가리키는 객체 내부로 제한됩니다. 두 개의 다른 객체가, 같은 클래스에 속한다고 할지라도, 인스턴스 변수에 다른 값을 가질 수 있습니다. 객체 밖에서는 프로그래머가 명시적으로 억세스를 위한 메소드를 지정하지 않는 한, 인스턴스 변수의 값을 변경하는 것은 물론, 값을 관찰하는 것도 불가능합니다(즉, 루비의 인스턴스 변수들은 결코 공용(public)이 아닙니다). 전역변수와 마찬가지로 초기화 하지 않은 인스턴스 변수의 값도 nil입니다.

인스턴스 변수는 선언될 필요가 없습니다. 이로 인해 유연한 객체 구조가 가능합니다. 각각의 인스턴스 변수는 가장 처음 값이 대입될 때 동적으로 객체에 추가됩니다.

ruby> class InstTest
    |   def set_foo(n)
    |     @foo = n
    |   end
    |   def set_bar(n)
    |     @bar = n
    |   end
    | end
   nil
ruby> i = InstTest.new
   #<InstTest:0x83678>
ruby> i.set_foo(2)
   2
ruby> i
   #<InstTest:0x83678 @foo=2>
ruby> i.set_bar(4)
   4
ruby> i
   #<InstTest:0x83678 @foo=2, @bar=4>

위 코드에서 객체 iset_bar 메소드가 호출되기 전까지  @bar의 값을 보여주지 않고 있음에 유의하십시오.

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackback 1 : Comment 0

[루비 사용자 가이드] 전역 변수

루비/레일스 프로그래밍/루비 사용자 가이드 2009.02.05 22:03

전역 변수는 $로 시작하는 변수입니다. 전역 변수는 프로그램 아무데서나 사용될 수 있습니다. 초기화 하지 않은 전역 변수는 nil값을 지닙니다.

ruby> $foo
   nil
ruby> $foo = 5
   5
ruby> $foo
   5

전역 변수는 적절한 곳에만 사용해야 합니다. 전역 변수는 아무데서나 쓰고 읽을 수 있기 때문에 위험합니다. 전역변수를 남용하는 것은 버그를 여기저기 퍼지기 쉽게 합니다. 또한, 전역변수가 많이 사용되었다는 것은 프로그램 디자인이 신중하게 이루어지지 못했음을 나타내는 증거라 할 수 있습니다. 여러분이 전역변수가 필요하다고 생각할 때마다, 나중에 무심코 다른 곳에서 사용되지 못하도록 최대한 자세한 설명이 되는 이름을 짓도록 하십시오(위 코드와 같이 $foo와 같은 이름을 사용하는 것은 좋은 생각이 못됩니다).

전역변수의 한 가지 좋은 점은 변경을 추적할 수 있다는 것입니다. 전역변수의 값이 변경될 때마다 호출되는 프로시저를 설정할 수 있습니다.

ruby> trace_var :$x, proc{puts "$x is now #{$x}"}
   nil
ruby> $x = 5
$x is now 5
   5

전역 변수가 변경시마다 프로시저를 호출하도록 지정된 경우에 액티브 변수(active variable)라 불리기도 합니다. 액티브 변수가 유용한 한 예로 GUI 화면이 최신 내용을 반영하도록 하는 것을 들 수 있습니다.

이름이 달러 기호($)와 그 뒤의 한 문자로 이루어진 특별한 변수들이 있습니다. 예를 들어 $$는 루비 인터프리터의 프로세스 아이디를 저장하고 있으며, 읽기만 가능합니다. 아래 표에 중요한 시스템 변수를 몇 가지 정리해 두었습니다.

$! 마지막 에러 메시지
$@ 에러가 발생한 위치
$_ gets가 마지막으로 읽어들인 스트링
$. 인터프리터가 마지막으로 읽어들인 라인 번호
$& 정규식에 의해 매치된 마지막 스트링
$~ 마지막으로 매치된 정규식 매치(부분식의 배열 형태)
$n 마지막 매치의 n번째 부분 식($~[n]과 동일)
$= 대소문자 구분 플래그
$/ 입력 레코드 구분자(separator)
$\ 출력 레코드 구분자(separator)
$0 루비 스크립트 파일의 이름
$* 커맨드 라인 인자들
$$ 인터프리터의 프로세스 아이디(ID)
$? 마지막으로 실행되었던 자식 프로세스의 종료 상태(exit status)

위에 나열된 변수 중에서 $_$~는 지역 범위를 가집니다. 이름은 전역변수여야 할 것 처럼 되어 있지만, 지역범위를 가질 때 더더욱 유용합니다. 또한, 그 변수들은 역사적 이유로 그런 이름이 붙었습니다(역주:Perl에 있는 특수 변수를 이어받아서 그렇습니다).

신고
크리에이티브 커먼즈 라이선스
Creative Commons License
Trackbacks 2 : Comment 0

티스토리 툴바