목표
- 시작 값과 끝나는 값이 있는 범위를 다루는 방법을 이해한다.
- Range와 Progression의 차이를 이해하고, 상황에 맞게 쓸 수 있도록 한다.
*Range와 Progression에 대한 자료가 많이 없어 직접 소스코드를 분석해서 자료를 만들었습니다. 부족한 부분이 있을 수 있으니 수정해야 할 부분은 댓글로 남겨주시면 감사하겠습니다.
개요
이 장에서 Progression Class와 Range Class를 묶어 놓은 것은 Range는 Progression의 특수한 형태이기 때문이다. 둘 다 범위를 다룬다는 것은 같지만, Progression은 start와 end를 정해놓고 구간을 1이상의 정수로 바꿀 수 있는 반면 range는 구간이 1로 고정되어 있다.
Progression 이란
Progression은 시작점과 끝점이 있고, 구간을 지정할 수 있는 범위이다.
Progression의 일부인 CharProgression의 internal constructor을 보면 아래와 같이 시작점(start) · 끝점(endInclusive) · 구간(step) 으로 구성되어 있다(가독성을 위해 아래와 같이 바꾸어 놓았다).
또한 Iterable Interface를 구현해놓아 반복문에 사용할 수 있으며, 코틀린에서 제공하는 확장 API(forEach, filter 등) 또한 사용할 수 있으며, Iterable의 확장함수로 정의된 contains 또한 사용이 가능하다.
open class CharProgression(start: Char, endInclusive: Char, step: Int) : Iterable<Char>
Range 란
Range는 구간이 1인 Progression에 해당한다. 먼저 Range의 정의를 보고 시작하자.
public class CharRange(start: Char, endInclusive: Char) : CharProgression(start, endInclusive, 1), ClosedRange<Char>
Range는 Progression을 상속하는데 step(구간)부분에 1이 들어간다. 즉, 시작점과 끝점이 있고 구간이 1인 특수한 형태의 Progression이다. ClosedRange<T> 인터페이스를 통해 contains를 구현하여 사용이 가능하다. 즉, Progression과는 contains 메서드 동작 방식이 다르지만, 결과 값은 같다.
override fun contains(value: Char): Boolean = first <= value && value <= last
override fun isEmpty(): Boolean = first > last
개념은 간단하게 이정도로 넘어가고 생성을 다루어보자.
Range & Progression 활용
위에서는 Progression이 상위 개념이었으므로 Progression에 대해 먼저 다룬 후 Range를 다루었다. 아래에서는 Progression의 간단한 형태인 Range부터 다루어 보도록 하겠다.
Range 활용
Range 생성
코틀린에서는 rangeTo라는 연산자 함수(Operator Function)에 대한 반환 값으로 닫힌 구간(양 끝을 포함하는 구간) 생성하거나, infix function until()을 이용해 시작은 닫혀있고 끝만 열린 구간 생성한다.
operator | operator function |
.. | rangeTo() |
infix function |
until() |
public class Int private constructor() : Number(), Comparable<Int> {
...
public operator fun rangeTo(other: Int): IntRange
public operator fun rangeTo(other: Long): LongRange
}
rangeTo는 해당 Range의 Int, Char 등 Class의 function으로 들어가 있다. 하지만, 해당 타입 말고 다른 타입의 Range도 생성할 수 있다. 자 이제 range를 생성해보자
- IntRange 생성하기
1 .. 10 // 1..10
1.rangeTo(10) // 1..10
1 until 10 // 1..9 -- 끝이 열려있다.
- CharRange 생성하기
'A'..'Z' // A..Z
'A'.rangeTo('Z') // A..Z
'A' until 'Z' // A..Y -- 끝이 열려있다.
- ComparableRange 생성하기 : Comparable 인터페이스가 구현되어 있는 Class만 ComparableRange로 생성될 수 있다.
- *ComparableRange는 until로 생성이 불가능하다.
public operator fun <T : Comparable<T>> T.rangeTo(that: T): ClosedRange<T> = ComparableRange(this, that)
"A".."Z" // A..Z
"A".rangeTo("Z") // A..Z
Range조작
- Iterable에 대한 확장함수(forEach, filter, groupBy 등)로 조작이 가능하다. Progression에 iterable이 구현되어 있기 때문이다.
('A'..'C').forEach {
print(it)
}
// ABC
('A'..'C').filter{ it == 'A' } // [A] -- List<Char>형태
- Iterable을 돌리는 keyword 'in'으로 조작이 가능하다
for (charValue in 'A'..'C') {
print(charValue)
}
//ABC 출력
- Collection으로 변환 : Iterable의 확장함수로 변환이 가능하다. Array로의 변환은 List나 Set으로 변환한 후에 가능하다.
('A'..'C').toList()
('A'..'C').toSet()
('A'..'C').toList().toTypedArray()
- Range 안에 특정한 값이 포함되어 있는지 확인하는 contains 확장함수의 사용이 가능하다. ClosedRange 인터페이스가 구현되어 있기 때문이다.
('A'..'Z').contains('C') // true
Progression 활용
Progression 생성
Progression은 구간(step)이 있는 범위이다. 총 세가지의 function으로 생성할 수 있다.
function type | function |
infix | downTo |
infix | step |
extension | reversed |
1..10 step 2 // 1..9 step 2
10 downTo 1 step 2 // 10 downTo 2 step 2
(1..10).reversed() // 10 downTo 1
1..11 step 2 // 1..11 step 2
11 downTo 1 step 2 // 11 downTo 2 step 2
(1..11).reversed() step 2 // 11 downTo 1 step 2
Progression 조작
Progression에 대한 조작은 Range으로 할 수 있는 조작과 같다.
- Iterable에 대한 확장함수(forEach, filter, groupBy 등)로 조작이 가능하다.
- Iterator을 돌리는 키워드 'in'으로 조작이 가능하다.
- Collection으로 변환이 가능하다.
- Iterable의 확장함수로 정의된 contains 메서드 사용이 가능하다.