(본 게시물은 저작권의 문제 발생시 출판사의 요청에 의해 삭제될 수 있습니다.)
표현식
루비가 다른 언어들과 다른 특징 중 하나는 값을 반환할 수 있어 보이는 모든 곳에서 값을 반환한다는 것이다. 따라서 거의 모든 것이 표현식이 된다. 이것이 실제로 의미하는 바는 무엇일까?
이러한 특징을 통해 구문을 연속해서 쓸 수 있다는(chain statements) 점은 쉽게 유추할 수 있다.
a = b = c = 0 # => 0
[3, 1, 7, 0].sort.revers # => [7, 3, 1, 0]
C나 자바에서의 일반적인 구문이 루비에서는 표현식이다. 예를들어 if 와 case 구문ㅇ느 둘 다 마지막에 평가된 표현식의 값을 반환한다.
연산자 표현식
루비는 기본적인 연산자(+, -, *, / 등)와 함께 몇 가지 놀랄 만한 기능을 제공한다. 루비의 많은 연산자는 실제로 메서드 호출로 구현되어 있다. a*b+c라고 입력하면 실제로는 a가 참조하는 객체에게 b를 매개 변수로하여, '*' 메서드를 실행하라고 요청하는 것이다. 그리고 그 계산 결과로 반환된 개게에게 c를 매개 변수로 하여 '+' 메서드를 실행하라고 요청한다.
a, b, c = 1, 2, 3
a * b + c # => 5
(a . *(b)) . +(c) #= > 5
루비에서 모든 것은 객체이고 인스턴스 메서드를 재정의하는 것도 가능하기 때문에 기본적으로 정의된 연산 결과가 마음에 들지 않는다면 기본 연산을 재정의 할 수도 있다.
class Fixnum
alias old_plus + # 기존의 '+' 연산자에 'old_plus'라는 별칭을 붙여준다.
def + (other) # Fixnum 클래스의 + 연산자를 재정의한다. 별로 좋은 생각은 아니다!
old_plus(other).succ
end
end
1 + 2 #=> 4
a = 3
a += 4 #=> 8
a + a + a #= 26
우리가 작성한 클래스를 마치 내장 객체인 것처럼 연산자 포현식의 일부로 쓸 수 있다는 것이다. 왼쪽 시프트 연산자인 <<는 수신자 끝에 요소를 더한다는 의미로 많이 사용된다.
a = [ 1, 2, 3 ]
a << 4 # => [1, 2, 3, 4]
이 연산자를 자신의 정의한 클래서에서 사용할 수 있다.
class ScoreKeeper
def initialize
@total_score = @count = 0
end
def <<(score)
@total_score += score
@count += 1
self
end
def average
fail "No score" if @count?zero?
Float(@total_score) / @count
end
end
score = ScoreKeeper.new
scores << 10 << 20 << 40
puts "Average = #{scores.average}"
Average = 23.33333333333333333332
<< 메서드가 명시적으로 self를 반환하는 이유는 연쇄적으로(<< 10 << 20 << 30) 메서드를 사용하기 위해서이다. +, *, << 와 같이 용도가 명확한 연산자뿐 아니라 대괄호를 사용한 인덱스 구현도 메서드 호출을 통해 구현되어 있다. some_obj[1,2,3]
some_obj 객체에 [ ]이름을 가진 메서드를 호출하면서 세 개의 변수를 넘기는 것과 같다.
class SomeCalss
def [ ](p1, p2, p3)
# ...
end
end
요소 대입은 [ ]= 메서드를 통해 정의된다. 인덱스로 넘겨진 맨 마지막 매개변수를 값으로 하고 나머지 매개 변수들을 인덱스로 사용한다.
class SomeClass
def [ ]= (*params)
value = params.pop
puts "Indexed with #{params.join(', ')}"
puts "value = #{value.inspect}"
end
end
s = SomeClass.new
s[1] = 2
s['cat', 'dog'] = 'enemies'
실행결과
Indexed with 1
value = 2
Indexed with cat, dog
value = "enemies"
기타 표현식
명령어 확장
문자열을 역따옴표로 둘러싸거나 %x{...} 같은 구분자 형식을 사용하면, 기본적으로 사용중인 운영 체제에 의해 커맨드로 실행된다. 결괏값은 해당하는 명령어의 표준 출력이다. 줄바꿈이 잘리지 않고 남아 있어 문자열 값 끝에 리턴이나 줄바꿈이 포함되어 잇을 수 있다.
`date` # => "Tus Nov 14 16:31:02 CST 2013\n"
`ls`.split[34] # => "metaprogramming.pml"
%x{echo "hello there"} # => "hello there\n"
역따옴표 재정의하기
명령어 출력 표현식을 설명할때 역따옴표로 문자열을 둘러싸면 이 문자열이 갖는 의미는 '기본적으로 운영 체제에서 명령어를 실행하라는 의미'라고 설명했다 . 실재로는 문자열을 Object&`(역따옴표) 메서드에 매개 변수로 넘기는 것이다. 원한다면 이를 재정의 할 수 있다.
alis old_backquote `
def ` (cmd)
result = old_backquote(cmd)
if $? != 0
puts "*** Command #{cmd} failed: status = #{$?.exitstatus}"
end
result
end
print `ls -l /etc/passwd`
print `ls -l /etc/wibble`
실행결과
-rw-r--r-- 1 root wheel 5253 Oct 31 09:09 /etc/passwd
ls: /etc/wibble: No such file or directory
*** Command ls -l /etc/wibble failed: status = 1
끄읕.