본문 바로가기

코틀린

코틀린: 연산자 in과 범위(updated)

범위(range) 관련 연산에 관한 보충 설명입니다. 자세한 내용은 쉽게 다가가는 최신 프로그래밍: 코틀린 - 2.6 범위 관련 연산을 참고하기 바랍니다. 연산자 in은 설정된 범위 안에 원소가 있는지를 조사하거나 for 문에서 해당 범위의 원소를 차례대로 순환할 수 있습니다.

원소 포함 여부 : in과 범위
어떤 영어 문자가 소문자인지 확인하려면 'a'에서 'z' 사이에 속하는지 조사해야겠죠. 아래처럼 코딩했다면 아직 프로그램 초보자입니다.

fun main() {
    val ch = 'k'
    println(isSmallCase(ch))
}

fun isSmallCase(c: Char) : Boolean {
    return ('a' <= c && c <= 'z')
}

함수 isLowerCase()를 사용한다면 "코딩 좀 해봤네..." 입니다.

fun isSmallCase(c: Char) : Boolean {
    return (c.isLowerCase())
}

그런데, 소문자 범위가 'k'부터 'p'까지이면 어떻게 할까요? 아쉽지만 isLowerCase()는 사용할 수 없습니다. 이럴 때 in 연산자와 범위 타입을 함께 사용하면 됩니다. 

fun isSmallCase(c: Char) : Boolean {
    return (c in 'k'..'p')
}

연산자 in은 함수 contains()와 연산자 중복(operator overloading) 관계입니다. 아래처럼 contains()를 사용해도 되지만, in을 사용하는 게 더 깔끔하고 코드를 읽기 쉽습니다.

fun isSmallCase(c: Char) : Boolean {
    return (('k'..'p').contains(c))
}

 

■ 범위 타입
위 예에서 연산자 ".."(점 2개)를 사용해 범위(시작..끝)를 설정했습니다. 범위는 시작..끝에서 시작과 끝을 모두 포함하는 폐구간입니다.  아래처럼 범위 타입을 갖는 변수(chRange)를 만들 수도 있습니다. 연산자 in과 범위 타입은 뗄레야 뗄 수 없는 사이입니다. 

fun isSmallCase(c: Char) : Boolean {
    val chRange: CharRange = 'k'..'p'
    return c in chRange
}

아래 코드는 정상 동작할까요? 범위는 문자나 숫자 뿐아니라 비교 가능한 클래스(Comparable인터페이스와 Comparator 인터페이스)라면, 해당 클래스의 객체를 사용해 범위를 만들 수 있습니다.

fun main() {
    println(isCorrectPrefix("cx"))
    println(isCorrectPrefix("ck"))
}

fun isCorrectPrefix(s: String) : Boolean {
    return s in "ca".."cp"
}

■ 응용 예제: 오차 허용 범위
Double 타입은 정밀도(precision)가 소수점이하 15자리로 가장 높습니다. 아래 예에서 변수 d2의 값은 3.14가 아닙니다. 정말? 확인해 보세요. d1과 d2의 값은 아주 아주 작은 차가 있습니다. 함수 inAllowableRange()에서 오차 허용 범위를 0.001까지 지정하면, 이제 두 수는 같은 수로 판별할 수 있습니다. 오차 허용 범위는 필요에 따라 언제든 수정할 수 있죠. 직접 코드를 입력하고 실행해보면 알 수 있습니다. 

import kotlin.math.abs

fun main() {
    val d1 = 3.14
    val d2 = 0.0314 * 1E2
    println("d1= $d1, d2 = $d2, diff = ${abs(d1-d2)}")
    println(d1-d2)
    if (inAllowableRange(d1-d2)) {
        println("two values are the same")
    } else {
        println("two values are the different")
    }
}

fun inAllowableRange(d: Double) : Boolean {
    val numberRange = 0.0..0.001
    return d in numberRange
}

 

순환: for 문에서 in 사용
for 문에서 in 은 설정된 범위의 모든 원소를 차례로 순환(iteration)할 수 있습니다. 아래처럼 순방향 또는 역방향으로 순환할 수 있습니다.

fun main() {
    for (c in 'k'..'p') {       // 순방향
        print("$c ")  
    }
    println()   // k l m n o p 

    for (c in 'p' downTo 'k') {  // 역방향
        print("$c ")
    }
    println()   // p o n m l k 
}

문자나 숫자로 설정한 범위 외에도 컬렉션 객체에 대해서도 in을 사용해 모든 원소를 순환할 수 있습니다.

fun main() {
    val list = listOf(1, 2, 3, 4, 5)
    for (item in list) {
        print("$item ")
    }
    println()  // 1 2 3 4 5
}

주의할 점은 Comparable 인터페이스를 구현한 클래스의 객체를 사용해 범위를 설정하면, for 문에서 in을 사용해 순환할 수 없습니다. 

 for (s in "ca".."cx")    // 에러 발생

이미 앞에서 설명한 것처럼, 해당 범위 안에 원소가 포함되는지 확인할 수는 있습니다.

s in "ca".."cx"