Kotlin - same condition: multiple if statements or one if statement - kotlin

In Kotlin you can use if statements kind of like ternary operators.
We have the option to do something like this:
val x = if (isOdd) 1 else 2
but if we have multiple variables that need to be set based on some condition is it more correct to do it the old fashioned way like so:
val x: Int
val y: Int
val z: Int
if (isOdd) {
x = 1
y = 3
z = 5
} else {
x = 2
y = 4
z = 6
}
or like this :
val x = if (isOdd) 1 else 2
val y = if (isOdd) 3 else 4
val z = if (isOdd) 5 else 6
the second way looks much cleaner to me, but I'd like to know if the first method would be computed faster since it only needs to calculate the condition once whereas the second way needs to check the condition 3 times.
Is the second way actually slower or will it be optimized by the compiler?

I'd prefer something like this, looks way more Kotlinesque:
data class Point3D(val x: Int, val y: Int, val z: Int)
fun foo(isOdd: Boolean): Point3D = if (isOdd) Point3D(1, 3, 5) else Point3D(2, 4, 6)
//or using destructureing see https://kotlinlang.org/docs/reference/multi-declarations.html)
val (x,y,z) = if (isOdd) Triple(1, 3, 5) else Triple(2, 4, 6)
Also it combines the best of both, using if as expression and only one if is needed. (At the cost of an additional object allocation).
But to answer your question. Do what you like and think is most readable. Performance wise I doubt you will make a difference.

if is an expression in Kotlin, not a statement: it returns a value, whereas it doesn't in Java's case.
I don't think here is such an optimization issue you should ever think about, honestly. Premature optimization is a common source of problems. If this boolean variable is thread-confined, then I think the compiler will perform all the optimizations that are possible in this context, so it will be almost no overhead at all (if not completely).
Wise choice in OO languages is to prefer clearness and flexibility over low-level optimization issues (especially when compilers are able to resolve them).

Okay, so just saw this question again and got curious... So I did some tests.
Turns out there is actually a HUGE difference, heres the results:
Code
fun main() {
for (i in 0 until 3) {
val t1_s = System.currentTimeMillis()
for (j in 0 until 100000) {
when (i){
0 -> a(j % 2 == 0)
1 -> b(j % 2 == 0)
2 -> c(j % 2 == 0)
}
}
val t1_e = System.currentTimeMillis()
println("Test $i - time ${t1_e - t1_s}")
}
}
fun a(isOdd: Boolean): Int {
val x: Int
val y: Int
val z: Int
if (isOdd) {
x = 1
y = 3
z = 5
} else {
x = 2
y = 4
z = 6
}
return x + y + z
}
fun b(isOdd: Boolean): Int {
val x = if (isOdd) 1 else 2
val y = if (isOdd) 3 else 4
val z = if (isOdd) 5 else 6
return x + y + z
}
fun c(isOdd: Boolean): Int {
val (x,y,z) = if (isOdd) Triple(1, 3, 5) else Triple(2, 4, 6)
return x + y + z
}
Output
Test 0 - time 3
Test 1 - time 1
Test 2 - time 8
It seems my second solution is the fastest, my first suggestion next, and the top answer as MUCH slower.
Does any one know why this might be? Obviously these are milliseconds so it almost always wouldn't matter, but it is neat to think that one method is 5-10 times faster
EDIT:
So tried bumptin the iterations up to 100000000 and the results were:
Test 0 - time 6
Test 1 - time 41
Test 2 - time 941
I Guess the first 2 options are getting optimized out but the third option is always creating a new object making it much slow
Try it online!

Related

Axiomatic Semantics - How to calculate a weakest precondition of a program

Assuming the post-condition, how can I compute the weakest pre-condition of a program containing two statements?
For example :
a=x;
y = 0
{x = y + a}
Another example:
y = x;
y = x + x + y
{y = 3x ^ z> 0}
I tried to solve them but both questions resulted in pre-conditions or post-condition that are identical to the statement and I don't know if this is valid.
for example, the precondition of the last statement is "y=x" , thus it is the post condition of the preceding statement which is " y=x" as well
You can apply the rules of Hoare Logic here. Specifically, for the examples you have, you only need the rule for assignment:
{ P[E/x] } x = E { P }
Here, P[E/x] means take P and substitute (i.e. replace) all occurrences of x with E. For example, if P is x == 0 then P[0/x] gives 0 == 0.
To calculate the weakest precondition, you start from the end and work backwards. For your first example, we start with the last statement:
{ ??? } y = 0 { x == y + a }
The goal is to determine something suitable for ???. Applying our rule for assignment above, we can see that this is a solution:
{ x == 0 + a } y = 0 { x == y + a }
We can further simplify this to { x == a }. Then, we move on to address the statement before y = 0, and so on.

Kotlin Calculating with BigDecimal vs Double

I have 2 Functions. One uses BigInteger and BigDecimal. I want to calculate sin(z) using the Taylor series:
Here is my code:
fun sinus(z: BigDecimal, upperBound: Int = 100): BigDecimal = calcSin(z, upperBound)
fun cosinus(z: BigDecimal, upperBound: Int = 100): BigDecimal = calcSin(z, upperBound, false)
fun calcSin(z: BigDecimal, upperBound: Int = 100, isSin: Boolean = true): BigDecimal {
var erg: BigDecimal = BigDecimal.ZERO
for (n in 0..upperBound) {
// val zaehler = (-1.0).pow(n).toBigDecimal() * z.pow(2 * n + (if (isSin) 1 else 0))
// val nenner = fac(2 * n + (if (isSin) 1 else 0)).toBigDecimal()
val zaehler = (-1.0).pow(n).toBigDecimal() * z.pow(2 * n + 1)
val nenner = fac(2 * n + 1).toBigDecimal()
erg += (zaehler / nenner)
}
return erg
}
fun calcSin(z: Double, upperBound: Int = 100): Double {
var res = 0.0
for (n in 0..upperBound) {
val zaehler = (-1.0).pow(n) * z.pow(2 * n + 1)
val nenner = fac(2 * n + 1, true)
res += (zaehler / nenner)
}
return res
}
fun fac(n: Int): BigInteger = if (n == 0 || n == 1) BigInteger.ONE else n.toBigInteger() * fac(n - 1)
fun fac(n: Int, dummy: Boolean): Double = if (n == 0 || n == 1) 1.0 else n.toDouble() * fac(n - 1, dummy)
According to Google, Sin(1) is
0.8414709848
The Output of the following is however:
println("Sinus 1: ${sinus(1.0.toBigDecimal())}")
println("Sinus 1: ${sinus(1.0.toBigDecimal()).toDouble()}")
println("Sinus 1: ${sinus(1.0.toBigDecimal(), 1000)}")
println("Sinus 1: ${calcSin(1.0)}")
Output:
Sinus 1: 0.8414373208078281027995610599000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Sinus 1: 0.8414373208078281
Sinus 1: 0.8414373208078281027995610599000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Sinus 1: 0.8414709848078965
Wha am I missing? Why does the Double-Variant gives the correct value, while The BigDecimal doesn't? Even with 1000 Iterations.
The commented out code was meant for calculation Cos as well, but wanted to figure out that Problem first, so i made both Functions look the same
In the BigDecimal variant, try replacing erg += (zaehler / nenner) with erg += (zaehler.divide(nenner, 20, RoundingMode.HALF_EVEN))
I suspect that the defaults for scaling the division results (as described here https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/math/BigDecimal.html) are not what you want.
BTW - I assume that performance is not part of the exercise, otherwise your implementation of factorial is a low hanging fruit.

Find the first element in a list that verify a condition

Assuming we are given a list of integers R = [3,5,3,6,0,6,7], an threshold x (integer) and a window size (integer) p. For example, x=4 and p = 2.
I need to find the first index t that verifies the the following conditions:
R[t] >= 4, R[t+1] >= 4. Since p=2, we need to only verify for two boxes t and t+1. If p was equal to 3 we will need to verify for t, t+1 and t+2.
Here the t I am looking for is 5 (indexing is starting from 0).
How to write this in a elegant way in Kotlin (rather than looping on the elements).
A tentative that is giving an error (x=4 and p = 2. The output should be 3 since we start indexing by 0):
val numbers = listOf(1, 2, 3, 4, 6, 8, 2)
val firstIndex = numbers.find { it >= 4 for it in it..it+2-1}
val numbers = listOf(1, 2, 3, 4, 6, 8, 2)
val p = 2
val x = 4
val t = numbers.windowed(p).indexOfFirst { window -> window.all { it >= x } } // t == 3
t will be equal to -1 in case if no matches will be found
Use windowed to check groups of values for each index in the list. Use withIndex() so you are iterating with the indices, which you need in your final result. Then use firstOrNull() (which find() is a redundant alias of). And finally, take ?.index to get the index of the first entry that satisfies the condition, or null if none satisfy.
val x = 4
val p = 3
val list = listOf(2,5,3,6,0,6,7)
val t = list
.windowed(p)
.withIndex()
.firstOrNull { (_, sublist) -> sublist.all { it >= x } }
?.index
find Returns the first element matching the given predicate, or null if no such element was found.
If I've understood correctly, this should work:
fun main() {
val list = listOf(3,5,3,6,0,6,7)
val p = 2
val x = 4
val t = list.withIndex().windowed(p).firstOrNull() { window ->
window.all { it.value >= x }
}?.first()?.index
println(t)
}
Output:
5

When statement kotlin greater than or equals

In kotlin when statement I can do in 2..4 which is equivalent to >= 2 and <= 4.
How do I just look for >= 2? I tried an infinite range: in 2.., but this doesn't seem to be a thing.
I also tried just putting in >= 2, but no luck either.
How do I do this in kotlin? Or do I have to switch to an if statement?
Should be
in 2..Integer.MAX_VALUE
Assumed your values are integers, as you are looking for an iterable range.
What was the problem with >=?
This works fine:
val x = 3
val y = when {
x + 1 >= 4 -> "one"
x + 1 < 4 -> "two"
else -> "else"
}
println(y)

Using groupBy/groupingBy/aggregate to sum into smaller buckets in parallel?

I've got a collection of "stuff", and I'd like to sum it into smaller buckets. (In my particular case, I'm downsampling a luma channel of an image by 8x.)
I'd like it to be as fast as possible on your average multi-core android device, which I think means coroutine-per-bucket. (because there isn't any reason to play with IntAdders if I don't have to)
The naive linear solution:
val SCALE = 8
image.planes[0].buffer.toByteArray().forEachIndexed { index, byte ->
val x1 = index % image.width
val y1 = index / image.width
val x2 = x1 / SCALE
val y2 = y1 / SCALE
val quadIdx = y2 * (image.width / SCALE) + x2
summedQuadLum[quadIdx] += (byte.toInt() and 0xFF)
}
That isn't great - needs to pre-declare the summedQuadLum collection, and doesn't have any chance of parallel work.
I'd love to use groupBy, or groupingBy? or aggregate?) but those all seem to use the values to determine the new keys, and I need to use the key to determine the new keys. I think the least overhead is withIndex which could be done as
val thumbSums = bufferArray.withIndex().groupingBy { (idx, _) ->
val x1 = idx % previewImageDimension.width
val y1 = idx / previewImageDimension.width
val x2 = x1 / SCALE
val y2 = y1 / SCALE
y2 * (previewImageDimension.width / SCALE) + x2
}.aggregate { _, acc: Int?, (_, lum), _ ->
(acc ?: 0) + (lum.toInt() and 0xFF)
}.values.toIntArray()
Much better, it is really close - if I could figure out how to sum each bucket in a coroutine, I think it would be as good as can be expected.
So after groupingBy we have a Grouping object, which we can use to aggregate values. It's important to notice the grouping itself has not been done yet, we basically have a description how to group the values and an iterator of the original array. From here we have a few options:
Create a Channel from the iterator and launch a few worker coroutines to consume it in parallel. Channels support fan-out, so every item in the source is processed by one worker only. The problem here is all the workers need to update different items in the resulting array, so synchronization is required and that's where it gets tricky and likely inefficient.
To avoid multiple workers to write to the same item, we need to tell each of them what items to process. That mean either each of the worker should process all the items, picking only suitable or we should precalculate the groups in advance and feed the workers with the groups. Both approaches have pretty much the same performance as the serial algorithm, so do not make any sense.
So to parallelize it efficiently we want to avoid having a shared mutable state, because it requires synchronization. Obviously we don't want to precalculate the groups also.
My suggestion here is to come from another side - instead of mapping original array to sampled one, let's map sampled array to the original. So we say
This approaches makes each value to be calculated independently by one worker, so no synchronization needed. Now we can implement it like this:
suspend fun sample() {
val asyncFactor = 8
val src = Image(bufferArray, width)
val dst = Image(src.width / SCALE, src.height / SCALE)
val chunkSize = dst.sizeBytes / asyncFactor
val jobs = Array(asyncFactor) { idx ->
async(Dispatchers.Default) {
val chunkStartIdx = chunkSize * idx
val chunkEndIdxExclusive = min(chunkStartIdx + chunkSize, dst.sizeBytes)
calculateSampledImageForIndexes(src, dst, chunkStartIdx, chunkEndIdxExclusive, SCALE)
}
}
awaitAll(*jobs)
}
private fun calculateSampledImageForIndexes(src: Image, dst: Image, startIdx: Int, exclusiveEndIdx: Int, scaleFactor: Int) {
for (i in startIdx until exclusiveEndIdx) {
val destX = i % dst.width
val destY = i / dst.width
val srcX = destX * scaleFactor
val srcY = destY * scaleFactor
var sum = 0
for (xi in 0 until scaleFactor) {
for (yi in 0 until scaleFactor) {
sum += src[srcX + xi, srcY + yi]
}
}
dst[destX, destY] = sum / (scaleFactor * scaleFactor)
}
}
Where Image is a convenient wrapper around the image data buffer:
class Image(val buffer: ByteArray, val width: Int) {
val height = buffer.size / width
val sizeBytes get() = buffer.size
constructor(w: Int, h: Int) : this(ByteArray(w * h), w)
operator fun get(x: Int, y: Int): Byte = buffer[clampX(x) * width + clampY(y)]
operator fun set(x: Int, y: Int, value: Int) {
buffer[x * width + y] = (value and 0xFF).toByte()
}
private fun clampX(x: Int) = max(min(x, width), 0)
private fun clampY(y: Int) = max(min(y, height), 0)
}
Also, with this approach you can easily implement many image processing functions, which based on convolution operation, like blur and edge detection.