Kotlin: Dynamically choose companion object function - kotlin

My Enum class and companion object are as follows:
enum class Temperature(val names: List<String>) {
Celsius(listOf("degree Celsius", "degrees Celsius", "celsius", "dc", "c")),
Kelvin(listOf("Kelvin", "Kelvins", "k")),
Fahrenheit(listOf("degree Fahrenheit", "degrees Fahrenheit", "fahrenheit", "df", "f"));
companion object Conversion {
fun CelsiusToKelvin(c: Double) = c + 273.15
fun CelsiusToFahrenheit(c: Double) = (c * 9 / 5) + 32
fun FahrenheitToKelvin(f: Double) = (f + 459.67) * 5 / 9
fun FahrenheitToCelsius(f: Double) = (f - 32) * 5 / 9
fun KelvinToCelsius(K: Double) = K - 273.15
fun KelvinToFahrenheit(K: Double) = K * 9 / 5 - 459.67
}
}
I get input temperature in a scale which I need to convert to a different output scale. Is there a way to dynamically call the companion object functions based on the input and output scales?
For example:
var convert = "CelsiusToKelvin"
val value = 36.9
val myFunc = Temperature.getFunction(convert)
val myOutput = myFunc(value)
This should invoke the Temperature.CelsiusToKelvin function.
I know that I can check the input using if or when statements and manually invoke the function I want. But is it possible without doing this?
Edit 1:
I solved it using when by changing the function:
companion object Conversion {
fun convert(a:String,b:String,c:Double) = when {
a == "Celsius" && b == "Kelvin" -> c + 273.15
a == "Celsius" && b == "Fahrenheit" -> (c * 9 / 5) + 32
a == "Fahrenheit" && b == "Kelvin" -> (c + 459.67) * 5 / 9
a == "Fahrenheit" && b == "Celsius" -> (c - 32) * 5 / 9
a == "Kelvin" && b == "Celsius" -> c - 273.15
a == "Kelvin" && b == "Fahrenheit" -> c * 9 / 5 - 459.67
else -> 0.0
}
}
But I want to know if what I originally wanted to do was possible

I think a better approach, if you must use Strings, is to dynamically determine the constant, and have each constant provide the necessary conversion functions. For example:
// Note: Didn't include your "names" property
enum class Temperature {
Celsius {
override fun toKelvin(value: Double) = value + 273.15
override fun toFahrenheit(value: Double) = (value * 9.0 / 5.0) + 32.0
},
Kelvin {
override fun toCelsius(value: Double) = value - 273.15
override fun toFahrenheit(value: Double) = value * 9.0 / 5.0 - 459.67
},
Fahrenheit {
override fun toCelsius(value: Double) = (value - 32.0) * 5.0 / 9.0
override fun toKelvin(value: Double) = (value + 459.67) * 5.0 / 9.0
};
open fun toCelsius(value: Double) = value
open fun toKelvin(value: Double) = value
open fun toFahrenheit(value: Double) = value
fun convert(value: Double, target: Temperature) = when (target) {
Celsius -> toCelsius(value)
Kelvin -> toKelvin(value)
Fahrenheit -> toFahrenheit(value)
}
companion object {
fun convert(source: String, target: String, value: Double) =
valueOf(source).convert(value, valueOf(target))
}
}
The valueOf method, which is added implicitly by the compiler, will throw an IllegalArgumentException if the string does not match the name of an enum constant exactly. Your code has a similar requirement except you return 0 if there's no match. I personally prefer the exception.

Related

Kotlin infix function with generics

Try to write a simple Kotlin infix function for plus operation. What's wrong with generic type?
infix fun <T : Number> T.myPlus(that: T): T = this + that
As others have mentioned, there's no solutions using generics for various reasons. You have to define an extension function for each Number type (Byte, Short, Int, Long, Float, Double). E.g. for Int you could do:
when (that) {
is Byte, is Short, is Int, is Long -> that.toLong().plus(this)
is Float -> that + this
is Double -> that + this
else -> throw Exception("Types mismatch")
}
Even doing that, in some cases you need to decide whether you want to truncate or round the result.
val n1 = 1230000000000000000L
val n2 = 123.7
This case (n1 is a Long, n2 is a Float) could be handled like this:
is Float -> this + that.toLong()
resulting in 1230000000000000123
or it could be handled like this:
is Float -> this.toFloat() + that
resulting in 1.23E18
You forgot the operator keyword:
infix operator fun T.plus(that: T): T = this + that
Edit:
infix fun Number.infixPlus(that: Number): Number =
when (that) {
is Int -> this.toInt() + that
is Long -> this.toLong() + that
is Float -> this.toFloat() + that
is Double -> this.toDouble() + that
else -> throw Exception()
}
val n1 = 123
val n2 = 123.456
val result = n1 infixPlus n2
println("result: " + result)
println("is Int: " + (result is Int))
println("is Long: " + (result is Long))
println("is Float: " + (result is Float))
println("is Double: " + (result is Double))
A bit more casting safety:
infix fun Number.infixPlus(that: Number): Number {
return when {
this is Int && that is Int -> this + that
this is Double && that is Double -> this + that
this is Float && that is Float -> this + that
else -> throw Exception("Types mismatch")
}
}
In prev. example args:
val n1 = 1230000000000000000L
val n2 = 123
val result = n1 infixPlus n2
result: -1313144709
No exception and wrong result.
You have to find the correct type of the result:
val types = listOf("Double", "Float", "Long", "Integer", "Short", "Byte")
infix fun <T : Number> T.myPlus(that: T): T {
return when(types[min(types.indexOf(this.javaClass.simpleName), types.indexOf(that.javaClass.simpleName))]) {
types[0] -> (this.toDouble() + that.toDouble()) as T
types[1] -> (this.toFloat() + that.toFloat()) as T
types[2] -> (this.toLong() + that.toLong()) as T
types[3] -> (this.toInt() + that.toInt()) as T
types[4] -> (this.toShort() + that.toShort()) as T
types[5] -> (this.toByte() + that.toByte()) as T
else -> throw IllegalArgumentException()
}
}

How can I check if a set of functions all return non null, in a single expression?

Suppose I have three functions foo, bar, baz, all of which return nullable types.
fun foo(): Int? = 1
fun bar(): Int? = 2
fun baz(): Int? = 3
I want to call them, and if all them returns non-null, I want to compute a value from their return values.
I could do this with statements, like this:
val x = foo()
val y = bar()
val z = baz()
val result = if (x != null && y != null && z != null) x + y + z else null
However, I don't like the fact that I have to declare 3 extra variables that I can still access afterwards. By having 3 extra statements like this, it also means that I cannot use expression-bodied functions, if I were writing a function that returns result.
If I use lets instead:
val result = foo()?.let { x ->
bar()?.let { y ->
baz()?.let { z ->
x + y + z
}
}
}
This creates a deep nesting. If it were only one function, this would have been fine, but with 3 functions or more, this makes my intention of "call these three functions, if they are all non null, add them together" rather unclear.
How can I write this in a way that clearly conveys my intention, but also making it a single expression?
If they are of different types, I think you need to write your own helper functions like these (different overloads needed for different numbers of parameters, because there's no other way for the compiler to know the types of the arguments):
inline fun <T : Any, U : Any, R> ifAllNotNull(t: T?, u: U?, block: (t: T, u: U) -> R): R? {
return when {
t != null && u != null -> block(t, u)
else -> null
}
}
inline fun <T : Any, U : Any, V : Any, R> ifAllNotNull(t: T?, u: U?, v: V?, block: (t: T, u: U, v: V) -> R): R? {
return when {
t != null && u != null && v != null -> block(t, u, v)
else -> null
}
}
val result = ifAllNotNull(foo(), bar(), baz()) { x, y, z -> x + y + z }
Note that all three parameters will be evaluated before any are checked for null.
Or if you want to do what you described (hiding the three variables after the result calculation) using just standard library functions, you can use run to limit the scope of the temporary variables:
val result = run {
val x = foo()
val y = bar()
val z = baz()
if (x != null && y != null && z != null) x + y + z else null
}
This would also give you the opportunity to short-circuit if you like:
val result = run {
val x = foo() ?: return#run null
val y = bar() ?: return#run null
val z = baz() ?: return#run null
x + y + z
}
You could filter out all null-values and only apply an operation on the list, if it did not shrink in size, e.g.:
fun sumIfNoneNull(values: List<Int?>): Int? = values
.filterNotNull()
.takeIf { it.size == values.size }
?.sum()
One may generalize this further, e.g.:
fun <T, R> List<T>.foldIfNoneNull(
initial: R,
operation: (acc: R, T) -> R
): R? = this
.filterNotNull()
.takeIf { nonNullList -> nonNullList.size == this.size }
?.fold(initial, operation)
You may use this like any other fold, e.g.:
listOf(foo(), bar(), baz()).foldIfNoneNull(0) { acc, cur -> acc + cur }
val result = listOf(foo(), bar(), baz())
.reduce { acc, i ->
when {
acc == null || i == null -> null
else -> acc + i
}
}
Or as function:
fun <T> apply(operation: (T, T) -> T, vararg values: T?): T? {
return values
.reduce { acc, i ->
when {
acc == null || i == null -> null
else -> operation(acc, i)
}
}
}
val result = apply({ x, y -> x + y }, foo(), bar(), baz())

How to explain implementation in Kotlin

I am really new in Kotlin programming language. I work with generic types and I found code statements shown below. What does it means? What is the meaning of each line?
class s(val b: t) : t by b {
fun f1(): Int = b.f3 + 1
override fun f2(g: Int): Any? = when {
g == 0 -> null
else -> b.f2(g - 1)
}
override fun l(g: Int, h: k?, z: m):
k = when {
g == 0 -> z.f1.f2.f3(1, z, true)
else -> b.l(g - 1, h, z)}}
override fun f4 (g: Int): Short = when {
g == 0 -> 0
else -> b.f4(g - 1)}}}

Ranges in Kotlin

How do I write code to return a range of 10 numbers from a given number.
i.e if I am given 5, code should return 0..9
358 should return 350..359
33 should return 30..39 etc
Do you mean something like this?
fun range10(contained: Int): IntRange {
val start = contained - contained % 10
val end = start + 9
return start..end
}
If the given number is integer type, you can simply write
val x = 358
(x / 10 * 10)..(x / 10 * 10 + 9)
You can use the following code:
fun answer(givenNum: Int) : IntRange {
val startOfRange = givenNum - (givenNum % 10)
return startOfRange until (startOfRange + 10)
}
fun main() {
val givenNum = 33
println(answer(givenNum)) // 30..39
}

Elementary functions in kotlin

Looking for a way to get elementary functions and their derivatives I approach it like this:
abstract class Fun() {
/**
* i = 0 -- the function itself,
* i = 1, 2, 3, ... -- its successive derivatives
*/
abstract fun d(i: Int, x: Float): Float
}
class Lin(val k: Float) : Fun() {
// y = k*x
override fun d(i: Int, x: Float, p: Float) = when (i) {
0 -> k * x
1 -> k
else -> 0.0f
}
}
class Sum(val fun0: Fun, val fun1: Fun) : Fun() {
// y = fun0(x) + fun1(x)
override fun d(i: Int, x: Float, p: Float) = fun0.d(i, x) + fun1.d(i, x)
}
class Example(val fun1: Fun, val fun2: Fun){
var res = fun1.d(0, 5.25f) // fun1 value at 5.25f
res = fun1.d(1, 3.29f) // fun1 first derivative at 3.29f
val sum = Sum(fun1, fun2) // sum of f1 and f2
res = sum(0, 3.78f) // sum value at 3.78f
res = sum(1, 5.69f) // sum first derivative at 5.69f
}
Is there a more idiomatic way to do it in Kotlin?
I have exposed the problem as I had done in Java, that is, classes that contain functions. My question is if I can do the same with functions, pass them on to a class like:
class ParametricCurveXYZ(val fun_x: Fun, val fun_y: Fun, val fun_z: Fun) {
fun pointToXYZ(s: Float) = VectorXYZ(fun_x.d(0, s), fun_y.d(0, s), fun_z.d(0, s))
fun tangent(s: Float) = VectorXYZ(fun_x.d(1, s), fun_y.d(1, s), fun_z.d(1, s)).normalized()
}
You can use lambdas instead of regular classes and overload operators to combine lambdas.
fun lin(k: Float) = { i: Int, x: Float ->
when (i) {
0 -> k * x
1 -> k
else -> 0.0f
}
}
operator fun ((Int, Float) -> Float).plus(that: (Int, Float) -> Float) =
{ i: Int, x: Float -> this(i, x) + that(i, x) }
fun doSomething() {
val sum = lin(1f) + lin(2f)
val res = sum(0, 3.78f)
}