[Kotlin] - 제어문

코틀린 완벽 가이드를 공부하며 작성한 글입니다.
혼자 공부하고 정리한 내용이며, 틀린 부분은 지적해주시면 감사드리겠습니다 😀

제어문

kotlin의 제어문은 java와 꽤나 다른 모습을 보인다. 차이점을 하나씩 보면서 공부해보도록 하자!

조건문

kotlin에서의 조건문은 if, when으로 나뉜다.

if

if문은 불(boolean) 식의 결과에 따라 두가지 대안 중 하나를 선택할 수 있다.

fun main() {
    println(max(20, 30))
}

fun max(a: Int, b: Int): Int {
    if(a > b) return a
    return b
}

이렇게만 보면 java와 별 차이가 없는 것처럼 보이지만, 유연하게 활용할 수 있는 방법이 있다.

java에서는 if문을 사용해 변수에 값을 할당하려면 아래와 같이 작성해야 한다.

public class Test {
    public static void main(String[] args) {
        int num;

        if (args.length == 0) num = 0;
        else num = args.length;

        System.out.println(num);
    }
}

코드 블록으로 감싸면 굉장히 길고, 불필요한 느낌이 든다. 하지만 kotlin의 경우 변수에 바로 값을 할당 할 수 있다.

fun main(args: Array<String>) {
    val num = if (args.isEmpty()) { 0 } else args.size
}

이러한 유연함 덕분에 코드 다이어트는 물론이고, 보다 더 명확하게 어떠한 값이 저장되는지 쉽게 알 수 있다. if문을 위 코드처럼 식으로 사용할 경우 null을 방지하기 위한 else문도 필요하다.

이러한 방식을 지원하는 이유는 kotlin에는 삼항연산자 기능이 없기 때문이다. java에서의 삼항연산자는 여러 줄의 연산을 할 수 없지만, kotlin에서는 식으로 사용할 경우 여러 줄로 작성이 가능하다.

fun main() {
    // 10/3 입력
    val str = readln()
    val idx = str.indexOf("/")

    val result = if (idx >= 0) {
        val a = str.substring(0, idx).toInt()
        val b = str.substring(idx + 1).toInt()
        (a/b).toString()
    } else ""

    // 출력 : 3
    println("result = $result")
}

범위, 진행, 연산

when절을 보기 전에 위 내용에 대해 먼저 짚고 넘어가자.

java를 이용해 코딩 테스트 문제를 풀 때, 가장 불편했던 것은 범위를 지정하는 것이다.

0 <= nx < W, 0 <= ny < H를 표현하기 위해서는 아래와 같이 작성해야 한다.

if(nx >= 0 && nx < W && ny >= 0 && ny < H) { doSomething }

kotlin은 순서가 정해진 값 사이의 수열(interval)을 표현하는 몇 가지 타입을 제공해 훨씬 수월하게 코드를 작성할 수 있다.

if (nx in 0..(W - 1) && ny in 0..(H - 1)) { doSomething() }

우선 코드에서 보이는 ..은 범위를 지정하는 연산자이다.

0..2라고 지정할 경우 0에서 2까지의 범위를 지정하는 것이며, 0, 1, 2를 의미한다. 여기서 in 연산을 사용하면, 어떤 값이 범위 안에 들어있는지 확인할 수 있다.

즉, 위 코드는 nx가 0에서 W까지 속해있고, ny가 0에서 H까지 속해있을 경우를 의미하는 것이다.

이와 같이 숫자를 제외하고 문자열 혹은 문자 타입에서도 사용이 가능하다.

fun main() {
    val c = 'c'
    // false
    println(c in 'a' .. 'b')
    // true
    println(c in 'a' .. 'c')

    val str = "xyy"
    // false
    println(str in "aaa" .. "xyx")
    // true
    println(str in "aaa" .. "xyz")
}

정리해보면, .. 연산자는 시작 값과 끝 값을 모두 포함하는 범위를 나타낸다. 만약 끝 값을 포함하고 싶지 않다면 until 연산을 사용하면 된다.

if (nx in 0 until W && ny in 0 until H) { doSomething() }

위 처럼 작성하면 0 <= nx < W, 0 <= ny < H 식을 완벽히 나타낸 코드가 된다.

when

when절은 switch문과 비슷하다고 생각하면 된다.

특정 수를 입력하면 16진수로 변경해주는 프로그래밍을 예시로 코드를 확인해보자.

우선, java 13버전에서 릴리즈된 람다식을 이용한 switch문을 먼저 살펴보도록 하자.

public class Test {
    public static void main(String[] args) {
        int n = 13;

        char c = switch (n) {
            case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 -> '0';
            case 10, 11, 12, 13, 14, 15 -> (char)('A' + n - 10);
            default -> '?';
        };

        System.out.println(c);
    }
}

java에서는 switch를 사용해 변수에 바로 값을 넣을 수 있게 업데이트 되었지만, case에 조건식을 사용할 수 없기 때문에 모든 수를 각각 입력해줘야하는 번거로움이 있다.

kotlin의 경우에는 when절 내부에 범위 연산자인 ..을 사용할 수 있기 때문에 더욱 유연한 프로그래밍이 가능하다.

fun main() {
    println(hexDigit(13))
}

fun hexDigit(n: Int): Char {
    /*
    아래와 동일한 코드
    when {
        n in 0..9 -> return '0' + n
        n in 10..15 -> return 'A' + n - 10
        else -> return '?'
    }
    */
    when (n) {
        in 0..9 -> return '0' + n
        in 10..15 -> return 'A' + n - 10
        else -> return '?'
    }
}

switch와 여러 대안 중 하나를 선택한다는 것은 비슷하지만, 가장 큰 차이점은 조건식의 여부이다. when에서는 임의의 조건을 검사할 수 있지만, switch문은 주어진 식의 여러 가지 값 중 하나만 선택할 수 있다는 점이다.

switch는 fall-through라는 의미로, 어떤 조건을 만족할 때 프로그램이 해당 조건에 대응하는 문을 실행하고, 명시적으로 break를 만날 때까지 그 이후의 모든 가지를 실행한다.

kotlin의 경우 조건을 만족하는 가지만 실행하고, 절대 폴스루를 하지 않는다.

kotlin 1.3부터는 식의 대상을 변수에 연결(binding)할 수 있다.

fun readHexDigit() = when (val n = readLine()!!.toInt()) {
    in 0..9 -> '0' + n
    in 10..15 -> 'A' + n - 10
    else -> '?'
}

반복문

java에서의 for-loop는 아래 코드와 같이 생겼다.

public class Test {
    public static void main(String[] args) {
        for (int i = 0; i <= 10; i++) {
            System.out.print(i + " ");
        }
    }
}

하지만, kotlin에서의 for 루프는 javafor-each와 비슷한 형태를 띈다.

fun main() {
    for (i in 0 until 10) {
        print("$i ")
    }
}

만약 i+=2와 같이 값을 특정 수만큼 올려주고 싶으면 어떻게 작성해야할까?

바로 step 연산을 사용하여 1..10 step 2와 같이 원하는 정수를 작성해주면 끝이다.

fun main() {
    // 출력 : 0 2 4 6 8 10
    for (i in 0 .. 10 step 2) {
        print("$i ")
    }
}

i--와 같이 반대로 루프를 돌고 싶을 경우에는 downTo 키워드를 사용하면 된다.

fun main() {
    // 출력 : 10 8 6 4 2 0
    for (i in 10 downTo 0 step 2) {
        print("$i ")
    }
}

내포된 루프와 레이블

java에서의 레이블은 아래와 같이 사용된다.

public class Solution {
    public static void main(String[] args) {
        outer: for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 10; j++) {
                if (i * j == 32) {
                    // i = 4, j = 8 출력
                    System.out.printf("i = %d, j = %d", i, j);
                    break outer;
                }
            }
        }
    }
}

kotlin도 크게 다르진 않지만, @를 사용해 레이블을 표현한다.

fun main() {
    outer@ for (i in 0 until 10) {
        for (j in 0 until 10) {
            if (i * j == 32) {
                println("i = $i, j = $j")
                break@outer
            }
        }
    }
}

간단한 Up&Down 게임을 만들어보면 아래와 같다.

import kotlin.random.Random

fun main() {
    val num = Random.nextInt(1, 101)
    outer@ while (true) {
        var guess = readln().toInt()

        val msg = when {
            guess < num -> "up"
            guess > num -> "down"
            else -> break@outer
        }
        println(msg)
    }
    println("answer : $num")
}

태그:

카테고리:

업데이트:

댓글남기기