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

루비/레일스 프로그래밍/루비 사용자 가이드 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

티스토리 툴바