Ranges

범위 표현식은 연산자 형식인 ..을 가지는 rangeTo 함수로 구성된다. in 이나 !in과 함게 쓰인다. 범위는 모든 비교가능한 타입에 대해 정의되지만, 정수형 기본 타입에 최적화된 구현을 제공한다.

if (i in 1..10) { // 1 <= i && i <= 10 와 동일
    println(i)
}

정수 타입 범위 (IntRange, LongRange, CharRange)에는 반복가능한 특수 기능을 제공한다. 컴파일러가 이를 자바의 인덱스 기반 for-loop 와 동일하게 변환하여 오버헤드가 없다.

for (i in 1..4) print(i) // "1234" 출력

for (i in 4..1) print(i) // 아무것도 출력하지 않음

숫자를 역순으로 반복하고 싶은 경우 표준 라이브러리에 있는 downTo() 함수를 사용하면 된다.

for (i in 4 downTo 1) print(i) // "4321" 출력

1씩 증가/감소가 아닌 임의의 단계롤 숫자를 반복하는 경우 step() 함수를 사용하면 된다.

for (i in 1..4 step 2) print(i) // "13" 출력

for (i in 4 downTo 1 step 2) print(i) // "42" 출력

마지막 요소를 포함하지 않는 범위를 만들 경우 until 함수를 사용하면 된다.

for (i in 1 until 10) { // i in [1, 10), 10 은 제외된다
     println(i)
}

동작 방식

범위는 라이브러리의 공통 인터페이스인 ClosedRange<T>를 구현한다.

ClosedRange<T> 는 comparable 타입에 대해 수학적 의미의 닫힌 간격을 나타낸다. 범위에는 startendInclusive 두 개의 끝점을 가진다. 주 연산은 contains 이며 일반적으로 in/!in 연산자 형식으로 사용한다.

정수 타입 프로그레션 (IntProgression, LongProgression, CharProgression) 은 산술 진행을 나타낸다. 프로그레션은 first , last , 0이 아닌 step 으로 정의된다. 첫 번재 요소는 first 이고, 다음 요소는 이전 요소와의 step을 더한 값이다. last 요소는 프로그레션이 비어 (empty) 있지 않으면 항상 도달한다.

프로그레션은 Iterable<N>의 하위 타입으로 N에는 Int , Long , Char가 올수 있으며 for-loop나 map , filter와 같은 함수에서 사용가능 하다. 프로그레션을 통한 반복은 Java/JavaScript의 인덱싱된 for-loop와 동일하다.

for (int i = first; i != last; i += step) {
  // ...
}

정수 타입에서 .. 연산자는 ClosedRange<T>*Progression을 모두 구현한 객체를 생성한다.

예를들어, IntRange는 ClosedRange<Int>를 구현하고 IntProgression을 확장하므로 IntProgression에 정의된 모든 오퍼레이션을 IntRange에서도 사용 가능하다.

downTo()step() 함수의 결과는 항상 *Progression 이다.

프로그레션은 companion 객체에 정의된 fromClosedRange 함수로 생성된다.

IntProgression.fromClosedRange(start, end, step)

양수 step 기준으로 end 값보다 크지않은 최대값 또는 음수 step 기준으로 end보다 작지않은 최소값을 찾기위해 (last - first) % step == 0 와 같은 식을 사용해서 프로그레션의 last 요소를 계산한다.

유틸리티 함수

class Int {
    //...
    operator fun rangeTo(other: Long): LongRange = LongRange(this, other)
    //...
    operator fun rangeTo(other: Int): IntRange = IntRange(this, other)
    //...
}
public operator fun <T: Comparable<T>> T.rangeTo(that: T): ClosedRange<T>
fun Long.downTo(other: Int): LongProgression {
    return LongProgression.fromClosedRange(this, other.toLong(), -1L)
}

fun Byte.downTo(other: Int): IntProgression {
    return IntProgression.fromClosedRange(this.toInt(), other, -1)
}
fun IntProgression.reversed(): IntProgression {
    return IntProgression.fromClosedRange(last, first, -step)
}
fun IntProgression.step(step: Int): IntProgression {
    if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step")
    return IntProgression.fromClosedRange(first, last, if (this.step > 0) step else -step)
}

fun CharProgression.step(step: Int): CharProgression {
    if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step")
    return CharProgression.fromClosedRange(first, last, if (this.step > 0) step else -step)
}
(1..12 step 2).last == 11  // progression with values [1, 3, 5, 7, 9, 11]
(1..12 step 3).last == 10  // progression with values [1, 4, 7, 10]
(1..12 step 4).last == 9   // progression with values [1, 5, 9]

results matching ""

    No results matching ""