Kotlin - What happens when a range is created using .. or how it performs comparations? - kotlin

I recently noticed a code refactor from:
if (date < minDate || date > maxDate)
to
if (date !in minDate..maxDate)
to which my main worry was that using range would create an "array" or some list of all the milliseconds between minDate and maxDate
I tried researching some of the kotlin internals but couldn't get a final answer on what would happen on that case.
Supposedly:
In Kotlin in checks are translated to the corresponding contains
calls

No, it does not create an array of every possible value (because an array would be inefficient for that, even if we did need to store every value, which we don't).
This is the source for the ClosedRange interface, which the .. range operator translates into (comments removed):
public interface ClosedRange<T: Comparable<T>> {
public val start: T
public val endInclusive: T
public fun isEmpty(): Boolean = start > endInclusive
public operator fun contains(value: T): Boolean =
value >= start && value <= endInclusive
}
As you can see the type it ranges over (T) must implement Comparable<T>. This allows the implementation to do a straight comparison of the value and the start and endInclusive of the range. You can see this in the implementation of contains(value: T).

According to Range class implementation in kotlin, it won't create a list and comparison will be done as follows (contains is called when comparing ranges), based on LongProgression class (created on Long ranges).
/**
* A range of values of type `Long`.
*/
public class LongRange(start: Long, endInclusive: Long) : LongProgression(start, endInclusive, 1), ClosedRange<Long> {
override val start: Long get() = first
override val endInclusive: Long get() = last
override fun contains(value: Long): Boolean = first <= value && value <= last
override fun isEmpty(): Boolean = first > last
override fun equals(other: Any?): Boolean =
other is LongRange && (isEmpty() && other.isEmpty() ||
first == other.first && last == other.last)
override fun hashCode(): Int =
if (isEmpty()) -1 else (31 * (first xor (first ushr 32)) + (last xor (last ushr 32))).toInt()
override fun toString(): String = "$first..$last"
companion object {
/** An empty range of values of type Long. */
public val EMPTY: LongRange = LongRange(1, 0)
}
}

Related

Kotlin: How to use higher order functions with OpenEndRange<LocalDate>

I want to use higher order functions like map for open end ranges.
val from = LocalDate.now().minusDays(10)
val to = LocalDate.now()
(from ..< to).forEach(::println)
I tried to copy an example for ClosedRange<LocalDate> but it does not work.
package de.otto.di.extensions
import java.time.LocalDate
class OpenEndRangeLocalDateIterator(
startDate: LocalDate,
private val endExclusive: LocalDate,
private val stepDays: Long
) : Iterator<LocalDate> {
private var currentDate = startDate
override fun hasNext() = currentDate.plusDays(stepDays) <= endExclusive
override fun next(): LocalDate {
val next = currentDate
currentDate = currentDate.plusDays(stepDays)
return next
}
}
#OptIn(ExperimentalStdlibApi::class)
class OpenEndLocalDateRange(
override val start: LocalDate,
override val endExclusive: LocalDate,
private val stepDays: Long = 1
) : Iterable<LocalDate>, OpenEndRange<LocalDate> {
override fun iterator(): Iterator<LocalDate> =
OpenEndRangeLocalDateIterator(start, endExclusive, stepDays)
infix fun step(days: Long) = OpenEndLocalDateRange(start, endExclusive, days)
}
infix operator fun LocalDate.rangeUntil(to: LocalDate): OpenEndLocalDateRange =
OpenEndLocalDateRange(this, to)
It is implemented for Int so I assume it must be possible somehow. How can I achieve this?
The issue here is that you've defined the operator function to return OpenEndRange<LocalDate> rather than OpenEndedLocalDateRange. If you change the return type of your operator function that should fix the issue.
The reason why it isn't working as is is because OpenEndRange doesn't have the higher order functions defined for it (ClosedRange doesn't have them defined as well). Int has it because the operators return an IntRange which indirectly extends Iterable<Int> via IntProgression and Iterable has these higher order functions defined, so, the only missing piece is failing to return the correct type from your operator function.

equal() function in Kotlin

I need if two objects are equal() need to print("Equal") if objects are not equal -> "Not equal".I can not find mistake of this codeThis is my code in IntelliJ IDEA
As a side note, when we override equals(), it is recommended to also override the hashCode() method. If we don’t do so, equal objects may get different hash-values; and hash based collections, including HashMap, HashSet, and Hashtable do not work properly (see this for more details). We will be covering more about hashCode() in a separate post.
References:
internal class Complex(private val re: Double, private val im: Double) {
// Overriding equals() to compare two Complex objects
fun equals(o: Object): Boolean {
// If the object is compared with itself then return true
if (o === this) {
return true
}
/* Check if o is an instance of Complex or not
"null instanceof [type]" also returns false */if (o !is Complex) {
return false
}
// typecast o to Complex so that we can compare data members
val c = o as Complex
// Compare the data members and return accordingly
return (java.lang.Double.compare(re, c.re) == 0
&& java.lang.Double.compare(im, c.im) == 0)
}
} // Driver class to test the Complex class
fun main(args: Array<String>) {
val c1 = Complex(12.0, 15.0)
val c2 = Complex(10.0, 15.0)
if (c1 == c2) {
println("Equal ")
} else {
println("Not Equal ")
}
}
In Kotlin, you use Any instead of Object. It will not allow you to test if your class instance is an Object, only Any.
Also, you are failing to override equals since you didn't use the override keyword. The argument needs to be Any?, not Object.
Change
fun equals(o: Object): Boolean {
to
override fun equals(o: Any?): Boolean {
Also, in this case, you should use a data class so you won't have to write your own equals() implementation in the first place.
And in the future, when you aren't using a data class, you can use the IDE option to generate equals and hashcode for you automatically.
A data class would make more sense:
data class Complex(
private val re: Double,
private val im: Double
)
val c1 = Complex(12.0, 15.0)
val c2 = Complex(10.0, 15.0)
if (c1 == c2) {
println("Equal")
} else {
println("Not Equal")
}
Output: Not Equal

What is the difference between Comparable and operator compareTo?

Lets say I have class A(val foo: Double).
I want to be be able to compare it to other A, Double, and Int.
If I implement Comparable, I can only compare it against one other object type.
override fun compareTo(other: A): Int {
return when {
this.foo == other.foo -> 0
this.foo > other.foo -> 1
else -> -1
}
}
But I've also seen extension functions overriding the compareTo operator.
operator fun A.compareTo(d: Double): Int {
return when {
this.foo == d -> 0
this.foo > d -> 1
else -> -1
}
}
What is the difference between these and what should I be using? I'm guessing if I want to compare it to multiple types then I have to use extension functions?
The Comparable interface comes from Java, and, as you have seen, is defined with only a compareTo( other) method, which only provides for comparing an object to another object of the same type.
As you have also noticed, the Kotlin extension functions are additional functions allowing you to compare an object to whatever you want, as long as you define the compareTo method to take an argument of the type to which you want to compare.
So, yes, if you want to compare an object to an object of a different type, you will need to write an appropriate extension function.
Of course, in Java, if you have control of the source code of the class, you can always add a custom compareTo method.
Comparable is a standard interface, it's the way you define a class as having some ordering, and every library that deals with ordering works with Comparable types. Basically, if you want to be able to order and compare your things using all the standard functions and anything anyone else might write, you need to implement the Comparable interface.
This works:
data class SportsTeam(val name: String) : Comparable<SportsTeam> {
override fun compareTo(other: SportsTeam): Int = when {
name == "best team" -> 1
other.name == "best team" -> -1
else -> 0
}
}
fun main(args: Array<String>) {
val best = SportsTeam("best team")
val worst = SportsTeam("worst team")
print("The winner is: ${maxOf(best, worst).name}")
}
but because maxOf takes a Comparable type, this won't work:
data class SportsTeam(val name: String)
fun SportsTeam.compareTo(other: SportsTeam): Int = when {
name == "best team" -> 1
other.name == "best team" -> -1
else -> 0
}
fun main(args: Array<String>) {
val best = SportsTeam("best team")
val worst = SportsTeam("worst team")
print("The winner is: ${maxOf(best, worst).name}")
}
Wenn you make your own objects you must implement Comparable interface and then override compareTo function
class MyClass : Comparable<MyClass> {
override fun compareTo(other: MyClass): Int {
// TODO Returns zero if this object is equal to the specified other object
}
}
You can also override an operator function, for example from Int class in kotlin
fun main(args: Array<String>) {
val a = 1
val b = "2"
println(a.compareTo(b))
}
operator fun Int.compareTo(i: String) : Int {
return if (this.toString() == i) {
0
} else {
1
}
}
I hope that's helpfull from you

loop over BigInteger values using foreach in Kotlin

I am trying to loop over BigInteger values in Kotlin using the following code snippet. But it's telling For-loop range must have an 'iterator()' method. How can I loop over the BigInteger values in Kotlin?
private fun pow(base: BigInteger, power: BigInteger): String {
for(i in BigInteger.ZERO..power){ //Giving error
}
}
You can extend BigInteger to allow this
In particular we need to:
introduce rangeTo function to BigInteger (to allow using .. operator)
add iterator function to the range returned by rangeTo operator
The rangeTo function
Here I'm defining an extension function for BigInteger
operator fun BigInteger.rangeTo(other: BigInteger) =
BigIntegerRange(this, other)
BigIntegerRange:
class BigIntegerRange(
override val start: BigInteger,
override val endInclusive: BigInteger
) : ClosedRange<BigInteger>, Iterable<BigInteger> {
override operator fun iterator(): Iterator<BigInteger> =
BigIntegerRangeIterator(this)
}
BigIntegerRangeIterator:
class BigIntegerRangeIterator(
private val range: ClosedRange<BigInteger>
) : Iterator<BigInteger> {
private var current = range.start
override fun hasNext(): Boolean =
current <= range.endInclusive
override fun next(): BigInteger {
if (!hasNext()) {
throw NoSuchElementException()
}
return current++
}
}
Now this code:
fun main() {
for (i in BigInteger.ZERO..BigInteger.TEN) {
println(i)
}
}
Compiles and prints:
0
1
2
3
4
5
6
7
8
9
10
Do not forget to import the rangeTo function
See also:
Ranges
Control Flow. For Loops

Why Kotlin doesn't have a Decimal Progression?

Recently I faced with a problem iterating through decimal numbers with a decimal step and I was wondered, why Kotlin has Progressions only for Int, Long and Char.
I understand, that there could be some caveats with decimal numbers. But still. We just want to have a start BigDecimal number, end BigDecimal number and then iterate through them with BigDecimal step.
Q: So, why there is no any progressions for not integer numbers? Thank you.
P.S.: Here is a sample code of possible implementation (I took sources for Int and adapted to BigDecimal):
/**
* Returns a progression that goes over the same range with the given step.
*/
public infix fun BigDecimalProgression.step(step: BigDecimal): BigDecimalProgression {
if (step <= java.math.BigDecimal.ZERO) throw IllegalArgumentException("Step must be positive, was: $step.")
return BigDecimalProgression.fromClosedRange(first, last, if (this.step > java.math.BigDecimal.ZERO) step else -step)
}
/**
* A progression of values of type `BigDecimal`.
*/
public open class BigDecimalProgression
internal constructor
(
start: BigDecimal,
endInclusive: BigDecimal,
step: BigDecimal
) : Iterable<BigDecimal> {
init {
if (step == BigDecimal.ZERO) throw kotlin.IllegalArgumentException("Step must be non-zero")
}
/**
* The first element in the progression.
*/
public val first: BigDecimal = start
/**
* The last element in the progression.
*/
public val last: BigDecimal = getProgressionLastElement(start, endInclusive, step)
/**
* The step of the progression.
*/
public val step: BigDecimal = step
override fun iterator(): BigDecimalIterator = BigDecimalProgressionIterator(first, last, step)
/** Checks if the progression is empty. */
public open fun isEmpty(): Boolean = if (step > BigDecimal.ZERO) first > last else first < last
override fun equals(other: Any?): Boolean =
other is BigDecimalProgression && (isEmpty() && other.isEmpty() ||
first == other.first && last == other.last && step == other.step)
override fun hashCode(): Int =
if (isEmpty()) -1 else (31 * (31 * first.hashCode() + last.hashCode()) + step.hashCode())
override fun toString(): String = if (step > BigDecimal.ZERO) "$first..$last step $step" else "$first downTo $last step ${-step}"
companion object {
/**
* Creates BigDecimalProgression within the specified bounds of a closed range.
* The progression starts with the [rangeStart] value and goes toward the [rangeEnd] value not excluding it, with the specified [step].
* In order to go backwards the [step] must be negative.
*/
public fun fromClosedRange(rangeStart: BigDecimal, rangeEnd: BigDecimal, step: BigDecimal): BigDecimalProgression = BigDecimalProgression(rangeStart, rangeEnd, step)
}
}
fun getProgressionLastElement(start: BigDecimal, end: BigDecimal, step: BigDecimal): BigDecimal {
if (step > BigDecimal.ZERO) {
return start + BigDecimal(((end - start) / step).toInt()) * step
} else if (step < BigDecimal.ZERO) {
return start - BigDecimal(((start - end) / -step).toInt()) * -step
} else {
throw kotlin.IllegalArgumentException("Step is zero.")
}
}
/** An iterator over a sequence of values of type `BigDecimal`. */
public abstract class BigDecimalIterator : Iterator<BigDecimal> {
override final fun next() = nextBigDecimal()
/** Returns the next value in the sequence without boxing. */
public abstract fun nextBigDecimal(): BigDecimal
}
/**
* An iterator over a progression of values of type `BigDecimal`.
* #property step the number by which the value is incremented on each step.
*/
internal class BigDecimalProgressionIterator(first: BigDecimal, last: BigDecimal, val step: BigDecimal) : BigDecimalIterator() {
private val finalElement = last
private var hasNext: Boolean = if (step > BigDecimal.ZERO) first <= last else first >= last
private var next = if (hasNext) first else finalElement
override fun hasNext(): Boolean = hasNext
override fun nextBigDecimal(): BigDecimal {
val value = next
if (value >= finalElement) {
if (!hasNext) throw kotlin.NoSuchElementException()
hasNext = false
}
else {
next += step
}
return value
}
}
As it said in the documentation for ranges:
Floating point numbers (Double, Float) do not define their rangeTo
operator, and the one provided by the standard library for generic
Comparable types is used instead:
public operator fun <T: Comparable<T>> T.rangeTo(that: T): ClosedRange<T>
The range returned by this function cannot be used for iteration. You
will have to use some other kind of loop since you can't use ranges.
They simply do not define.