개요
오늘은 함수형 프로그래밍의 마지막 포스팅입니다.
이번 포스팅에서는 코틀린의 Infix 함수 그리고 연산자 오버로딩에 대해 알아보도록 하겠습니다.
Infix 함수
코틀린에서는 아주 독특한 형태의 함수를 하나 제공합니다.
a addNumber b
위 코드는 더하기(+), 빼기(-), 곱하기(*), 나누기(/)와 같은 `연산자(Operator)`도 아닌 거 같은데,
알고리즘 또는 프로그램의 동작을 논리적이고 간단한 언어로 설명하는 의사 코드(pseudocode)일까요?
이 코드의 정체는 코틀린의 Infix 함수였습니다.
Infix 함수란 중위 표기법으로 호출할 수 있는 함수입니다.
이는 함수 이름을 중위 연산자처럼 사용할 수 있도록 지원합니다.
여기서 중위 연산자란 연산자 기호가 피연산자 사이에 위치하는 연산 방식을 의미합니다. ( a + b )
코틀린에서는 이처럼 함수를 연산자처럼 사용할 때 Infix 함수를 활용할 수 있는데요.
이 함수는 지난 포스팅에서 다루었던, 확장 함수와 유사한 방법으로 함수를 설계할 수 있습니다.
아래는 코틀린에서 Infix 함수를 만드는 방법입니다.
infix fun Int.addNumber(x:Int):Int{
return this + x;
}
fun main() {
val value1 = 3;
val value2 = 4;
val result = value1 addNumber value2
println(result); // 7
}
위 코드에서 'addNumber'라는 단어는 코틀린에서 이미 정의된 표준 연산자는 아닙니다.
다만, Infix 함수를 통해 마치 연산자처럼 함수를 사용할 수 있도록 하고 있는데요.
그렇다고 해서 모든 함수를 다 Infix 함수로 만들 수 있는 것은 아닙니다.
코틀린에서 Infix 함수를 선언하기 위해서는 다음과 같은 규칙을 따라야 합니다.
- 함수는 멤버 함수일 필요가 없을 경우
- 하나의 매개변수를 가지는 함수
- 함수 선언 앞에 infix 키워드를 추가 (infix fun ...)
코틀린에서 Infix 함수를 사용하면 연산이나 작업의 의도를 더 명확하게 전달할 수 있습니다.
예를 들어, 수학적인 연산을 할 때나 도메인 특정한 작업을 할 때
인픽스 함수를 사용하면 코드가 더 읽기 쉽고 자연스러워집니다.
아래는 이에 대한 보다 직관적인 예시를 위하여 이차방정식의 근의 공식을 구하는 Infix 함수를 구현해보았습니다.
이차방정식의 근의 공식을 구하는 식은 아래와 같습니다.
import kotlin.math.sqrt
data class QuadraticEquation(val a: Double, val b: Double, val c: Double)
// 인픽스 함수로 이차 방정식의 근을 계산하는 확장 함수
infix fun QuadraticEquation.solve(): Pair<Double, Double> {
val discriminant = sqrt(b * b - 4 * a * c)
val root1 = (-b + discriminant) / (2 * a)
val root2 = (-b - discriminant) / (2 * a)
return Pair(root1, root2)
}
fun main() {
val equation = QuadraticEquation(1.0, -3.0, 2.0)
val (root1, root2) = equation solve()
println("Root 1: $root1, Root 2: $root2")
}
위의 코드에서 QuadraticEquation 클래스는 이차 방정식의 계수 a, b, c를 나타냅니다.
그리고 solve라는 인픽스 함수를 정의하여 이차 방정식의 근을 계산합니다.
solve 함수는 이차 방정식의 근을 쌍으로 반환합니다.
main 함수에서는 이 클래스를 사용하여 이차 방정식의 근을 계산하고 출력합니다.
이처럼 인픽스 함수를 사용하여 이차 방정식의 근을 계산하는 함수를 직관적으로 만들 수 있습니다.
코틀린에서는 이외에도 이미 정의된 Infix 함수를 제공하고 있습니다.
가장 널리 사용되는 것 중 하나는 범위 생성 연산자 ..입니다.
이 연산자는 시작과 끝을 포함하는 범위를 생성하는 데 사용됩니다.
예를 들어, 1..10은 1부터 10까지의 정수 범위를 생성합니다.
또한, 컬렉션에서도 인픽스 함수가 사용됩니다.
예를 들어, in 연산자는 컬렉션에 특정 요소가 포함되어 있는지 확인하는 데 사용됩니다.
이것은 인픽스 함수로 정의되어 있습니다.
이처럼 코틀린 표준 라이브러리에서 이미 정의된 인픽스 함수를 정리하면 아래와 같습니다.
종류 | 설명 |
.. | 범위 생성 연산자 |
in | 컬렉션에 요소가 포함되어 있는지 확인하는 연산자 |
!in | 컬렉션에 요소가 포함되어 있지 않은지 확인하는 연산자 |
as | 형 변환을 수행하는 연산자 |
이러한 인픽스 함수들은 코틀린에서 자주 사용되는 함수 중 하나이며,
복잡한 코드를 더 읽기 쉽고 직관적으로 만들어주는 이점이 있습니다.
연산자 오버로딩
코틀린에서 어떤 두 값을 사칙 연산 하려면 연산자(operator)를 통해 계산할 수도 있지만,
연산자 함수를 통해 계산할 수도 있습니다.
예컨대, 어떤 두 값을 더하는 연산(+)은 아래와 같이 두 가지 방법 모두 사용할 수 있습니다.
fun main() {
val a = 1;
val b = 2;
val result1 = a + b;
val result2 = a.plus(b);
println(result1); // 3
println(result2); // 3
}
코틀린에서 이처럼 연산자(+)의 기능을 할 수 있도록 이미 구현된 메서드(함수)를 연산자 함수라고 합니다.
그리고 코틀린에서 이 연산자 함수는 오버 로딩할 수 있도록 지원합니다.
특정 연산자를 사용자 정의 클래스나 타입에 대해 사용할 수 있도록 하여 새로운 의미로 연산자를 사용할 수 있습니다.
연산자 오버로딩을 사용하려면 다음과 같은 과정을 따라야 합니다
- 연산자 오버로딩을 할 때는, 이미 정의된 연산자 함수명을 그대로 사용하되 operator 키워드를 통해 재정의해야 합니다. 이때 연산자 함수명은 대부분 연산자를 영어단어로 바꾼 것입니다. 예를 들어, + 연산자의 경우 plus, - 연산자의 경우 minus와 같은 식으로 작성됩니다.
- 연산자를 오버 로딩하려는 클래스에 해당 함수를 선언합니다.
- 함수의 매개변수 개수와 타입은 해당 연산자의 의미에 맞게 로직을 설계하고 구성합니다.
아래의 코드를 살펴보도록 하겠습니다.
data class Point(val x: Int, val y: Int) {
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
fun main() {
val point1 = Point(1, 2)
val point2 = Point(3, 4)
val result = point1 + point2 // + 연산자를 사용하여 두 점을 더함
println("Result: $result") // Result: Point(x=4, y=6)
}
위 코드에서 Point 클래스는 plus 함수를 정의하여 + 연산자를 오버 로딩했습니다.
이제 Point 객체 간에 + 연산자를 사용하여 두 점을 더할 수 있습니다.
이처럼 연산자 오버로딩을 사용하면 코드가 더 직관적이고 읽기 쉬워지며,
사용자 정의 클래스에 표준 연산자를 사용할 수 있습니다.
그러나 연산자 오버로딩을 과용하면 코드의 가독성을 해치고 오해를 일으킬 수 있으므로 주의해서 사용해야 합니다.
이외에도 코틀린에 연산자의 오버 로딩에 사용할 수 있는 연산자 함수는 아래와 같은 것들이 있습니다.
연산자 함수 | 설명 |
unaryPlus | 단항 연산자 +를 오버로딩하여 양수를 반환합니다. |
unaryMinus | 단항 연산자 -를 오버로딩하여 음수를 반환합니다. |
not | 단항 연산자 !를 오버로딩하여 논리 부정을 수행합니다. |
inc | 전위/후위 증가 연산자 ++를 오버로딩하여 값을 1 증가시킵니다. |
dec | 전위/후위 감소 연산자 --를 오버로딩하여 값을 1 감소시킵니다. |
times | * 연산자를 오버로딩하여 곱셈을 수행합니다. |
div | / 연산자를 오버로딩하여 나눗셈을 수행합니다. |
rem | % 연산자를 오버로딩하여 나머지 연산을 수행합니다. |
plus | + 연산자를 오버로딩하여 덧셈을 수행합니다. |
minus | - 연산자를 오버로딩하여 뺄셈을 수행합니다. |
rangeTo | .. 연산자를 오버로딩하여 범위를 생성합니다. |
contains | in 연산자를 오버로딩하여 컬렉션에 특정 요소가 포함되어 있는지 확인합니다. |
invoke | 함수 호출 연산자 ()를 오버로딩하여 함수를 호출합니다. |
get | 배열 접근 연산자 []를 오버로딩하여 특정 인덱스의 요소를 반환합니다. |
set | 배열 접근 연산자 []를 오버로딩하여 특정 인덱스의 요소를 설정합니다. |
equals | == 연산자를 오버로딩하여 객체의 동등성을 비교합니다. |
compareTo | 비교 연산자를 오버로딩하여 객체 간의 비교를 수행합니다. |
iterator | 반복 연산자 for를 오버로딩하여 컬렉션에 대한 반복을 수행합니다. |
next | 반복자의 next() 메서드를 오버로딩하여 다음 요소를 반환합니다. |
plusAssign | 복합 대입 연산자 +=를 오버로딩하여 덧셈 후 할당을 수행합니다. |
minusAssign | 복합 대입 연산자 -=를 오버로딩하여 뺄셈 후 할당을 수행합니다. |
timesAssign | 복합 대입 연산자 *=를 오버로딩하여 곱셈 후 할당을 수행합니다. |
divAssign |
복합 대입 연산자 /=를 오버로딩하여 나눗셈 후 할당을 수행합니다. |
remAssign |
복합 대입 연산자 %=를 오버로딩하여 나머지 연산 후 할당을 수행합니다. |