2011년 6월 2일 목요일

Expressions

Expressions

Operator Expressions
루비의 많은 연산자가 실제로는 메서드 호출로 구현 되어 있다. a*b+c는 (a.*(b)).+(c)와 같다. 루비에서는 모든 것이 객체이고 인스턴스 메서드를 재정의하는 것도 가능하기 때문에 기본 연산을 재정의할 수도 있다.
class Fixnum
 alias oldPlus +
 def +(other)
   oldPlus(other).succ
 end
end
1 + 2»4
a = 3
a += 4»8


Miscellaneous Expressions

Command Expansion

문자열을 역따옴표로 싸거나, %x로 시작하는 구분자 형식을 사용하면, 이것은 사용중인 기반 운영체제에 의해 커멘드로 실행된다. 표현식의 결과값은 해당 커맨드의 표준 출력 문자열이 된다.
`date`»"Sun Jun  9 00:08:26 CDT 2002\n"
`dir`.split[34]»"lib_singleton.tip"
%x{echo "Hello there"}»"Hello there\n"


커맨드 문자열에서도 표현식 확장과 일반적인 이스케이프 문자열을 모두 사용할 수 있다.

for i in 0..3
   status = `dbmanager status id=#{i}`
   # ...
end

Assignment

대입문은 좌변값에 위치한 변수나 속성이 오른편의 우변값을 참조하게 한다. 그리고 그 값을 대입문의 결과값으로 반환한다.
a = b = 1 + 2 + 3
a»6
b»6
a = (b = 1 + 2) + 3
a»6
b»3
File.open(name = gets.chomp)


루비에서는 두 가지 기본적인 대입문 형식이 있다. 변수나 상수에 객체 참조를 대입하는 것과, 왼편에 객체의 속성이나 요소값 참조를 포함하는 경우이다.

instrument = "piano"
MIDDLE_A = 440

aSong.duration = 234
instrument["ano"] = "ccolo"

Parallel Assignment

루비의 대입문은 효율적인 방법으로 병렬 수행되기 때문에, 할당된 변수들은 대입문 자체에 의해서 영향을 받지 않는다. 오른편의 값들은 왼편에 있는 변수나 속성에 대입이 이루어지기 전에 쓰인 순서대로 모두 계산한다.
x = 0»0
a, b, c   =   x, (x += 1), (x += 1)»[0, 1, 2]


대 입문이 하나 이상의 좌변값을 갖는다면, 대입문은 우변값들의 배열을 결과로 반환한다. 만일 우변값의 수보다 죄변값의 수가 더 많을 경우 초과된 좌변값쪽 변수들은 nil이 된다. 만일 반대일 경우에는 초과된 우변값은 무시된다. 또 대입문에 좌변값이 한 개인데 여러개의 우변값이 주어진다면 이때는 우변값이 배열로 변환되어 좌변값에 대입된다.

루비의 병렬 대입 연산자를 이용하면 배열을 합치거나 확장할 수 있다. 마지막 좌변값이 * 로 시작할 경우, 나머지 우변값들이 모두 합쳐져서 좌변값에 배열로 저장된다. 우변값에 *를 붙이면 이것은 그자리에 직접 하나씩 쓴 것처럼 확장 된다.
a = [1, 2, 3, 4]
b,  c = a»b == 1,c == 2
b, *c = a»b == 1,c == [2, 3, 4]
b,  c = 99,  a»b == 99,c == [1, 2, 3, 4]
b, *c = 99,  a»b == 99,c == [[1, 2, 3, 4]]
b,  c = 99, *a»b == 99,c == 1
b, *c = 99, *a»b == 99,c == [1, 2, 3, 4]


Nested Assignments

대입문의 왼쪽에는 괄호로 싸인 표현이 올수도 있다. 루비는 이 표기를 중첩 대입문으로 인식한다. 이는 상위 수준의 대입을 이어가기 전에 먼저, 대응되는 우변값을 추출해서 괄호로 둘러싸인 부분에 대입한다.
b, (c, d), e = 1,2,3,4»b == 1,c == 2,d == nil,e == 3
b, (c, d), e = [1,2,3,4]»b == 1,c == 2,d == nil,e == 3
b, (c, d), e = 1,[2,3],4»b == 1,c == 2,d == 3,e == 4
b, (c, d), e = 1,[2,3,4],5»b == 1,c == 2,d == 3,e == 5
b, (c,*d), e = 1,[2,3,4],5»b == 1,c == 2,d == [3, 4],e == 5


Conditional Execution

Boolean Expressions

루비에서 참의 정의는 nil, 혹은 상수 false 가 아니면 true 이다. 숫자 0이 false로 해석되지 않는다는 점을 주의 해야 한다.

Defined?, And, Or, and Not

"and", "&&"는 양쪽으 피연산자가 모두 참일때만 참이다. 첫 번째 피연산자가 참일 때 두 번째 피연산자를 평가한다. and는 && 보다 우선순위가 낮다.
"or", "||" 둘다 한쪽 연산만 참이면 참이다. 첫 번째 피연산자가 거짓인 경우 두 번째 피연산자를 평가한다. or는 || 보다 우선순위가 낮다.
and, or는 우선순위가 같고, &&는 || 보다 우선순위가 높다.
"not", "!"는 해당 피연산자의 반대값을 반환한다.
"defined?" 연사자는 매개변수가 정의되지 않았을 때 nil을 반환하며, 그렇지 않은 경우 매개변수에 대한 설명을 반환한다. 만일 매개변수가 yield 문인 경우 defined?는 현재의 문맥에 블록이 결합된 경우에 한해서 'yield' 라는 문자열을 반환한다.
defined? 1»"expression"
defined? dummy»nil
defined? printf»"method"
defined? String»"constant"
defined? $&»nil
defined? $_»"global-variable"
defined? Math::PI»"constant"
defined? ( c,d = 1,2 )»"assignment"
defined? 42.abs»"method"


루 비 객체는 ==, ===,<=>,=~, eql?, equal? 등의 메서드를 이용해서 비교연산을 지원한다. <=>를 제외한 모든 메서드는 Object 클래스에 정의되어 있지만, 적절한 의미에 맞추기 위해 하위 클래스에서 재정의하는 일이 빈번하다.

==, =~는 둘 다 부정형이 존재하는데 !=, !~이다. 이것들은 루비가 프로그램을 읽을 때 자동으로 변환된다. a !=b 는 !(a==b), a!~b 는 !(a=~b)로 변환된다.

Common comparison operators
OperatorMeaning
==Test for equal value.
===Used to test equality within a when clause of a case statement.
<=>General comparison operator. Returns -1, 0, or +1, depending on whether its receiver is less than, equal to, or greater than its argument.
<, <=, >=,>Comparison operators for less than, less than or equal, greater than or equal, and greater than.
=~Regular expression pattern match.
eql?True if the receiver and argument have both the same type and equal values. 1 == 1.0 returns true, but 1.eql?(1.0) is false.
equal?True if the receiver and argument have the same object id.


루비에서는 범위 또한 논리식으로 사용할수 있다. exp1..exp2 와 같은 범위는 exp1이 참이 되기 전 까지는 거짓으로 계산되고, exp1이 참이 된 후에는 exp2가 참이 되기 전까지는 참으로 계산된다.

If and Unless Expressions

아래의 모든 If 문은 동일하다.

if aSong.artist == "Gillespie" then
 handle = "Dizzy"
elsif aSong.artist == "Parker" then
 handle = "Bird"
else
 handle = "unknown"
end

if aSong.artist == "Gillespie"
 handle = "Dizzy"
elsif aSong.artist == "Parker"
 handle = "Bird"
else
 handle = "unknown"
end

if aSong.artist == "Gillespie" then  handle = "Dizzy"
elsif aSong.artist == "Parker" then  handle = "Bird"
else  handle = "unknown"
end

handle = if aSong.artist == "Gillespie" then
       "Dizzy"
     elsif aSong.artist == "Parker" then
       "Bird"
     else
       "unknown"
     end

루비에서는 if의 부정형도 존재한다.

unless aSong.duration > 180 then
 cost = 0.25
else
 cost = 0.35
end

삼항 연산자도 지원한다.

cost = aSong.duration > 180 ? 0.35 : 0.25

If and Unless Modifiers

구문 변경자는 조건문을 일반 구문의 끝에 붙여서 사용할 수 있게 해준다. 앞에 오는 표현식은 조건이 참일때만 계산된다. unless는 그 반대로 동작한ㄷ.

mon, day, year = $1, $2, $3 if /(\d\d)-(\d\d)-(\d\d)/
puts "a = #{a}" if fDebug
print total unless total == 0

while gets
 next if /^#/         # Skip comments
 parseLine unless /^$/   # Don't parse empty lines
end

Case Expressions

비 교할 대상을 가장 위의 case 옆에 쓰고, 하나 이상의 when을 늘어놓아서 각각 비교한다. case는 마지막에 수행된 표현식의 결과를 반환한다. 조건과 표현식을 같은 줄에 쓰고 싶다면 then 키워드로 구분한다. if 구문과 마찬가지로 then 자리에 :을 대신 쓸 수 있다.
case는 비교할 대상을 각각의 when 키워드 다음에 오는 비교 표현식들과 비교하는 연산을 수행한다.

case inputLine

 when "debug"
dumpDebugInfo
dumpSymbols

 when /p\s+(\w+)/
dumpVariable($1)

 when "quit", "exit"
exit

 else
print "Illegal command: #{inputLine}"
end

kind = case year
     when 1850..1889 then "Blues"
     when 1890..1909 then "Ragtime"
     when 1910..1929 then "New Orleans Jazz"
     when 1930..1939 then "Swing"
     when 1940..1950 then "Bebop"
     else              "Jazz"
   end

kind = case year
     when 1850..1889 : "Blues"
     when 1890..1909 : "Ragtime"
     when 1910..1929 : "New Orleans Jazz"
     when 1930..1939 : "Swing"
     when 1940..1950 : "Bebop"
     else              "Jazz"
   end

Loops

while 반복문은 그 조건에 따라 안쪽의 구문을 실행하지 않거나, 한 번 이상 반복해서 실행한다.
until 반복문은 조건이 참이 되기 전까지만 실행한다.

while gets
   # ...
end

until playList.duration > 60
    playList.add(songList.pop)
end

구문 변경자로 이용할 수 있다.

a *= 2 while a < 100
a -= 10 until a < 100

while과 until이 구문 변경자로 쓰일 때 begin/end 블록을 구문 변경자로 꾸밀 경우, 논리 표현식의 값에 상관없이 블록의 코드가 무조건 한 번은 실행된다.

print "Hello\n" while false
   begin
    print "Goodbye\n"
end while false

>> Goodbye

Iterators

0부터 9까지의 숫자를 출력하는 반복문

0.upto(9) do |x|
    print x, " "
end

>> 0 1 2 3 4 5 6 7 8 9

0에서 12까지 3씩 증가하는 반복문

0.step(12, 3) {|x| print x, " " }

>> 0 3 6 9 12

배열이나 다른 컨테이너를 반복하는 경우 each 메서드를 사용한다.

[ 1, 1, 2, 3, 5 ].each {|val| print val, " " }

>> 1 1 2 3 5

loop 반복자는 결합된 블록을 계속 반복해서 호출한다.

loop {
   # block ...
}

For...In

루비에서는 for문을 쓰면 내부에서 each 문으로 변환한다.

for aSong in songList
   aSong.play
end

내부 변환
songList.each do |aSong|
   aSong.play
end

for는 Array나 Range 처럼 each 메서드에 응답하는 객체에는 어디든 사용할 수 있다.

for i in ['fee', 'fi', 'fo', 'fum']
 print i, " "
end
for i in 1..3
 print i, " "
end
for i in File.open("ordinal").find_all { |l| l =~ /d$/}
 print i.chomp, " "
end

클래스를 정의할 때 each 메서드를 정의해주기만 하면, 객체를 탐색하기 위해 for문을 사용할수 있다.

class Periods
 def each
yield "Classical"
yield "Jazz"
yield "Rock"
 end
end

periods = Periods.new
for genre in periods
 print genre, " "
end

Break, Redo, Next

반복문 제어를 위한 break, redo, next를 이용하면 반복문이나 반복자의 실행 흐름을 바꿀수 있다.
break는 실행중인 줄을 싸고 있는 반복문을 종료 하고 제어권은 블록 다음에 오는 구문으로 넘긴다.
redo는 반복문을 시작부터 다시 수행하지만 다음 원소를 가져오거나 종료조건을 재평가하지는 않는다.
next는 현재 위치에서 반복문의 끝으로 이동하므로 실제로는 다음 반복을 시작하게 한다.

while gets
 next if /^\s*#/   # skip comments
 break if /^END/   # stop at end
                # substitute stuff in backticks and try again
 redo if gsub!(/`(.*?)`/) { eval($1) }
 # process line ...
end

i=0
loop do
 i += 1
 next if i < 3
 print i
 break if i > 4
end

Retry

retry 구문은 반복문을 맨 처음의 시작점으로 되돌린다. 어떠한 종류의 반복문이건 처음부터 다시 시작한다.

for i in 1..100
 print "Now at #{i}. Restart? "
 retry if gets =~ /^y/i
end

>> Now at 1. Restart? y
>> Now at 1. Restart? n
>> Now at 2. Restart? y
>> Now at 1. Restart? n
>> Now at 2. Restart?

retry는 재시작하기 전에 반복자의 매개변수를 재평가 한다.

def doUntil(cond)
 yield
 retry unless cond
end

i = 0
doUntil(i > 3) {
 print i, " "
 i += 1
}

>> 0 1 2 3 4

Variable Scope and Loops

내장 반복문인 while, until, for는 새로운 변수 범위를 만들지는 않는다. 이전에 존재했던 지역 변수는 반복문 안에서 사용할 수 있고, 반복문 안에서 새로 만들어진 지역 변수는 반복문이 끝나도 계속 사용할 수 있다.
loop, each등에서 사용하며 만든 지역변수는 블록 바깥에서는 접근할 수 없다.

[ 1, 2, 3 ].each do |x|
 y = x + 1
end
puts [ x, y ]

>> Expression.rb:4: undefined local variable or method `x' for main:Object (NameError)

x = nil
y = nil
[ 1, 2, 3 ].each do |x|
 y = x + 1
end
puts [ x, y ]

>> 3
>> 4

블록과 메서드를 최대한 짧게 유지한다. 변수를 적게 쓸수록 그것들이 겹칠만한 경우가 줄어든다.
블록 형식인수와 지역변수의 이름을 지을때 서로 다른 명명 방식을 가지도록 한다.

댓글 없음:

댓글 쓰기

블록체인 개요 및 오픈소스 동향

블록체인(block chain) 블록체인은 공공 거래장부이며 가상 화폐로 거래할때 발생할때 발생할 수 있는 해킹을 막는 기술. 분산 데이터베이스의 한 형태로, 지속적으로 성장하는 데이터 기록 리스트로서 분산 노드의 운영자에 의한 임의 조작이 불가...