개발일지

Kotlin in A..Z (9) - 다양한 함수 본문

Kotlin (코틀린)

Kotlin in A..Z (9) - 다양한 함수

강태종 2020. 7. 14. 23:39

- 익명함수

함수의 형태를 가지고 이름이 없는 함수이다.

 

코드

val fun0: (Int, Int) -> Int = fun(x:Int, y:Int): Int {return x + y}
println(fun0(1, 2))

결과

3

익명함수의 간단한 표현

 

코드

val fun0 = fun(x:Int, y:Int) = x + y
println(fun0(1, 2))

결과

3

람다식 vs 익명함수

람다식에서는 return, continue, break처럼 제어문을 사용하기 어렵기 때문이다. 하지만 익명함수는 람다식보다 가독성이 떨어지는 특징이 있기 때문에 적절히 사용하애한다.


- 인라인 함수 (inline)

인라인 함수는 함수가 호출되는 곳에 코드를 붙여넣기 때문에 함수 콜백간 오버헤드를 줄일 수 있는 장점이 있다. 하지만 너무 긴 코드를 붙여넣기 하면 코드량이 많아져 성능 저하가 일어날 수 있다.

 

코드

inline fun add(x:Int, y:Int) = x + y


fun main() {
    println(add(1, 2))
}

 

결과

3

- 인라인 함수 제한하기 (noinline)

인라인 함수의 매개변수로 사용한 람다식의 코드가 너무 길거나 인라인 함수의 코드가 너무 길면 위에서 언급한 성능저하 문제가 발생할 수 있기 때문에 noinline키워드를 사용하여 매개변수로 사용되는 람다식의 인라인을 제한할 수 있다.

 

코드

inline fun noinlineFunction(lambda1: () -> Unit, noinline lambda2: () -> Unit) {
    lambda1()
    lambda2()
    lambda2()
}

- 인라인 함수의 비지역 반환 (crossline)

원래 람다식은 return문을 포함할 수 없지만 inline처리된 람다식은 포함할 수 있다. 하지만 return문이 inline처리되면서 비지역 반환을 하여 예상치 못한 결과값을 반환할 수 있다.

 

코드

inline fun func1(lambda1:(Int) -> Unit) {
    println("func1 Start")
    lambda1(10)
    println("func1 End")
}

fun main() {
    func1() {
        println("lambda1 : $it")
        return
    }
}

 

결과

func1 Start
lambda1 : 10

 

return문이 inline처리되면서 fun1 End를 출력하기 전에 return되는 문제가 발생 이러한 문제를 막기위해 crossinline 키워드로 비지역 반환을 금지하는 람다식을 매개변수로 받을 수 있다.

 

 

코드

inline fun func1(crossinline lambda1:(Int) -> Unit) {
    println("func1 Start")
    lambda1(10)
    println("func1 End")
}

fun main() {
    func1() {
        println("lambda1 : $it")
        return // crossinline으로 비지역 반환을 금지하기 때문에 return문이 들어갈 수 없음
        // Kotlin: 'return' is not allowed here
    }
}

- 확장함수

클래스에는 다양한 함수가 정의되어 있지만, 기존의 정의된 함수가 아닌 확장함수를 통하여 개발자가 원하는 함수를 더 추가할 수 있다.

 

코드

// 기존의 없던 getLongString을 String의 확장 함수로 선언함
fun String.getLongString(str: String): String {
    return if(this.length >= str.length) {
        this
    } else {
        str
    }
}

fun main() {
    val str1 = "가"
    val str2 = "가나다"
    
    println(str1.getLongString(str2))
}

 

결과

가나다

- 중위함수 (infix)

중위함수는 연산자를 구현할 수 있는 함수이다

 

중위함수의 조건

  • 멤버 메서드 또는 확장 함수여야 한다.
  • 하나의 매개변수를 가져야 한다
  • infix 키워드를 사용하여 정의한다.

코드

infix fun Int.squared(indices:Int): Int {
    var result = 1
    for (i in 1..indices) {
        result *= this
    }

    return result
}

fun main() {
    println(2 squared 4) // 연산자 형식으로 사용할 수 있다.
}

 

결과

16

- 꼬리 재귀 함수 (tailrec)

재귀 함수는 자기 자신을 계속하는 함수이다. 재귀 함수는 자기 자신을 호출하면서 스택에 메모리를 쌓게되고 계속 쌓다보면 스택오버플로를 발생할 수 있다. Kotlin에서는 이러한 문제점을 해결하기 위해 꼬리 재귀 함수를 지원한다.

꼬리 재귀함수는 반환형이 □ * 함수 형태가 아닌 함수만 반환해야 한다.

 

일반적인 재귀함수

 

코드

fun factorial(n: Long): Long {
    return if(n == 1L) {
        1
    }
    else {
        n * factorial(n - 1)
    }
}

fun main() {
    println(factorial(5))
}

 

결과

120

일반 재귀함수의 스택오버플로우

 

코드

fun factorial(n: Long): Long {
    return if(n == 1L) {
        1L
    }
    else {
        n * factorial(n - 1L)
    }
}

fun main() {
    println(factorial(100000000))
}

 

결과

Exception in thread "main" java.lang.StackOverflowError

꼬리재귀함수

 

코드

tailrec fun factorial(n: Long, result: Long): Long {
    return if(n == 1L) {
        result
    }
    else {
        factorial(n - 1, result * n)
    }
}

fun main() {
    println(factorial(100000000, 1))
}

 

결과

0 // Long의 범위를 초과하여 결과값이 이상하긴 하지만 스택오버플로를 발생하지 않음

'Kotlin (코틀린)' 카테고리의 다른 글

Kotlin in A..Z (11) - when  (0) 2020.07.15
Kotlin in A..Z (10) - if문  (0) 2020.07.15
Kotlin in A..Z (8) - 람다  (0) 2020.07.11
Kotlin in A..Z (7) - 함수  (0) 2020.07.11
Kotlin in A..Z (6) - 연산자  (0) 2020.07.10
Comments