Kotlin - Convert while loop to functional style - while-loop

I have the following Kotlin function:
fun func(n: Int): Int {
var count = 1
var m = n
while(m != 1) {
m = if(m.isOdd()) 3 * m + 1 else m / 2
count++
}
return count
}
I would like to write that simple algorithm in a "functional" style, using Kotlin's operators like map(), count(), etc. The closest thing I could come up with was this:
fun func(n: Int): Int {
return n.toList()
.map{ if(it.isOdd()) 3*it+1 else it/2 }
.takeWhile { it != 1 }
.count()
}
Obviously, the above code does not work because map is executed only once, but you get the idea of what I am trying to achieve.
PS: toList() is just an extension function that converts an int to a list containing that int:
fun Int.toList() = listOf(this)

Since you do not know how many items there will be, you can construct a (possibly infinite) sequence where each item is calculated based on the previous one, then limit it with your condition it != 1 and count how many items there are:
return generateSequence(n) { if (it.isOdd()) 3 * it + 1 else it / 2 }
.takeWhile { it != 1 }
.count()
Here, generateSequence(n) { ... }, constructs a Sequence<Int> that has n for its first element, and each of the following elements is calculated by the code passed as a lambda (it is called on the previous element, and only when another item is queried, i.e. lazily).

Related

problems with index of array

I'm writing a function that allows you to remove certain numbers from an int arraylist.
My code
for (i in 1 until 4) {
divider = setDivider(i)
for(index in 0 until numbers.size){
if(index <= numbers.size){
if (numbers[index] % divider == 0 && !isDone) {
numbers.removeAt(index)
}
}else{
isDone = true
}
}
if(isDone)
break
}
the function to set the divider
fun setDivider(divider: Int): Int {
when (divider) {
1 -> return 2
2 -> return 3
3 -> return 5
4 -> return 7
}
return 8
}
I do not know why the ide is giving me the error Index 9 out of bounds for length 9.
Author explained in the comments that the goal is to remove all numbers that are divisible by 2, 3, 5 and 7.
It can be achieved much easier by utilizing ready to use functions from stdlib:
val dividers = listOf(2, 3, 5, 7)
numbers.removeAll { num ->
dividers.any { num % it == 0 }
}
It removes elements that satisfy the provided condition (is divisible) for any of provided dividers.
Also, it is often cleaner to not modify a collection in-place, but to create an entirely new collection:
val numbers2 = numbers.filterNot { num ->
dividers.any { num % it == 0 }
}

how to increase the size limit of a mutable list in kotlin?

I was attempting to solve the multiset question (https://codeforces.com/contest/1354/problem/D) on codeforces using Fenwick Tree Data structure. I passed the sample test cases but got the memory limit error after submitting, the testcase is mentioned below.
(Basically the testcase is:
1000000 1000000
1.............1 //10^6 times
-1...........-1 //10^6 times).
I tried similar testcase in my IDE and got the below mentioned error.
(Similar to above, the testcase I provided is:
1000000 1
1.............1 //10^6 times
-1
)
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 524289 out of bounds for length 524289
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:248)
at java.base/java.util.Objects.checkIndex(Objects.java:373)
at java.base/java.util.ArrayList.get(ArrayList.java:426)
at MultisetKt.main(multiset.kt:47)
at MultisetKt.main(multiset.kt)
Here is my code:
private fun readInt() = readLine()!!.split(" ").map { it.toInt() }
fun main() {
var (n, q) = readInt()
var list = readInt() //modify the list to store it from index 1
var finalList = listOf(0) + list
val query = readInt()
var bit = MutableList(n+1){0}
fun update(i:Int, value:Int) {
var index = i
while(index < n){
bit.set (index , bit[index] + value)
index += (index and -index)
}
}
fun rangefunc(i:Int): Int {
var su = 0
var index = i
while(index > 0){
su += bit[index]
index -= (index and -index)
}
return su
}
fun find(x:Int):Int {
var l = 1
var r = n
var ans = n
var mid = 0
while (l <= r) {
mid = (l + r) / 2
if (rangefunc(mid) >= x) {
ans = mid
r = mid - 1
} else {
l = mid + 1
}
}
return ans
}
for (i in 1..n) {
update(finalList[i], 1)
}
for (j in 0..q - 1) {
if (query[j] > 0) {
update(query[j], 1)
} else {
update(find(-query[j]), -1)
}
}
if(rangefunc(n) == 0){
println(0)
}else{
println(find(1))
}
}
I believe this is because the BITlist is not able to store 10^6 elements but not sure. Please let me know what changes should I make in my code also any additional advice on how to deal with such cases in the future.
Thank you in advance :)
An ArrayList can store over 2 billion items (2 * 10^9). That is not your issue. ArrayIndexOutOfBoundsException is for trying to access an index of an ArrayList that is less than zero or greater than or equal to its size. In other words, an index that it doesn't yet contain.
There's more code there than I have time to debug. But I would start at the line that the stack trace points to and see how it's possible for you to attempt to call bit[index] with an index that equals the size of the ArrayList.
To answer your literal question, you can use LinkedList explicitly as your type of MutableList to avoid the size restriction, but it is heavier and it is slower when accessing elements by index.

Is there a way to merge filter and map into single operation in Kotlin?

The below code will look for "=" and then split them. If there's no "=", filter them away first
myPairStr.asSequence()
.filter { it.contains("=") }
.map { it.split("=") }
However seeing that we have both
.filter { it.contains("=") }
.map { it.split("=") }
Wonder if there's a single operation that could combine the operation instead of doing it separately?
You can use mapNotNull instead of map.
myPairStr.asSequence().mapNotNull { it.split("=").takeIf { it.size >= 2 } }
The takeIf function will return null if the size of the list returned by split method is 1 i.e. if = is not present in the string. And mapNotNull will take only non null values and put them in the list(which is finally returned).
In your case, this solution will work. In other scenarios, the implementation(to merge filter & map) may be different.
I see your point and under the hood split is also doing an indexOf-check to get the appropriate parts.
I do not know of any such function supporting both operations in a single one, even though such a function would basically just be similar to what we have already for the private fun split-implementation.
So if you really want both in one step (and require that functionality more often), you may want to implement your own splitOrNull-function, basically copying the current (private) split-implementation and adapting mainly 3 parts of it (the return type List<String>?, a condition if indexOf delivers a -1, we just return null; and some default values to make it easily usable (ignoreCase=false, limit=0); marked the changes with // added or // changed):
fun CharSequence.splitOrNull(delimiter: String, ignoreCase: Boolean = false, limit: Int = 0): List<String>? { // changed
require(limit >= 0, { "Limit must be non-negative, but was $limit." })
var currentOffset = 0
var nextIndex = indexOf(delimiter, currentOffset, ignoreCase)
if (nextIndex == -1 || limit == 1) {
if (currentOffset == 0 && nextIndex == -1) // added
return null // added
return listOf(this.toString())
}
val isLimited = limit > 0
val result = ArrayList<String>(if (isLimited) limit.coerceAtMost(10) else 10)
do {
result.add(substring(currentOffset, nextIndex))
currentOffset = nextIndex + delimiter.length
// Do not search for next occurrence if we're reaching limit
if (isLimited && result.size == limit - 1) break
nextIndex = indexOf(delimiter, currentOffset, ignoreCase)
} while (nextIndex != -1)
result.add(substring(currentOffset, length))
return result
}
Having such a function in place you can then summarize both, the contains/indexOf and the split, into one call:
myPairStr.asSequence()
.mapNotNull {
it.splitOrNull("=") // or: it.splitOrNull("=", limit = 2)
}
Otherwise your current approach is already good enough. A variation of it would just be to check the size of the split after splitting it (basically removing the need to write contains('=') and just checking the expected size, e.g.:
myPairStr.asSequence()
.map { it.split('=') }
.filter { it.size > 1 }
If you want to split a $key=$value-formats, where value actually could contain additional =, you may want to use the following instead:
myPairStr.asSequence()
.map { it.split('=', limit = 2) }
.filter { it.size > 1 }
// .associate { (key, value) -> key to value }

Group a collection by n conditions

In Kotlin, what would be a neat (preferably functional) way of grouping elements having n grouping conditions?
For example:
class Item(val level : Int)
Given the list: (Item(1), Item(2), Item(5))
and two grouping conditions:
level > 0 && level < 3
level > 4
The following lists are expected:
listOf(Item(1), Item(2))
listOf(Item(5))
The groupBy function takes only 1 condition argument. Is there any other function that would be helpful?
You could return an Int in the lambda passed to groupBy which identifies your criteria. This would work for any number of conditions.
val l = listOf(Item(1), Item(2), Item(5))
val g = l.groupBy {
when {
it.level > 0 && it.level < 3 -> 0
it.level > 4 -> 1
// ...
else -> null
}
}.filterKeys { it != null }) // optional: filter out null as default key
Result:
{0=[Item(level=1), Item(level=2)], 1=[Item(level=5)]}
I'd also utilize partion, as already suggested, here. You could also chain them:
val cond1: (Item) -> Boolean = { it.level in 0..2 }
val cond2: (Item) -> Boolean = { it.level > 4 }
val parts = elements
.partition { cond1(it) || cond2(it) }
.first.partition { cond1(it) }
println(parts)
This will result into iterations of your input which is slightly less efficient than the groupBy. Still a linear runtime complexity.
Try to filter out all elements which aren't necessary and then either groupBy or partition them, e.g.:
using partition (i.e. you only need 2 lists out of 1):
listSequence()
.filter { it.level > 0 && it.level != 3 } // it seems you are only interested in levels > 0 && != 3
.partition { it.level in 1..2 } // partition into items having 0 < level < 3 and the rest
.run(::println) // prints: [[Item(level=1), Item(level=2)], [Item(level=5)]] (which is a pair of lists)
using groupBy similar to what Willi Mentzel has shown:
listSequence()
.filter { it.level > 0 && it.level != 3 } // if you need to filter... otherwise skip that and assign just a random group
.groupBy {
when (it.level) {
in 1..2 -> 0
else -> 1
}
}
.values.run(::println) // which also prints ([Item(level=1), Item(level=2)], [Item(level=5)]) but now is a collection of lists
In both cases I used a sequence as follows:
fun listSequence() = sequenceOf(Item(1), Item(2), Item(5), Item(-4), Item(0))
Depends what you want to accomplish in the end... You may also be interested in some of the other available collection functions.

Why aren't the entries in a Kotlin Pair mutable?

I have a MutableList of Pairs and I'd like to decrement the value of the first entry so my condition my pass(change):
while(n > 0) {
if(sibice[i].first > 0) {
sum += sibice[i].second
//sibice[i].first-- will not compile
n--
} else i++
}
But the Pair class doesn't let me do that, besides creating my own pair is there any other workaround and why is this even the case?
Like with all entities, issues arise with mutability.
In your case you can just update the list-entry with a new pair of values.
val newPair = oldPair.copy(first = oldPair.first-1)
Or directly use an array of length 2 instead intArrayOf(0, 0). So you can access the elements directly.
while(n > 0) {
if(sibice[i][0] > 0) {
sum += sibice[i][1]
sibice[i][0]--
n--
} else i++
}
You could even define extension values first and second to the IntArray type and use it the same like before.
val IntArray.second get() = get(1)
var IntArray.first
set(value) = set(0, value)
get() = get(0)