[Kotlin] 고차함수

이상목·2024년 5월 26일
0

Kotlin

목록 보기
12/20

오늘은 코틀린의 고차함수를 배워보겠습니다.

고차 함수와 함수 리터럴

  • 고차 함수는 함수를 인자로 받거나 함수를 반환하는 함수입니다.
  • 함수 리터럴은 이름이 없는 함수, 즉 람다(lambda) 표현식을 의미합니다.
fun <T> filter(list: List<T>, predicate: (T) -> Boolean): List<T> {
    val result = mutableListOf<T>()
    for (item in list) {
        if (predicate(item)) {
            result.add(item)
        }
    }
    return result
}

// 함수 리터럴의 예: 람다 표현식
val isEven = { x: Int -> x % 2 == 0 }

// 사용 예시
val numbers = listOf(1, 2, 3, 4, 5, 6)
val evenNumbers = filter(numbers, isEven)
println(evenNumbers) // 출력: [2, 4, 6]

복잡한 함수 타입과 고차 함수의 단점

  • 고차 함수는 강력하지만, 복잡한 함수 타입을 사용하면 코드가 어렵고 가독성이 떨어질 수 있습니다.
  • 예를 들어, 함수가 다른 함수를 반환하거나 여러 개의 함수를 인자로 받을 때, 함수 타입의 선언이 복잡해질 수 있습니다.
// 복잡한 함수 타입의 예: 함수를 반환하는 함수
fun operation(): (Int, Int) -> Int {
    return { a, b -> a + b }
}

// 고차 함수의 단점 예시: 함수 타입이 복잡해질 수 있음
fun calculate(a: Int, b: Int, op: (Int, Int) -> Int): Int {
    return op(a, b)
}

val sum = calculate(5, 3, operation())
println(sum) // 출력: 8
// 이 경우 함수 타입 (Int, Int) -> Int를 여러 번 사용하므로 코드가 장황해질 수 있습니다.

inline 함수

  • inline 함수는 함수 호출을 줄여 성능을 향상시킬 수 있는 기능입니다.
  • 코틀린에서는 함수가 inline 키워드로 선언되면, 해당 함수 호출이 컴파일 시점에 인라인화되어 호출 비용을 줄일 수 있습니다.
// inline 함수의 예
inline fun <T> lock(lock: Lock, body: () -> T): T {
    lock.lock()
    try {
        return body()
    } finally {
        lock.unlock()
    }
}

// 사용 예시
val lock = ReentrantLock()
lock(lock) {
    println("Locked and executing!")
}
  • 위와 같이 lock 함수가 inline으로 선언되어, 실제로 함수 호출 없이 본문이 직접 호출된 위치에 삽입됩니다.
  • 이는 성능 최적화에 도움이 됩니다.



SAM (Single Abstract Method)

  • SAM은 단일 추상 메서드(Single Abstract Method)를 의미하며, 자바의 함수형 인터페이스와 유사한 개념입니다.
  • 코틀린에서는 SAM 변환을 통해 자바 인터페이스를 람다 표현식으로 쉽게 사용할 수 있습니다.
  • 즉, 하나의 추상 메서드를 가지는 인터페이스를 람다로 사용할 수 있게 해줍니다.

예를 들어, 자바의 Runnable 인터페이스는 단일 추상 메서드 run()을 가지고 있습니다.
코틀린에서는 이를 람다로 사용할 수 있습니다.

예시

fun runRunnable(runnable: Runnable) {
    runnable.run()
}

fun main() {
    // SAM 변환을 사용한 람다 표현식
    runRunnable { println("Hello from a Runnable") }
}

runRunnable 함수는 Runnable 타입의 매개변수를 받습니다. 이를 호출할 때 람다 표현식을 사용하여 Runnable의 run 메서드를 구현할 수 있습니다.

함수 참조 (Function References)

  • 함수 참조는 코틀린에서 함수 또는 메서드를 변수에 저장하거나 다른 함수에 전달할 수 있는 기능입니다. 함수 참조는 :: 연산자를 사용하여 생성됩니다.

  • 아래 예제들에서 함수 참조는 함수 또는 메서드를 :: 연산자를 사용하여 변수에 할당하거나 다른 함수에 전달합니다.


최상위 함수 참조

fun greet(name: String) {
    println("Hello, $name!")
}

fun main() {
    val greeter: (String) -> Unit = ::greet
    greeter("World")
}

클래스의 멤버 함수 참조

class Person(val name: String) {
    fun sayHello() {
        println("Hello, my name is $name")
    }
}

fun main() {
    val person = Person("Alice")
    val greeter: () -> Unit = person::sayHello
    greeter()
}

확장 함수 참조

fun String.shout() {
    println(this.uppercase())
}

fun main() {
    val shouter: String.() -> Unit = String::shout
    "hello".shouter()
}

생성자 참조

class User(val name: String, val age: Int)

fun main() {
    val userCreator: (String, Int) -> User = ::User
    val user = userCreator("Bob", 30)
    println("${user.name}, ${user.age}")
}

SAM과 함수 참조 결합

  • 두 개념을 결합하여 사용할 수도 있습니다. 예를 들어, 자바의 Comparator 인터페이스는 단일 추상 메서드 compare를 가지고 있으며, 이를 코틀린에서 람다 표현식이나 함수 참조로 사용할 수 있습니다.
fun main() {
    val list = listOf("banana", "apple", "cherry")

    // 람다 표현식 사용
    list.sortedWith(Comparator { a, b -> a.length - b.length }).forEach { println(it) }

    // 함수 참조 사용
    val compareByLength: (String, String) -> Int = { a, b -> a.length - b.length }
    list.sortedWith(Comparator(compareByLength)).forEach { println(it) }
}

결국엔 코드의 재사용성과 가독성을 높이기 위해 즉, 성능을 위해 사용하는 것으로 문법이 익숙해지면 클린 코드 작성에 용이할 것으로 보인다 !

profile
기록은 기억을 지배한다.

0개의 댓글