대부분의 언어와 마찬가지로 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

Kotlin에서 반복문은 for 또는 while을 사용하여 구현할 수 있습니다.

for와 while 사용법에 대해 이야기 하기전에 Kotlin에서 제공하는 Ranges의 개념을 이해할 필요가 있습니다.

Ranges

Ranges 반복문에서 몇번을 반복할 것인지에 대한 구현입니다.

// 시작 숫자, 마지막 숫자 포함
1..5 // 1,2,3,4,5
1.rangeTo(5) // 1,2,3,4,5

// 마지막 숫자 포함하지 않음
1..<5 // 1,2,3,4
1 until 5 // 1,2,3,4
1.rangeUntil(5) // 1,2,3,4

// 역으로 반복
(1..5).reversed() //5,4,3,2,1
5 downTo 1 // 5,4,3,2,1

// step를 사용하여 임의의 단계로 숫자를 반복 
0..10 step 2 // 0,2,4,6,8,10
0.rangeTo(10) step 2 // 0,2,4,6,8
0 until 10 // 0,2,4,6,8
0.rangeUntil(10) step 2 //0,2,4,6,8
10 downTo 0 sten 2 // 10,8,6,4,2,0

// char에도 동일하게 적용 가능
tempChar in 'a' until 'f' step 2 //a,c,e

 

Loops

Kotlin에서는 반복 구조를 구현하기 위해 for와 while을 사용할 수 있습니다.

Rages를 사용하여 범위 값안에서 반복하여 작업을 해야하는 경우는 for를 사용하고,

특정 조건이 충족될 때까지 작업을 계속하려면 while를 사용합니다.

 

for

for 반복 구문을 사용해서 구구단 프로그램을 만들어 보겠습니다.

fun main() {
    println("몇단을 외워볼까요?")
    
    // readln()은 입력값을 String으로 반환
    // 계산을 위해 Int로 분석
    val level = readln().toInt
    
    println("$level 단을 외워보겠습니다.")
    
    // 결과 값을 저장할 변수 생성
    var result = 0
    // 1에서 9까지(9포함) for문 구현
    for(num in 1..9) {
    	result = level*num
    	println("$level X $num = $result")
    }
}

실행 결과

 

for 구문의 반복 지정을 위해서 range를 사용하는 방법을 알아보았습니다.

사실 for 구문의 반복 지정은 range를 통해서만 가능한 것은 아닙니다.

Kotlin 공식 문서에 따르면 'iterator(반복자)를 제공하는 어떤 것이던 for 반복문에서 사용할수 있다.' 라고 언급하고 있습니다.

무슨 이야기 인지 예제로 알아보겠습니다.

fun main() {
    // Array 선언
    val array = arrayOf("apple","banana","cherry")

    // iterator는 array의 element 순서대로 반복하는 iterator를 반환 
    for (value in array.iterator())
        print("$value ")
    //apple banana cherry

    // indices는 array의 유효한 인덱스 범위를 반환
    for (index in array.indices)
        print("${array[index]} ")
    //apple banana cherry
    
    // array의 각 element를 index와 element 자체를 포함하는
    // IndexedValue로 래핑하여 Lazy Iterable 반환
    for ((index, value) in array.withIndex())
        println("$index:$value ")
    //0:apple 1:banana 2:cherry 
}

 

while

while 과 do-while 반복 구문이 있습니다. 두 구문은 특정 조건(혹은 상태)가 만족하는 한 { }안의 내용을 연속적으로 반복한다는 것은 같으나 특정 조건(혹은 상태)를 확인하는 시점이 다릅니다.

 

특정 조건(혹은 상태) 확인 시점

while 반복문은 조건을 먼저 확인하고 조건이 맞으면 { }안의 내용을 실행

do-while 반복문은 먼저 실행하고 그 다음 조건을 확인. 따라서 조건과 상관없이 먼저 한번은 실행됩니다.

fun main() {
    var x = 5
    
    // x가 0보다 크면 반복 실행합니다.
    while (x > 0) {
        println("x는 ${x}입니다.")
        x--
    }
    println("do-while 반복문 실행전 x는 ${x}입니다.")
    
    //먼저 실행한 후 x가 0보다 큰지 확인합니다.
    do {
        println("x는 ${x}입니다.")
        x--
    } while (x > 0)
}

위 예제에 대한 결과는 아래와 같습니다.

do-while문 x가 0보다 크지 않아도 실행 됨

결과에서 본것처럼 do-while문 실행전에 x의 값은 0이었습니다. do-while문은 구문을 먼저 실행하고 조건을 확인하기 때문에 먼저 {} 안의 프린트 문을 실행하였고 그 후에 x가 0보다 큰수가 아니기 때문에 반복문을 종료하였습니다.

 

 

 

모든 프로그래밍 언어는 Control flow가 있습니다.

무엇인가를 판단하여 사실인지 아닌지(true or false)를 반환(return)하는 코드입니다.

예를 들어 2에서 1을 빼면 1인가요? 라는 질문에 사실(true)이라고 반환(return)하겠지요. 

이러한 코드들을 Conditional expressions라고 합니다.

 

Conditional expressions

Kotlin은 Conditional expressions(조건)를 확인하기 위해 if와 when을 제공합니다. 

 

If expression

if 표현은 간단합니다.

// 조건이 사실(true)이면 해야할일 수행
// 아니면 아무것도 하지 않음
if(조건) {
	해야할일
}

// 조건이 사실이면(true) 해야할일 수행
// 조건이 사실이 아니면(false) 이면 다른일 수행
if(조건) {
	해야할일
} else {
	다른일
}

// 조건1이 사실이면(true) 해야할일1 수행
// 조건2가 사실이면(true) 해야할일2 수행
// 조건1과 조건2가 모두 사실이 아니면(false) 해야할일3 수행
if(조건1) {
	해야할일1
} else if(조건2) {
	해야할일2
} else {
	해야할일3
}

 

if(조건)은 조건의 사실여부에 따라 값을 반환하며, 그 값은 true 또는 false 입니다.

조건이 사실이면(true)이면, { } 안의 내용을 수행합니다.

조건을 여러개 확인하는 경우에는 else if(조건)을 사용할 수 있습니다. if문의 조건은 순차적으로 확인하여 진행하기 때문에, 앞의 조건이 true이면 그 아래 조건은 무시됩니다. else if(조건)은 계속 추가하여 여러개의 조건을 확인 할 수 있습니다.

Kotlin에는 JVM에 기반하지만 tenary operator, 삼항연산자(condition ? then : else)를 사용하지 않습니다.

대신, Kotlin에서는 ? operator에 Null safety를 위한 주요한 역할을 부여했습니다. 

 

if문에 대한 여러가지 표현식을 살표보겠습니다. ※해야할일 Code가 1 line이면 {}을 생략할 수 있습니다.  

val a = 3
val b = 2

var max = a
// b가 a보다 크면 max를 b로 바꿉니다.
// a와 b중 큰 수가 max값이 됩니다.
if(a < b) max =b

// else 구문 사용
if (a > b) {
	max = a
} else {
    max = b
}

// 간단히 줄이기
max = if(a > b) a else b

 

 

when expression

when은 확인하는 조건이 여러개일때 사용합니다. 

when 구문은 크게 2가지 형태로 표현할 수 있습니다.

when(조건) {} 또는 when {} 형태입니다.

when은 {} 안에 ->를 사용하여 각 컨디션에 대한 분기문을 표현합니다.

else if와 마찬가지로 분기는 순차적으로 확인되며 true가 되는 분기문을 실행하고 나머지는 무시됩니다.

만약 분기문 전체가 true가 아니면 else 구문이 실행됩니다.

fun main() {
	prinln("프로야구팀을 입력하세요.")
	val team = readln() // 입력창으로 부터 읽어드림

	// 조건 구문
	when(team) {
		"LG" -> println("서울")
		"KT" -> println("수원")
		"SSG" -> println("인천")
		"NC" -> println("창원")
		"두산" -> println("서울")
		"KIA" -> println("광주")
		"롯데" -> println("부산")
		"삼성" -> println("대구")
		"한화" -> println("대전")
		"키움" -> println("서울")
		else -> println("모르겠어요.")
	}
    
	// 결과가 같은 경우에는 조건을 합쳐서 표현할 수 있음
	when(team) {
    	// LG 또는 두산 또는 키움인 경우
		"LG","두산","키움" -> println("서울")
		"KT" -> println("수원")
		"SSG" -> println("인천")
		"NC" -> println("창원")
		"KIA" -> println("광주")
		"롯데" -> println("부산")
		"삼성" -> println("대구")
		"한화" -> println("대전")
		else -> println("모르겠어요.")
	}     

	// 표현 구문
	when {
		team == "LG" -> println("서울")
		team == "KT" -> println("수원")
		team == "SSG" -> println("인천")
		team == "NC" -> println("창원")
		team == "두산" -> println("서울")
		team == "KIA" -> println("광주")
		team == "롯데" -> println("부산")
		team == "삼성" -> println("대구")
		team == "한화" -> println("대전")
		team == "키움" -> println("서울")
		else -> println("모르겠어요.")
	}

	//공통된 println을 밖으로...
	val city = when {
		team == "LG" -> "서울"
		team == "KT" -> "수원"
		team == "SSG" -> "인천"
		team == "NC" -> "창원"
		team == "두산" -> "서울"
		team == "KIA" -> "광주"
		team == "롯데" -> "부산"
		team == "삼성" -> "대구"
		team == "한화" -> "대전"
		team == "키움" -> "서울"
		else -> "모르겠어요."
	}
	println(city)
}

 

※ Kotlin 공식 사이트에서는 if문과 when 문 중 하나를 선택해야 하는 경우 when을 사용하도록 권장합니다.

 

 

+ Recent posts