대부분의 언어와 마찬가지로 Kotilin에는 흐름에 대한 제어를 위한 표현이 있습니다.

  • return : 수행하던 구문에서 바로 리턴
  • break : 반복수행 하던 구문 중단
  • continue : 반복 구문 계속 수행

예제를 통해 알아보겠습니다.

fun main() {
    println("1에서 10까지의 수 하나를 입력하세요.")
    val value = readln().toInt()

    for(i in 1..10) {
        if(i/value == 1) {
            println("입력하신 값은 $i 입니다.")
            break
        }
    }
}

 

숫자를 입력받아 어떤 수인지 찾는 프로그램이라고 가정했습니다.

물론 입력 받은 value를 바로 출력하면 되지만, value의 값이 미지의 값이라고 가정하고 진행합니다.

for 반복문을 통해 i를 value로 나누어 1이면 같은 수겠지요? 그 수를 출력합니다.

출력했다는 이야기는 목적을 달성했다는 이야기이고 더 남은 반복문을 실행할 필요는 없습니다.

bareak를 통해 for 반복문에서 빠져 나옵니다.

 

위 예제에서 for 문을 별도 function으로 구현해 보겠습니다.

fun main() {
    println("1에서 10까지의 수 하나를 입력하세요.")
    val value = readln().toInt()

    findNumber(value)
}

fun findNumber(value :Int) {

    for(i in 1..10) {
        if(i/value == 1) {
            println("입력하신 값은 $i 입니다.")
            return
        }
        println("입력하신 값은 $i는 아닙니다.")
    }
    println("프로그램을 종료합니다.")
}

위 예제에서는 findNumber라는 function을 통해 value 값을 찾는 프로그램 입니다.

for 반복문을 수행하면서 if문의 조건이 true가 되면 출력을 하고 return합니다.

그럼 실제 실행해보면 결과는 어떨까요?

return 실행 결과

4를 입력해 4라는 것을 찾은 후 반복문에서 빠져나왔습니다.

그런데 여기서 보면 반복문 밖에 있는 출력문이 수행되지 않은 것을 알 수 있습니다.

반복문만이 아니라 function 자체에서 빠져나왔기 때문입니다.

 

그렇다면 어떻게 하면 for 반복문에서만 빠져 나올 수 있을까요?

return을 break로 변경한 실행 결과입니다.

break 실행 결과

break는 for 반복문에서만 빠져나와 아래에 있는 구문이 실행되었습니다.

 

Kotlin 모든 표현식에는 Label(레이블) 표시될 있습니다. 레이블은 abc@ 또는 fooBar@ 같이 식별자 뒤에 @ 기호가 붙는 형식을 갖습니다. 표현식에 레이블을 지정하려면 표현식 앞에 레이블을 추가하면 됩니다.

레이블은 어디에 쓰일까요?

fun main() {
    for(i in 1..3) {
        println("${i} 단입니다.")
        for (j in 1..3) {
            println("$i X $j = ${i*j}" )
            if(j==2) {
                break
            }
        }
    }
}

위 예제는 두개의 중첩 for 반복문이 있습니다. break 는 어디서 빠져나가는 것일까요?

실행결과

안쪽의 for문에서 빠져나왔습니다.

그렇다면 밖의 for 구문에서 빠져나오고 싶다면 어떻게 해야할까요? 이런 경우 사용하는 것이 레이블입니다. 

아래 예제로 확인해보겠습니다. 첫번째 반복문에 firstloop이라는 label을 달았고 break@firstloop라고 명시하여 첫번째 레이블 표현식에서 빠져나가도록 지정하였습니다.

fun main() {
    // 첫번째 for 구문에 firstloop이라는 레이블을 달았습니다.
    firstloop@ for(i in 1..3) {
        println("${i} 단입니다.")
        for (j in 1..3) {
            println("$i X $j = ${i*j}" )
            if(j==2) {
                // 레이블 firstloop에서 빠져나가라고 지시합니다.
                break@firstloop
            }
        }
    }
}

실행결과

 

Returns to labels

Kotlin에서는 함수들을 중첩해서 사용할 수 있습니다. return을 사용하면 외부 함수에서 반화하여 빠져나올 수 있습니다. 

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach {
        if (it == 3) return // 조건을 만족하면 반환합니다. 즉, foo ()함수에서 빠져 나갑니다.
        print(it)
    }
    println("이 문구는 프린트 되지 않습니다.")
}

 

만약 List에 3이 없다면 println이 실행됩니다.

List에 3이 있어도 println을 실행하려면 어떻게 해야할까요?

 

break 구문과 유사하게 label을 사용하여 landa 표현식에서만 빠져나올 수 있습니다.

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach lit@{
        if (it == 3) return@lit // lamda 호출자에 대한 로컬 반환
        print(it)
    }
    println("이제 프린트 됩니다.")
}

 

좀더 편리하게 label을 지정하지 않고 묵시적 label을 사용하기도 합니다. 

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach {
        if (it == 3) return@forEach // local return to the caller of the lambda - the forEach loop
        print(it)
    }
    println(" 이제 프린트 됩니다.")
}

 

또, lamda 표현식을 익명 함수(anonymous function)으로 바꿀 수 있으며, 이때는 익명 함수 자체에서 반환 됩니다. 

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
        if (value == 3) return  // local return to the caller of the anonymous function - the forEach loop
        print(value)
    })
    println(" done with anonymous function")
}

이때에도 println 문은 실행됩니다.

 

break 구문에 직접적으로 상응하지는 않지만, 다른 중첩 lamda를 추가하고 로컬이 아닌곳에서 반환하여 시뮬레이션 할 수는 있습니다.

fun foo() {
    run loop@{
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return@loop // non-local return from the lambda passed to run
            print(it)
        }
    }
    print(" done with nested loop")
}

 

value를 return 할 수도 있습니다.

fun foo() {
    val num = run loop@{
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return@loop it// non-local return from the lambda passed to run
        }
    }
    print(" $num done with nested loop")
}

 

실행 결과는 아래와 같습니다.

3 done with nested loop

+ Recent posts