Swift 사용자 정의연산자(operator overloading)

C++, python을 접해보셨다면 연산자 중복정의라는 개념이 낯선 개념은 아닐 것입니다. 어떤 클래스에 대해 조금 더 간편한 접근 방법이 필요할 때, 연산자 중복정의는 가뭄에 단비와 같은 존재죠.

스위프트에서도 이러한 연산자 중복정의를 제공합니다. 애플 공식 문서에서는 custom operator 즉, 사용자 정의 연산자라고 하는데, 이번에는 이 기능에 대해 소개해보려 합니다.

C++, C#, 자바, 자바스크립트 파이썬을 접해보신 분들이라면 문자열에 대한 + 연산이 가능하다는 것을 익히 알고 계실 것입니다.

C++
string greeting = "Hello" + "World!";
C#, Java
String greeting = "Hello" + "World!";
JavaScript, python
greeting = "Hello" + "World!"
Swift
let greeting = "Hello" + "World!"

그렇다면 사용자 연산자를 어떻게 정의할까요?

우선 ❤︎연산자를 만들어봅시다.
❤︎연산자는 두 개의 수를 받아서 멱승을 반환합니다.

let hundred = 10❤︎2
print(hundred) //100

우선 두 개의 연산자를 갖기 때문에 이항연산자(infix)로 선언할 것입니다.

infix operator ❤︎{}

아, 여기서 끝내시면 안됩니다.
이렇게 선언된 상태에서 2❤︎4❤︎2처럼 사용하면 이것이 좌측결합인지, 우측결합인지를 결정하지 못합니다.

아마 중학교 수학을 배우셨다면 (a+b)+c 와 a+(b+c)를 두고 ‘결합법칙이 성립한다.’라고 배웠을 것입니다. 하지만 수학과 달리 프로그래밍에서는 좌측 결합과 우측 결합 중 하나를 선택해야 합니다.

우리는 좌측결합을 선택하기로 합시다. (좌측 결합 시 (2❤︎4)❤︎2 순서로 연산됩니다.)
그러므로 선언을 조금 수정하도록 하겠습니다.

infix operator ❤︎{associativity left}

사실 여기서도 부족한 점이 있습니다. 바로 연산자의 우선순위입니다.
10❤︎2+1은 어떻게 연산될까요? 101이 될까요, 아니면 1000이 될까요?
애플 공식문서에 의하면 + 연산자의 우선순위는 140입니다. 그러므로, 이와 동일하게 140으로 지정하겠습니다.

infix operator ❤︎{associativity left precedence 140}

자, 이제 연산자 선언은 완료되었습니다.
이제 연산자를 정의할 차례죠.

func ❤︎(left:Int, right:Int)->Int{
var base:Int = 1
for _ in 0..<right{
base *= left
}
return base
}

최종적으로 선언된 형식은 이렇습니다.

이제 print(10❤︎2)를 실행하면 100이라는 결과가 출력됩니다.


이항 연산자는 위와 같이 associativity와 precedence를 지정해야 합니다.
하지만 단항 연산자의 경우는 어떨까요?

단항 연산자는 전위 연산자(prefix)와 후위 연산자(postfix)로 나뉩니다.
전위 연산자든, 후위 연산자든 선언 방식은 동일하니, 하나의 예시만 들겠습니다.

우선, 단항 연산자는 결합의 순서와 우선순위를 선언하지 않습니다.

그렇다면 제곱근을 반환해주는 루트 연산자를 선언,정의해보겠습니다.

prefix operator √{}
prefix func √(square:Double)->Double{
return sort(square)
}

앞서 정의했던 이항연산자와의 차이점이 보입니다. 이항 연산자를 정의할 때 func 앞에 infix 키워드를 붙이지 않았는데, 여기서는 prefix 키워드가 붙어있습니다. 이는 단항 연산자가 연산항의 앞, 뒤에 붙을 수 있으며, 붙는 위치에 따라 그 동작이 다를 수 있기 때문입니다.(비록 전위 √연산자만 선언되어 있지만, 후위 √연산자를 선언할 경우, 동작이 달라야 하기 때문입니다.)

그렇다면 이제 √100을 실행해 볼 차례입니다.

print(√100) //10.0

성공적입니다.


마지막으로 기존에 존재하는 연산자를 중복정의 해볼까요?

기존에 존재하던 연산자는 선언을 할 필요가 없습니다. 정의만 새롭게 해주면 되죠.

prefix func +(intVal:Int)->Int{
return intVal+1
}
prefix func ++(intVal:inout Int)->Int{
intVal += 1
return intVal
}
postfix func ++(intVal:inout Int)->Int{
let retVal = intVal
intVal+=1
return retVal
}

사용자 정의 연산자, 연산자 중복 정의는 우선 이 정도로 마치겠습니다.
중구난방 긴 글 읽어주셔서 감사합니다.