Ruby

Programming Ruby (14) 표현식 - 1

weicome 2016. 10. 31. 23:37
[출처] Programming Ruby 

(본 게시물은 저작권의 문제 발생시 출판사의 요청에 의해 삭제될 수 있습니다.)





표현식


루비가 다른 언어들과 다른 특징 중 하나는 값을 반환할 수 있어 보이는 모든 곳에서 값을 반환한다는 것이다. 따라서 거의 모든 것이 표현식이 된다. 이것이 실제로 의미하는 바는 무엇일까?


이러한 특징을 통해 구문을 연속해서 쓸 수 있다는(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



끄읕.