연산자
연산자 (Operators)
Ruby에서 + 같은 연산자도 사실은 클래스에 정의된 메서드예요. 다만 리터럴들은 자기 메서드를 더 낮은 단계인 C 언어에서 정의해요. 예를 들어 String 클래스가 그렇죠.
Ruby 객체는 대부분의 연산자에 대해 자기만의 구현을 정의하거나 재정의(오버로드)할 수 있어요.
예를 하나 볼게요.
class Foo < String
def +(str)
self.concat(str).concat("another string")
end
end
foobar = Foo.new("test ")
puts foobar + "baz "
이 코드는 이렇게 출력해요.
test baz another string
어떤 연산자를 사용할 수 있는지는 그 연산자를 구현하는 클래스에 따라 달라져요.
출처: Ruby 공식 문서
연산자 동작 (Operator Behavior)
클래스가 주어진 연산자에 어떻게 반응하는지는 그 클래스 고유의 일이에요. 연산자는 메서드 구현이니까요.
연산자를 사용할 때 동작을 결정하는 건 연산의 왼쪽에 있는 표현식이에요.
'a' * 3 #=> "aaa"
3 * 'a' # TypeError: String can't be coerced into Integer
'a' * 3은 문자열이 곱해지지만, 3 * 'a'는 정수 쪽이 동작을 정하다 보니 문자열을 Integer로 바꿀 수 없다는 TypeError가 나는 거예요.
논리 연산자 (Logical Operators)
논리 연산자는 메서드가 아니라서 재정의하거나 오버로드할 수 없어요. 더 낮은 단계에서 토큰으로 처리되거든요.
단락(short-circuit) 논리 연산자(&&, ||, and, or)는 항상 boolean 값을 돌려주지는 않아요. 블록과 비슷하게, 마지막으로 평가된 표현식이 연산의 결과가 돼요.
&&, and
&&/and는 양쪽 피연산자를 왼쪽에서 오른쪽으로 실행하다가, 처음으로 falsey 표현식을 만나면 거기서 멈추는 단락 평가를 해요. 결과를 결정하는 표현식은 마지막으로 실행된 것, 즉 마지막 표현식이거나 처음 만난 falsey 표현식이에요.
몇 가지 예를 볼게요.
true && 9 && "string" #=> "string"
(1 + 2) && nil && "string" #=> nil
(a = 1) && (b = false) && (c = "string") #=> false
puts a #=> 1
puts b #=> false
puts c #=> nil
마지막 예시에서 c는 초기화는 됐지만 false를 만나면서 정의되지는 않았어요. 그래서 puts c가 nil을 출력하죠.
||, or
||/or가 단락 평가를 하는 방식은, truthy인 첫 번째 표현식의 결과를 돌려주는 거예요.
(1 + 2) || true || "string" #=> 3
false || nil || "string" #=> "string"
첫 번째는 3이 truthy라서 바로 3이 되고, 두 번째는 false와 nil을 지나 첫 truthy인 "string"이 결과가 돼요.
더 알아보기 (Learn more)
- 연산자의 실행 순서가 궁금하다면 우선순위 (Precedence) 문서를 함께 보세요.
- assignment 문서에서 대입·복합 대입 연산자의 동작을 더 살펴볼 수 있어요.