Required and one-of-more idioms - kotlin

Kotlin DSL support is great, but I ran into two scenarios I can only add workaround. Both workaround has its major drawback as they enforce constraints only at execution time.
First constraint: required parameter
I would like to write something like this:
start {
position {
random {
rect(49, 46, 49, 47)
rect(50, 47, 51, 48)
point(51, 49)
}
}
}
where position is a required parameter. My approach is to set the position to null at startup and checking it when building the start object.
Second constraint: one of many
I would like to allow exactly one of several possible sub objects:
start {
position {
random {
[parameters of random assign]
}
}
}
or
start {
position {
user {
[parameters of user assign]
}
}
}
I have a feeling that I reached the edge of possibilities of the Kotlin DSL toolkit, because this requirements are also only compile time validated in the core language as well.
Any idea?

You can take inspiration from Kotlin own HTML DSL. For mandatory arguments use simple functions with arguments, not function literal with a receiver.
Your DSL will look something like this:
start(
position {// This is mandatory
random {// This is not
}
}
)
And your start builder:
fun start(position: Position): Start {
val start = Start(position)
...
return start
}
Use same approach for position().

After some thought of the problem, I realized, that these two requirements can't be solved in Kotlin itself, therefore no pure syntactical solution is possible in the current form introduced above. However, there are a few options which may produce close enough syntax and addresses one or both problems at the same time.
Option 1: Parameters
This solution is quite simple and ugly, adding the awful "where-is-the-closing-parenthesis" anomaly. It simply moves the position property into constructor:
start(random {
rect(49, 46, 49, 47)
rect(50, 47, 51, 48)
point(51, 49)
}) {
windDirection to NORTH
boat turn (BEAM_REACH at STARBOARD)
}
This is simple in code:
fun start(pos : StartPosition, op: StartConfigBuilder.() -> Unit) : StartConfigBuilder
= StartConfigBuilder(pos).apply(op)
and creates top level builder functions for the position implementations:
fun random( op : RandomStartPositionBuilder.() -> Unit) = RandomStartPositionBuilder().apply(op).build()
class RandomStartPositionBuilder {
private val startZoneAreas = mutableListOf<Area>()
fun rect(startRow: Int, startColumn: Int, endRow: Int = startRow, endColumn: Int) =
startZoneAreas.add(Area(startRow, startColumn, endRow, endColumn))
fun point(row: Int, column: Int) = startZoneAreas.add(Area(row, column))
fun build() = RandomStartPosition(if (startZoneAreas.isEmpty()) null else Zone(startZoneAreas))
}
fun user( op : UserStartPositionBuilder.() -> Unit) = UserStartPositionBuilder().apply(op).build()
class UserStartPositionBuilder {
fun build() = UserStartPosition()
}
Although this solves both required and only one problems on edit time, makes the DSL much harder to read and we loose the elegance of the DSL tools. It will become even more messy if more than one properties have to be moved into the constructor or as the internal object (position) becomes more complicated.
Option 2: Infix function
This solution moves the required complex field outside the block (this is the "nasty" part) and uses it as an infix function:
start {
windDirection to NORTH
boat turn (BEAM_REACH at STARBOARD)
} position random {
rect(49, 46, 49, 47)
rect(50, 47, 51, 48)
point(51, 49)
}
or
start {
windDirection to NORTH
boat turn (BEAM_REACH at STARBOARD)
} position user {
}
This solution solves the "only one" problem, but not the "exactly one".
To achieve this, I modified the builders:
//Note, that the return value is the builder: at the end, we should call build() later progmatically
fun start(op: StartConfigBuilder.() -> Unit) : StartConfigBuilder = StartConfigBuilder().apply(op)
class StartConfigBuilder {
private var position: StartPosition = DEFAULT_START_POSITION
private var windDirectionVal: InitialWindDirection = RandomInitialWindDirection()
val windDirection = InitialWindDirectionBuilder()
val boat = InitialHeadingBuilder()
infix fun position(pos : StartPosition) : StartConfigBuilder {
position = pos
return this
}
fun build() = StartConfig(position, windDirection.value, boat.get())
}
// I have to move the factory function top level
fun random( op : RandomStartPositionBuilder.() -> Unit) = RandomStartPositionBuilder().apply(op).build()
class RandomStartPositionBuilder {
private val startZoneAreas = mutableListOf<Area>()
fun rect(startRow: Int, startColumn: Int, endRow: Int = startRow, endColumn: Int) =
startZoneAreas.add(Area(startRow, startColumn, endRow, endColumn))
fun point(row: Int, column: Int) = startZoneAreas.add(Area(row, column))
fun build() = RandomStartPosition(if (startZoneAreas.isEmpty()) null else Zone(startZoneAreas))
}
// Another implementation
fun user( op : UserStartPositionBuilder.() -> Unit) = UserStartPositionBuilder().apply(op).build()
class UserStartPositionBuilder {
fun build() = UserStartPosition()
}
This solves the problem of "only-one" implementation in an almost elegant way, but gives no answer to the "required property" option. So it is good when default value could be applied, but still gives only parse time exception when the position is missing.
Options 3: Chain of infix functions
This solution is a variant of the previous. To address the required issue of the previous, we use a variable and an intermediate class:
var start : StartWithPos? = null
class StartWithoutPos {
val windDirection = InitialWindDirectionBuilder()
val boat = InitialHeadingBuilder()
}
class StartWithPos(val startWithoutPos: StartWithoutPos, pos: StartPosition) {
}
fun start( op: StartWithoutPos.() -> Unit): StartWithoutPos {
val res = StartWithoutPos().apply(op)
return res
}
infix fun StartWithoutPos.position( pos: StartPosition): StartWithPos {
return StartWithPos(this, pos)
}
Then we could write the following statement in DSL:
start = start {
windDirection to NORTH
boat heading NORTH
} position random {
}
This would solve both problems, but with the cost of an additional variable assignment.
All three solutions work, adds some dirt to DSL, but one might chose which fits better.

Related

Kotlin: How to define a variable whose type depends on the input?

I have a function in Kotlin which takes a particular string as input. Depending on the input, I want to create a variable of a specific type and do some computations on it.
For example,
fun compute(input: String): Any{
if(input=="2d"){
var point: Point2D;// Points2D - x: int, y: int
//initilize and do some computations
return point.findDistanceFromOrigin()
}else if(input=="2d-1"){
var point: Point2DWithP1AsOrigin;// Point2DWithP1AsOrigin - x: int, y: int
//initilize and do some computations
return point.findDistanceFromOrigin()
}else if(input=="2d-2"){
var point: Point2DWithP2AsOrigin;
//initilize and do some computations
return point.findDistanceFromOrigin()
}
.
.
.
}
You can see in the above example, I want to initilize the type of point depending on the input and do computation and return.
All the if-else conditions have the same code except for the definition of the variable. How can I put all this in a single block with something like this:
var point: if(input=="2d) Point2D::class else if(input=="2d-1") Point2DWithP1AsOrigin::class.....
How can I do that?
You could do something like this
fun compute(input: String): Any{
val point: MyPoint = when(input) {
"2d" -> Point2D()
"2d-1" -> Point2DWithP1AsOrigin()
"2d-2" -> Point2DWithP2AsOrigin()
else -> Point2D() //fallback is necessary
}
//initilize and do some computations
return point.findDistanceFromOrigin()
}
But then it's essential that all those classes share the same interface. Because they need to have the same methods in order to do the same operations on them.
For example like this:
class Point2D : MyPoint {
override fun findDistanceFromOrigin() = 5
}
class Point2DWithP1AsOrigin : MyPoint{
override fun findDistanceFromOrigin() = 6
}
class Point2DWithP2AsOrigin : MyPoint{
override fun findDistanceFromOrigin() = 7
}
interface MyPoint {
fun findDistanceFromOrigin() : Int
}
You can store constructor references and then invoke required one
fun main() {
val constructors = mapOf(
"2d" to ::Point2D,
"2d-1" to ::Point2DWithP1AsOrigin,
"2d-2" to ::Point2DWithP2AsOrigin,
)
val type = "2d-2"
val constructor = constructors[type] ?: throw IllegalArgumentException("$type not supported")
val point = constructor()
println(point::class)
}
Output
class Point2DWithP2AsOrigin

Kotlin by lazy throws NullPointerException

I am currently trying to learn Kotlin with the help of the book "Kotlin Programming The Big Nerd Ranch Guide" and so far everything worked.
But now I am struggling with the "lazy" initialization which throws a NullPointerException which says
Cannot invoke "kotlin.Lazy.getValue()" because "< local1>" is null
The corresponding lines are:
val hometown by lazy { selectHometown() }
private fun selectHometown(): String = File("data/towns.txt").readText().split("\n").shuffled().first()
In case you want to compile it yourself or need more code for a better understanding I provide the Game.kt and Player.kt down below. If "lazy" is dropped for a "normal" initialization the hometown gets assigned as intended.
Any tips for solving the problem and understanding the cause of it is welcome.
// Game.kt
package com.bignerdranch.nyethack
fun main(args: Array<String>) {
val player = Player("Madrigal")
player.castFireball()
}
private fun printPlayerStatus(player: Player) {
println("(Aura: ${player.auraColor()}) " + "(Blessed: ${if (player.isBlessed) "YES" else "NO"})")
println("${player.name} ${player.formatHealthStatus()}")
}
// Player.kt
package com.bignerdranch.nyethack
import java.io.File
class Player(_name: String, var healthPoints: Int = 100, val isBlessed: Boolean, private val isImmortal: Boolean) {
var name = _name
get() = "${field.capitalize()} of $hometown"
private set(value) {
field = value.trim()
}
constructor(name: String) : this(name, isBlessed = true, isImmortal = false) {
if (name.toLowerCase() == "kar") healthPoints = 40
}
init {
require(healthPoints > 0, { "healthPoints must be greater than zero." })
require(name.isNotBlank(), { "Player must have a name" })
}
val hometown by lazy { selectHometown() }
private fun selectHometown(): String = File("data/towns.txt").readText().split("\n").shuffled().first()
fun castFireball(numFireballs: Int = 2) =
println("A glass of Fireball springs into existence. (x$numFireballs)")
fun auraColor(): String {
val auraVisible = isBlessed && healthPoints > 60 || isImmortal
return if (auraVisible) "GREEN" else "NONE"
}
fun formatHealthStatus() =
when (healthPoints) {
100 -> "is an excellent condition!"
in 90..99 -> "has a few scratches."
in 75..89 -> if (isBlessed) {
"has some minor wounds but is healing quite quickly"
} else {
"has some minor wounds"
}
in 15..74 -> "looks pretty hurt"
else -> "is in awful condition!"
}
}
I forgot the towns.txt so here it is (not that it matters much)
Neversummer
Abelhaven
Phandoril
Tampa
Sanorith
Trell
Zan'tro
Hermi Hermi
Curlthistle Forest
When something like this happens, it's usually due to bad ordering of initialization.
The initialization of the Player class goes this way:
the name property has its backing field initialized with the _name value
the init block is run, and tries to access name
the getter of name tries to read the hometown property, but fails because hometown is still not initialized
...if things had gone right, the hometown property would be initialized now with the lazy delegate
So basically you're trying to access hometown before the lazy delegate is configured.
If you move hometown's declaration above the init block, you should be fine.
You can see the fix in action on the playground

Proper use of Number class in Kotlin

Can anyone help me implement these methods in Kotlin?
I want to find min, max elements of array of numbers and also sort array in ascending order. Here is a code
class DataArray<Number>(vararg numbers: Number) {
private val array = mutableListOf<Number>(*numbers)
fun getMin() {
return array.minByOrNull { it! } //doesn't work
}
fun getMax() = array.max() //doesn't work
fun sort() = array.sort() //doesn't work
private fun <E> MutableList<E>.max(): Any { //was created to use in function above, but resulted in stack overflow
return this.max()
}
private fun <E> MutableList<E>.sort(): Any { //was created to use in function above, but resulted in stack overflow
return this.sort()
}
override fun toString(): String {
var str = ""
for(i in array)
str += "$i "
return str
}
}
fun main() {
val arr = DataArray(2, 5, 2, 6, 9, -3, 56, 16, 72, 8)
println(arr.getMax())
println(arr.getMin())
println(arr.sort())
print(arr)
}
Note that the word Number here declares a generic parameter called Number. It does not refer to kotlin.Number. You might have intended it to declare a generic parameter with a bound of Number instead, in which case you should have written:
class DataArray<T: Number>(vararg numbers: T) {
...
}
But even if you did, it still wouldn't work as Numbers are not comparable.
You would have to further constrain T to Comparable<T>:
class DataArray<T: Number>(vararg numbers: T) where T: Comparable<T> {
Then you can do:
fun getMin() = array.minOrNull()
fun getMax() = array.maxOrNull()
fun sort() = array.sort()
Extension functions on MutableList are unnecessary.
(Note that technically, the T: Number constraint is also unnecessary if you just want to use minOrNull, maxOrNull, and sort. I'm assuming you are planning on using one of the methods in kotlin.Number. Otherwise you can delete that constraint.)
You seem to be trying to implement your own MutableList by delegation. Keep in mind that you can easily do this using by:
class DataArray<T: Number>(
vararg numbers: T
) : MutableList<T> by mutableListOf(*numbers) {
override fun toString(): String {
var str = ""
for(i in this) // rather than "array", use "this"
str += "$i "
return str
}
}

What's the fastest/simplest way to calculate a moving Average in Kotlin?

I can think on some dirty ways to calculate a moving average on Kotlin, but I'm not sure which one is the best. I know that kotlin has a lot of interesting features to work with collections and list. What do you think is the most efficient (or simplest) way to calculate a moving average?
Kotlin 1.2 will introduce a sliding window which you can combine with average obviously.
val data = listOf(1,2,5,6,2,7,8,5,9)
// 3 "period" moving average
val movingAverage = data.windowed(3,1,List<Int>::average)
// OR
val movingAverage = data.windowed(3,1) { it.average() }
Until then you would have to introduce your own sliding sequence.
class SlidingSequence<out T>(val source: Iterable<T>,
val slideSize: Int,
val slideStep: Int) : Sequence<List<T>> {
override fun iterator(): Iterator<List<T>> = object : AbstractIterator<List<T>>() {
private val iterator = if (slideSize > 0) source.iterator() else emptyList<T>().iterator()
private var buffer = listOf<T>()
override fun computeNext() = when {
iterator.hasNext() -> {
buffer = buffer.drop(slideStep).let {
it + iterator.asSequence().take(slideSize - it.size)
}
setNext(buffer)
}
else -> done()
}
}
}
fun <T> Iterable<T>.windowed(size: Int,
step: Int = 1): Sequence<List<T>> {
return SlidingSequence(this, size, step)
}
// and then you can do
val data = listOf(1,2,5,6,2,7,8,5,9)
// 3 "period" moving average
val movingAverage = data.windowed(3).map(List<Int>::average)
PS. I haven't looked at the code of Kotlin 1.2 windowed implementation, but since the function takes an immediate transform, I'm guessing the result is not lazy, where in the self implemented case above it's a lazy result, so you need to actually enumerate the sequence with something like .toList() to get the actual values.
Another one-line given period > 0 is:
data?.takeLast(period)?.reduce { v, d -> v + d}?: 0 / period
This also works if the data is empty or null due to takeLast() `s behaviour.

Recursive definition of infinite sequence in Kotlin

I'm experimenting with the Kotlin sequences and particular the more complicated ones that are not simple calculations on the previous value.
One example I'd like to define is a sequence of all prime numbers.
An easy way to define the next prime is the next integer that is not divisible by any of the previous primes in the sequence.
In Scala this can be translated to:
def primeStream(s: Stream[Int]): Stream[Int] = s.head #:: primeStream(s.tail filter(_ % s.head != 0))
val primes = primeStream(Stream.from(2))
// first 20 primes
primes.take(20).toList
I'm having trouble translating this to Kotlin. In scala it works because you can pass function that returns a sequence that will be lazily evaluated but I can't do the same in Kotlin.
In Kotlin I tried
fun primes(seq: Sequence<Int>):Sequence<Int> = sequenceOf(seq.first()) + primes(seq.drop(1).filter {it % seq.first() != 0})
val primes = primes(sequence(2) {it + 1})
primes.take(20).toList()
But that obviously doesn't work because the function is evaluated straight away and leads to an infinite recursion.
The key point here is to implement a Sequence transformation so that its first item remains and the tail is lazily transformed from the original Sequence tail to something else. That is, the transformation is done only when the item is requested.
First, let's implement lazy sequence concatenation, which behaves like simple concatenation but the right operand is evaluated lazily:
public infix fun <T> Sequence<T>.lazyPlus(otherGenerator: () -> Sequence<T>) =
object : Sequence<T> {
private val thisIterator: Iterator<T> by lazy { this#lazyPlus.iterator() }
private val otherIterator: Iterator<T> by lazy { otherGenerator().iterator() }
override fun iterator() = object : Iterator<T> {
override fun next(): T =
if (thisIterator.hasNext())
thisIterator.next()
else
otherIterator.next()
override fun hasNext(): Boolean =
thisIterator.hasNext() || otherIterator.hasNext()
}
}
Laziness of otherIterator does all the trick: otherGenerator will be called only when otherIterator is accessed, that is, when the first sequence finishes.
Now, let's write a recursive variant of the sieve of Eratosthenes:
fun primesFilter(from: Sequence<Int>): Sequence<Int> = from.iterator().let {
val current = it.next()
sequenceOf(current) lazyPlus {
primesFilter(it.asSequence().filter { it % current != 0 })
}
}
Note that lazyPlus allowed us to lazily make another recursive call of primesFilter in the tail of the sequence.
After that, the whole sequence of primes can be expressed as
fun primes(): Sequence<Int> {
fun primesFilter(from: Sequence<Int>): Sequence<Int> = from.iterator().let {
val current = it.next()
sequenceOf(current) lazyPlus {
primesFilter(it.asSequence().filter { it % current != 0 })
}
}
return primesFilter((2..Int.MAX_VALUE).asSequence())
}
Though this approach isn't very fast. Evaluation of 10,000 primes takes a few seconds, however, the 1000th prime is emitted in about 0.1 second.
You can place the Sequence<Int> concatenation inside of a Sequence<Sequence<Int>> generator and then flatten it to a Sequence<Int> again:
fun primes(seq: Sequence<Int>): Sequence<Int> = sequence {
seq.take(1) + primes(seq.drop(1).filter { it % seq.first() != 0 })
}.flatMap { it }
val primes = primes(sequence(2) { it + 1 })
Output: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71]
It seems a bit slow though. What you probably want is to cache each result in a list and build off of it instead of recalculating the primes recursively. e.g.:
fun primes() = with(arrayListOf(2, 3)) {
asSequence() + sequence(last() + 2) { it + 2 }
.filter { all { prime -> it % prime != 0 } }
.map { it.apply { add(it) } }
}
My current answer is not to use a recursive function. I can get still get an infinite sequence of primes by modelling the sequence as a pair of values with the first the prime number and the second the current filtered sequence. I then apply the map to only select the first element.
val primes = sequence(2 to sequence(3) {it + 2}) {
val currSeq = it.second
val nextPrime = currSeq.first()
nextPrime to currSeq.filter { it % nextPrime != 0}
}.map {it.first}