Programming/Kotlin

코틀린(Kotlin) - 고차 함수 안에서 흐름 제어

JunsuKim 2021. 11. 14.
728x90

람다 안의 return문: 람다를 둘러싼 함수로부터 반환

data class Person(val name: String, val age: Int)
val people = listOf(Person("Alice", 29), Person("Bob", 31))
fun lookForAlice(people: List<Person>) {
    for(person in people) 
        if(person.name = "Alice") {
            println("Found!")
            return
        }
    }
    println("Alice is not found")
}

>>> lookForAlie(people)
Found!

for문을 forEach 함수로 바꾸어도 루프의 기능은 동일하다.

forEach에 넘긴 람다 안에 있는 return도 같은 의미이기 때문이다.

fun lookForAlice(people: List<Person>) {
    people.forEach {
        if(it.name == "Alice")
            println("Found!")
            return
        }
    }
    println("Alice is not found")
}

람다 안에서 return을 사용하면 람다로부터만 반환되는 게 아닌 그 람다를 호출하는 함수가 실행을 끝내고 반환한다.

자신을 둘러싸고 있는 블록보다 더 바깥에 있는 다른 블록을 반환하게 만드는 return문을 넌로컬(nunrocal) return이라 한다.

return이 바깥쪽 함수를 반환시킬 수 있는 때는 람다를 인자로 받는 함수가 인라인 함수인 경우 뿐이다.

람다로부터 반환: 레이블을 사용한 return

람다 식에서도 로컬 return을 사용할 수 있다.

람다 안에서 로컬 return은 for 루프의 break와 비슷한 역할을 한다. 로컬 return은 람다의 실행을 끝내고 람다를 호출했던 코드의 실행을 계속 이어간다.

로컬 return과 넌로컬 return을 구분하기 위해 레이블을 사용해야 한다.

→ return으로 실행을 끝내고 싶은 람다 식 앞에 레이블을 붙이고, return 키워드 뒤에 그 레이블을 추가하면 된다.

fun lookForAlice(people: List<Person>) {
    people.forEach label@{
        if(it.name == "Alice") return@label
    }
    println("Alice might be somewhere")
}

람다 식에 레이블을 붙이려면 레이블 이름 뒤에 @ 문자를 추가한 것을 람다를 여는 { 앞에 넣으면 된다.

람다로부터 반환하려면 return 키워드 뒤에 @ 문자와 레이블을 차례로 추가하면 된다.

people.forEach label@{
    if(it.name == "Alice") return@label
}

람다에 레이블을 붙여 사용하는 대신 람다를 인자로 받는 인라인 함수의 이름을 return 뒤에 레이블로 사용해도 된다.

fun lookForAlice(people: List<Person>) {
    people.forEach {
        if(it.name == "Alice") return@forEach
    }
    println("Alice might be somewhere")
}

람다 식의 레이블을 명시하면 함수 이름을 레이블로 사용할 수 없다.

람다 식에 레이블이 2개 이상 붙을 수 없다.

 

넌로컬 반환문은 장황하고, 람다 안의 여러 위치에 return 식이 들어가야 하는 경우 사용하기 불편하다.

넌로컬 반환문을 여럿 사용해야 하는 코드 블록을 쉽게 작성하기 위해 무명 함수를 알아야 한다.

무명 함수: 기본적으로 로컬 return

무명 함수는 코드 블록을 함수에 넘길 때 사용할 수 있는 방법이다.

fun lookForAlice(people: List<Person>) {
    people.forEach(fun (person) {
        if(person.name == "Alice") return
        println("${person.name} is not Alice")
    })
}

무명 함수는 일반 함수와 비슷해 보이지만 함수 이름이나 파라미터 타입을 생략할 수 있다.

무명 함수도 일반 함수와 같은 반환 타입 지정 규칙을 따른다.

블록이 본문인 무명 함수는 반환 타입을 명시해야 하지만, 식을 본문으로 하는 무명 함수의 반환 타입은 생략할 수 있다.

people.filter(fun (person): Boolean {
    return person.age < 30
})

무명 함수 안에서 레이블이 붙지 않은 return 식은 무명 함수 자체를 반환시킬 뿐 무명 함수를 둘러싼 다른 함수를 반환시키지 않는다.

return은 fun 키워드를 사용해 정의된 가장 안쪽 함수를 반환시킨다.

람다 식은 fun을 사용해 정의되지 않으므로 람다 본문의 return은 람다 밖의 함수를 반환시킨다.

무명 함수는 fun을 사용해 정의되므로 그 함수 자신이 바로 가장 안쪽에 있는 fun으로 정의된 함수이다.

-> 무명 함수 본문의 return은 그 무명 함수를 반환시키고, 밖의 다른 함수를 반환시키지 못한다.

 

무명 함수는 일반 함수와 비슷해 보이지만 실제로는 람다 식에 대한 문법적 편의일 뿐이다.

람다 식의 구현 방법이나 람다 식을 인라인 함수에 넘길 때 어떻게 본문이 인라이닝 되는지 등의 규칙을 무명 함수에도 적용할 수 있다. 

728x90

댓글