카테고리 : Ruby
2006/05/03   메쏘드와 관련된 몇가지 문법 정리
2006/04/30   Range에 반응하는 Class [3]
메쏘드와 관련된 몇가지 문법 정리
오늘은 메쏘드와 관련된 몇가지 기본적인 문법들을 정리해보려고 한다.

먼저 매쏘드의 정의! 매쏘드는 당연히 def 메쏘드 이름 ~ end 로 이루어진다. 그런데 매쏘드 이름이 대문자로 시작하면 어떻게 될까? 다음의 예제를 보자
def test
    puts
"test method"
end

test
()
test

def
Test
    puts
"test method"
end

Test
()
Test

결과를 관찰하면 알 수 있겠지만 "test method"라는 문자열이 4번 출력되지 않는다. 단지 3번 출력되고 마지막에 경고가 나온다. 경고 메시지는 다음과 같다
test.rb:13: uninitialized constant Test (NameError)

위의 예제의 13번째 줄은 마지막줄이다. 마지막 줄이 무엇이 이상한가? 그렇다. 루비에서는 대문자로 시작하는 것은 constant 로 일단 생각하는 것이다. 위의 test 는 메쏘드로 생각해서 실행시킨것과 대조된다. 처음 루비를 접하면 뒤에 괄호를 하지 않아도 된다는 생각에 나중에는 대문자로 정한 메쏘드까지도 그렇게 할 수 있지만 그렇지가 않다. 루비는 기본적으로 메쏘드의 이름은 소문자로 시작한다고 생각한다. 그러므로 명시적으로 뒤에 괄호를 붙여주지 않는다면 대문자로 시작하는 메쏘드는 호출되지 않는다.

두번째는 메쏘드가 가변인자 받기. 프로그래밍 랭귀지의 대표적인 C언어는 가변인자를 받는 것을 허용한다. 자바와 같은 경우도 버전이 1.4에서 5로 넘어가면서 가변인자를 받을 수 있게 되어 있다. 심지어 자바스크립트도 가변인자를 받을 수 있다. 루비는? 물론 루비도 받을 수 있다. 다음처럼...
def test(*ab)
    puts ab.join(
", "
)
end

test "A","B","C"

결과는 "A, B, C"이다. 루비에서는 이처럼 메쏘드의 인자의 이름 앞에 "*"를 붙여 주어 가변인자를 받도록 하고 있다.

세번째는 메쏘드가 블럭을 호출하기. 메쏘드는 메쏘드를 호출하면서 뒤이어 나온 블럭을 호출할 수 있다. 그렇게 하는 방법은 두가지이다. 다음이 그 예이다
def test1(&block)
    block.
call
end

def
test2
   
yield
end

test1
do
    puts
"block"
end

test2
do
    puts
"block"
end

첫번째는 인자로 받아서 호출하는 것이다. 두번째는 "yield"로 호출하는 것이다. 그런데 블럭을 메쏘드 뒤에 주었는지, 주지 않았는지 어떻게 알 수 있을까? 첫번째의 예제에서는 block라는 인자의 값이 nil인지 확인해보면 된다. 그리고 두번째의 경우에는 "block_given?"이라는 연산을 통해서 알 수 있다. 이 값은 주면 true, 주지 않으면 false를 반환한다.

그러면 블럭에 인자를 주고 블럭이 인자를 받는것은 어떻게 할 수 있는가? 이것역시도 기본적인 일이다. 배열 클래스의 each라는 메쏘드를 어떻게 사용하였는지 기억한다면 쉽게 알 수 있다. 다음이 간단한 예이다.
def test
    yield(
"test func"
)
end

test do |message|
    puts
message
end

보는 것처럼 인자를 주는것은 메쏘드를 호출하는 것과 같다. 인자로 받아서 .call을 통해서 호출하는 것도 마찬가지이다. 블럭이 인자를 받기 위해서는 "|"를 사용하며 받는 인자는 ","로 구분하여 여러개도 가능하다. 또한 가변인자를 받는 것도 메쏘드가 하는 것처럼 가능하다

더 나아가 블럭도 하나의 메쏘드처럼 리턴값을 줄 수 있다. 그리고 그것을 호출한 메쏘드는 다른 메쏘드를 호출하여 리턴값을 받듯이 받을 수 있다. 범위 클래스의 "collect"라는 메쏘드는 그 리턴값을 사용하는 대표적인 예이다. 간단히 언급하자면 "collect"라는 메쏘드는 다음과 같이 사용하며 배열을 리턴한다
(1..10).collect { |n| n*2 }


네번째는 메쏘드 호출시 배열의 사용.. 메쏘드를 호출할 때에는 그 메쏘드가 원하는 인자를 넘겨 주어야 한다. 그런데 그 인자를 넘겨줄 때, 인자가 여러개인 경우 배열 하나로 인자들을 넘겨주는 것이 가능하다. 다음이 그 예이다.
def test(a,b)
    puts
a
    puts
b
end

test
"A",
"B"

a = ["A", "B"
]
test *a

위에서 test라는 메쏘드를 호출하는데, 처음에는 그대로 인자를 주어서, 두번째는 배열로 주는 것이다. 배열로 인자를 줄 때에는 배열 이름 앞에 "*"를 명시해주어야 한다. 그리고 배열의 길이가 필요한 인자보다 많은 경우에는 in `test': wrong number of arguments (3 for 2) (ArgumentError) 라는 에러를 발생시킨다. 하지만 예상할 수 있듯이 가변인자를 받는 경우라면 에러를 발생시키지 않고 모조리 가변인자에 넘겨준다.

이제 몇가지 문법만 더 정리하면 "나를 위한 루비"를 시작할 수 있을 것 같다. "Ruby"카테고리에는 rubyist에 게시할, 루비와 관련해서 다른사람들에게도 도움이 될만한 내용들을 정리했다. 하지만 "나를 위한 루비"에는 개인적으로 루비를 즐긴 이야기들을 적을 생각이다.
by 이한길 | 2006/05/03 21:59 | Ruby | 트랙백
Range에 반응하는 Class
Programming Ruby 의 Standard Type에 보면 type의 하나로 Range가 나온다. 이것은 일종의 범위를 가르키는 형이다. 이를테면 1...10 이라고 하면 1부터 10까지의 범위가 만들어진다. 그리고 이 범위를 가지고 배열로 변환할 수도 있고 그대로 루프를 돌릴 수도 있으며 무슨 값이 그 범위에 포함되어 있는지도 체크할 수 있다.

아무튼 이렇게 유용하게 사용할 수 있는 범위라는 것이 있는데 내가 만든 클래스도 그 범위라는 것에 반응할 수 있을까? 물론 가능하다. 다음과 같은 간단한 예를 보면 쉽게 알 수 있다.
class Test
    attr :
a
    def initialize
a
        @a=
a
   
end
   
   
def
<=>(other)
        self.a <=> other.
a
   
end

    def
succ
        Test.new(@a.succ)
   
end

    def
to_s
        a.
to_s
   
end
end

t1 = Test.new
10
t2 = Test.new
20

t = t1 ..
t2
puts
t.to_a

위 예를 보면 <=>라는 것이 나온다. 이것이 바로 범위를 위한 '..' 혹은 '...'에 반응하는 메쏘드이다. 이 메쏘드를 통해서 범위를 만들어줄 수 있는데 위에서는 단지 숫자형을 그대로 늘려본 것이다.

참고로 '...'과 '..'의 차이는 범위를 만드는 것에서 1..10 은 1에서 10까지의 범위가 만들어지고 1...10은 1에서 9까지의 범위가 만들어진다.

위의 예를 잠깐 생각해보면 클래스의 "succ"라는 메쏘드가 그 역할을 하고 있음을 알게된다. 일반적으로 succ는 숫자형이나 문자형이나 하나 증가된 값을 반환해준다. 그러므로 그 메쏘드를 조작하면 숫자가 순서대로 늘어나지 않고 하나씩 건너띄게 하거나 그밖의 형태를 갖게 하는 것도 가능하다. 위 예를 간단히 고쳐보면 아래와 같다.

class Test
    attr :
a
    def initialize
a
        @a=
a
   
end
   
   
def
<=>(other)
        self.a<=>other.
a
   
end

    def
succ
        Test.new((@a+2).succ
)
   
end

    def
to_s
        a.
to_s
   
end
end

t1 = Test.new 10
t2 = Test.new
20

t = t1 ..
t2
puts
t.to_a
당연하겠지만 위 예제는 앞에 보인 예제와 결과가 다르다. 하지만 한가지 참고할 사항은 Range의 include?메쏘드이다. 이 메쏘드로 확인해보려고 t.include?(Test.new(11)) 을 실행하거나 t.include?(Test.new(20)) 을 실행하거나 전자의 예제는 갖고 있으므로 true를 반환한다고 생각할지 모르지만 후자의 예제도 마찬가지로 true를 반환한다. 즉 include?메쏘드는 실제 범위가 가지고 있는 값에서 확인하는 것이 아니라 단순히 값의 크기 비교로 작동한다.

이 예는 크게 쓸모있어 보이지는 않는다. 하지만 언어가 여기까지 지원이 된다는 것은 괜히 배움을 즐겁게 만든다.
by 이한길 | 2006/04/30 23:45 | Ruby | 트랙백 | 덧글(3)


< 이전페이지 다음페이지 >