코틀린에서는 범위, 수열을 쉽게 사용할 수 있는 Range, Progression 클래스가 있습니다
if (i in 1..4) { // equivalent of 1 <= i && i <= 4
print(i)
}
Range는 from..to 방식으로 만들 수 있습니다.
for (i in 1..4) print(i) // 1 2 3 4
정수형의 Range 클래스(IntRange, LongRange. CharRange)는 Progression도 되기 때문에 iterate가 가능합니다
for (i in 4 downTo 1) print(i) // 4 3 2 1
유의할 점은 .. 로 만들 때 a <= b 조건을 만족해야 합니다. 즉 오름차순으로만 만들 수 있고, 내림차순 형태로 만들려면 downTo 메소드를 사용해야 합니다
for (i in 1..8 step 2) print(i)
println()
for (i in 8 downTo 1 step 2) print(i)
// Output
// 1357
// 8642
step 메소드를 이용해 iteration step 값을 정할 수 있습니다. downTo 에서도 동일하게 사용 가능합니다
for (i in 1 until 10) { // i in 1 until 10, excluding 10
print(i)
}
range 끝 범위는 포함하고 싶지 않을 때, 예를 들어 [1, 10) 처럼 range를 만들고 싶은 경우 until 메소드를 사용합니다
for (i in (1..4).reversed()) print(i)
progression은 reversed 메소드를 사용해 역순으로 순회할 수 있습니다
커스텀 Range, Progression 클래스 만들기
커스텀 클래스가 in operator를 쓰거나 iteration이 가능하도록 만들어 봅시다
data class MyDate (
val year: Int,
val month: Int,
val day: Int,
)
예제로 MyDate 클래스를 만들어봤습니다. in operator를 사용하려면 Comparable 인터페이스를 구현하면 됩니다.
ClosedRange<T>는 compareTo 메소드만 쓰기 때문에 따로 만들어 줄 필요는 없습니다
data class MyDate (
val year: Int,
val month: Int,
val day: Int,
) : Comparable<MyDate> {
override fun compareTo(other: MyDate): Int =
when {
year != other.year -> year - other.year
month != other.month -> month - other.month
else -> day - other.day
}
}
완성입니다.
fun main(args: Array<String>) {
val d1 = MyDate(2022, 1, 1)
val d2 = MyDate(2022, 3, 10)
val d3 = MyDate(2022, 5, 21)
println(d2 in d1..d3) // true
println(d1 in d2..d3) // false
val r = d1..d3 // ClosedRange<MyDate>
}
이제 in을 이렇게 쓸 수 있습니다.
Iteration을 하고 싶다면 Progression 클래스를 만들어주면 됩니다.
data class MyDate(
var year: Int,
var month: Int,
var day: Int,
) : Comparable<MyDate> {
override fun compareTo(other: MyDate): Int =
when {
year != other.year -> year - other.year
month != other.month -> month - other.month
else -> day - other.day
}
// suppose every month ends on 30th day
fun nextDate(stepDays: Int): MyDate {
val tmp = this.copy()
tmp.addDate(stepDays)
return tmp
}
private fun addDate(stepDays: Int) {
day += stepDays
if (day > 30) {
month += day / 30
day %= 30
if (month > 12) {
year += month / 12
month %= 12
}
}
}
operator fun rangeTo(other: MyDate) = MyDateProgression(this, other)
}
class MyDateIterator(
startDate: MyDate,
private val endDateInclusive: MyDate,
private val stepDays: Int,
) : Iterator<MyDate> {
private var _current = startDate
override fun hasNext() = _current.nextDate(stepDays) <= endDateInclusive
override fun next(): MyDate {
val ret = _current
_current = _current.nextDate(stepDays)
return ret
}
}
class MyDateProgression(
override val start: MyDate,
override val endInclusive: MyDate,
private val stepDays: Int = 1,
) : Iterable<MyDate>, ClosedRange<MyDate> {
override fun iterator() = MyDateIterator(start, endInclusive, stepDays)
infix fun step(days: Int) = MyDateProgression(start, endInclusive, days)
}
짠 완성본입니다. (https://medium.com/kotlin-arsenal/kotlin-explained-custom-range-expressions-41537563f567 참고했습니다)
편의를 위해 매달 30일까지만 있다고 가정했습니다
Iterator와 Progression 클래스를 만들었고, 마지막으로 rangeTo 오퍼레이터를 클래스에 추가해줬습니다.
은근 귀찮네요 이건..;
fun main(args: Array<String>) {
val d1 = MyDate(2022, 1, 1)
val d2 = MyDate(2022, 5, 21)
for (d in d1..d2 step 47) {
println(d)
}
}
// Output
// MyDate(year=2022, month=1, day=1)
// MyDate(year=2022, month=2, day=18)
이런식으로 쓸 수 있습니다
downTo, until은 또 추가로 구현해줘야 하네요. 이건 머 생략하겠습니다
Reference
'프로그래밍 > Kotlin' 카테고리의 다른 글
[Kotlin] Extension function (확장 함수) (0) | 2022.03.21 |
---|---|
[Kotlin] map iteration with destructuring (0) | 2022.03.20 |
[Kotlin] 삼항 연산자 (ternary operator) (0) | 2022.03.20 |
[Kotlin] when expression (0) | 2022.03.20 |
[Kotlin] 프로퍼티 (Property) (0) | 2022.03.20 |