ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 3-3. 함수 정의하기 : 루프
    Study(종료)/Kotlin 22.09.13 ~ 12.18 2022. 9. 26. 12:51

    3장 정리

    3-1. 함수 정의하기 : 함수

    3-2. 함수 정의하기 : 조건문

    3-3. 함수 정의하기 : 루프

    3-4. 함수 정의하기 : 예외 처리


    3.3 루프

    1) while과 do-while 루프

    사용자가 입력한 값을 읽어서 0이면 루프를 끝내고

    이전 입력값들의 합계를 표시하는 함수를 while문과 do-while 루프를 이용해 작성해보자.

    var sum = 0
    var num = 0
    
    while(num != 0){
        num = readLine()!!.toInt()
        sum += num
    }
    
    println("Sum: $sum")

    var sum = 0
    var num: Int
    
    do{
        num = readLine()!!.toInt()
        sum += num
    }while(num != 0)
    
    println("Sum: $sum")

    while문의 경우 조건이 참인 동안 루프를 실행한다. 조건이 참이 아닌 경우 루프 내부의 코드를 한 번도 실행하지 않는 경우도 있다.

    하지만 do-while문의 경우 루프 내부의 코드를 최소 한 번은 실행하고 이후에 조건이 참이 아닐 경우 루프를 종료한다.


    2) for루프와 이터러블

    루프는 다음 세 부분으로 나눌 수 있다.

    - 반복 변수(iteration varible) 정의(x)

    - 반복에 사용할 값들이 담겨 있는 컨테이너를 계산하기 위한 식(a)

    - 루프 내부에 해당하는 코드. 반복문 실행 시 이 내부(body)가 실행된다.

    var a = IntArray(10){it + it}
    var sum = 0;
    for(i in a){
        sum += i
    }
    println(sum) // 90

    반복 변수는 루프 내부에서만 접근할 수 있으며 매 루프마다 새로운 값이 들어간다.

    일반 변수와 달리 루프 변수에는 val이나 var를 붙이지 않고, 루프 변수는 자동으로 불변(val) 값이 된다

     

    문자열과 배열에는 원소나 문자의 인덱스 범위를 제공하는 indices라는 프로퍼티가 제공된다.

    위 내용들을 이용해 IntArray에 대해서 모든 값을 훑어보며 특정 조건을 만족하는 원소를 변경하는 코드를 작성해보자

    var a = IntArray(10){it + it}
    
    for(i in 0..a.lastIndex){
        if(i % 2 == 0){
        a[i] *= 10
        }
    }
    for(i in 0..a.lastIndex){
        print("${a[i]} ")
    }
    // 0 2 40 6 80 10 120 14 160 18
    
    println()
    for(i in a.indices){
        print("${a[i]} ")
    }
    // 0 2 40 6 80 10 120 14 160 18
    // 위 결과와 동일

    3) 루프 제어 흐름 변경하기 : break와 continue

    일반적인 반복문의 흐름을 바꾸고 싶을 경우, 반복문의 중간에서 종료 조건을 검사하는 방법이 두 가지 있다.

    break는 즉시 반복문을 종료시키고, 루프 다음 코드부터 실행되게 만든다

    continue는 현재 반복문을 마치고 조건 검사로 이동한다. 아래 코드를 보면 이해가 될 것 같다.

    var a = IntArray(10){it}
    
    for(i in a.indices){
        if(i == 5) break
        print("${a[i]} ")
    }
    // 0 1 2 3 4 
    println()
    
    for(i in a.indices){
        if(i == 5) continue
        print("${a[i]} ")
    }
    // 0 1 2 3 4 6 7 8 9

     

    5) 꼬리 재귀 함수

    코틀린은 꼬리 재귀(tail recursive) 함수에 대한 최적화 컴파일을 지원한다. 

    함수에 tailrec을 붙이면 컴파일러가 재귀 함수를 비재귀적인 코드로 자동으로 변환해준다.

    즉 재귀 함수의 간결함과 비재귀 루프의 성능만을 취할 수 있다.

    tailrec fun binIndexOf(
        x: Int,
        array: IntArray,
        from: Int = 0,
        to: Int = array.size
    ): Int{
        if(from == to) return -1
        val midIndex = (from + to - 1) / 2
        val mid = array[midIndex]
        return when{
            mid < x ->binIndexOf(x, array, midIndex + 1, to)
            mid > x ->binIndexOf(x, array, from, midIndex)
            else -> midIndex
        }
    }

    위 코드는 컴파일시 아래와 같이 작동한다.

    fun binIndexOf(
        x: Int,
        array: IntArray,
        from: Int = 0,
        to: Int = array.size
    ): Int{
        var fromIndex = from
        var toIndex = to
        
        while (true){
            if(fromIndex == toIndex) return -1
            val midIndex = (from + to - 1) / 2
            val mid = array[midIndex]
            when{
                mid < x ->fromIndex = midIndex + 1
                mid > x ->toIndex = midIndex
                else -> return midIndex
            }
        }
    }

    이런 변환을 적용하려면 함수가 재귀 호출 다음에 아무 동작도 수행하지 말아야 한다.

    이 말이 꼬리 재귀라는 용어의 의미이다.

    함수에 tailric을 붙였는데, 꼬리 재귀가 아니라는 사실을 컴파일러가 발견하면,

    컴파일러는 경고를 표시하고 함수를 일반적인 재귀 함수로 컴파일한다.

    또한 함수에 tailrec을 붙였는데 함수가 재귀 함수가 아닌 경우에도 컴파일러가 경고를 표시한다.


    다음 글

    3-4. 함수 정의하기 : 예외 처리

    반응형

    댓글

Designed by Tistory.