Take the following one-liner, which can be expressed as a series of operations on a collection or a sequence:
val nums = (10 downTo 1)
// .asSequence() if we want this to be a sequence
.filter { it % 2 == 0 }
.map { it * it }
.sorted()
// .asList() if declaring it a sequence
println(nums) // [4, 16, 36, 64, 100]
Let's say I want to see the elements at each step, they would be (from deduction):
[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[10, 8, 6, 4, 2]
[100, 64, 36, 16, 4]
[4, 16, 36, 64, 100]
Unfortunately, there's no good way to either debug this with a debugger or log these values for later inspection. With good functional programming constructs, entire methods can be rewritten as single statements like this but there seems to be no good way to inspect intermediate states, even counts (10, 5, 5, 5 here).
What's the best way to debug these?
You can log the intermediate values (lists) with
fun <T> T.log(): T { println(this); this }
//USAGE:
val nums = (10 downTo 1)
.filter { it % 2 == 0 }.log()
.map { it * it }.log()
.sorted().log()
This will work as desired since in your example you work with collections, not sequences. For lazy Sequence you need:
// coming in 1.1
public fun <T> Sequence<T>.onEach(action: (T) -> Unit): Sequence<T> {
return map {
action(it)
it
}
}
fun <T> Sequence<T>.log() = onEach {print(it)}
//USAGE:
val nums = (10 downTo 1).asSequance()
.filter { it % 2 == 0 }
.map { it * it }.log()
.sorted()
.toList()
In latest Intellij Idea when adding a breakpoint you have an option to set it to not inspect whole expression but only a Lambda body.
Then in the debug itself you can see what is happening inside of your Lambda.
But this is not the only way. You can also use Run to cursor (Alt + F9).
I think the current correct answer is that you want the Kotlin Sequence Debugger plugin, which lets you use IntelliJ's lovely Java stream debugger with Kotlin sequences.
Note that (unless I'm doing something wrong) it doesn't appear to work with collections, so you will have to convert the collection to a sequence in order to debug it. Easy enough using Iterable.asSequence, and a small price to pay -- you can always revert that change once you are done debugging.
you may use the also inline function to log, print at any sequence stage as explained by Andrey Breslav at Google I/O '18
(1..10)
.filter { it % 2 == 0 }
.also { e -> println(e) /* do your debug or print here */ }
.map { it * 2 }
.toList()
Related
I have the following code in kotlin and I'm trying to find a rust equivalent, but don't understand the chaining mechanism in rust to convert.
val windowSize = 2
val result = listOf(1, 2, 3, 4, 5, 6)
.windowed(windowSize, 1) ; [[1,2], [2,3], [3,4], [4,5], [5,6]]
.map { it.sum() } ; [ 3, 5, 7, 9, 11]
.windowed(2, 1) ; [[3,5], [5,7], [7,9], [9,11] ]
.count { it[0] < it[1] } ; 4
;; result = 4, as there are 4 sequences that have first number less than 2nd,
;; when considering a sliding window over the original data of 2 items at a time.
It just takes a list of integers, splits them into pairs (but the windowSize will be a function parameter), sums those groups, splits the sums into pairs again, and finds where each second element is bigger than the previous, so finding increasing values over moving windows.
I'm converting this to the rust equivalent, but struggling to understand how to chain operations together.
What I've got so far is:
let input = [1, 2, 3, 4, 5, 6];
input.windows(2)
.map(|es| es.iter().sum())
// what goes here to do the next windows(2) operation?
.for_each(|x: u32| println!("{}", x));
I can "for_each" over the map to do things on the iteration, but I can't split it with another "windows()", or don't know the magic to make that possible. IntelliJ is showing me the return type from map is impl Iterator<Item=?>
Can anyone enlighten me please? I am an absolute beginner on rust, so this is undoubtedly to do with my understanding of the language as a whole.
The Itertools crate provides a reasonably convenient way to do this with the tuple_windows method.
use itertools::Itertools;
fn main() {
let input = [1i32, 2, 3, 4, 5, 6];
let output: usize = input
.windows(2)
.map(|es| es.iter().sum::<i32>())
.tuple_windows()
.filter(|(a, b)| a < b)
.count();
println!("{}", output);
}
Playground
The standard library does not have a way to do this without collecting the iterator first, which requires two passes through the data.
It is a bit convoluted to chain everything. You need to collect into a vec so you can access windows again. Then you can flat_map the windows to array references (taken from this other answer) to complete what you want to do:
fn main() {
let input = [1usize, 2, 3, 4, 5, 6];
let res = input
.windows(2)
.map(|es| es.iter().sum::<usize>())
.collect::<Vec<_>>()
.windows(2)
.flat_map(<[usize; 2]>::try_from)
.filter(|[a, b]| a < b)
.count();
println!("{}", res);
}
Playground
Note: Nightly feature array_windows that use const generic allow to remove the .flat_map(<&[usize; 2]>::try_from) call
As stated in #Aiden4's answer, the best solution is to use itertools::tuple_windows. It is however possible using just the standard library and without collecting to an intermediate vector using Iterator::scan:
fn main() {
let input = [1i32, 2, 3, 4, 5, 6];
let output: usize = input
.windows(2)
.map(|es| es.iter().sum())
.scan(0, |prev, cur| {
let res = (*prev, cur);
*prev = cur;
Some(res)
})
.skip(1)
.filter(|(a, b)| a < b)
.count();
println!("{}", output);
}
Playground
Using std and stable only:
fn main() {
let input = [1i32, 2, 3, 4, 5, 6];
let mut iter = input.windows(2).map(|es| es.iter().sum::<i32>());
let n = if let Some(mut prev) = iter.next() {
iter.map(|i| {
let ret = (prev, i);
prev = i;
ret
})
.filter(|(a, b)| a < b)
.count()
} else {
0
};
println!("{}", n);
}
This should be very fast.
For example we have such array:
val listArr = listOf(1,2,3,4,5,6,7)
and finally we receive:
1,2,3,4,5,6,7
maybe it is possible to write something like that:
val listArr = listOf(1..7)
and receive similar result. Or it is impossible right now?
You can use the IntRange.toList() function:
val list = (1..7).toList()
Ranges are automatically converted to lists when concatenating:
val combined = (1..6) + 12 + (34..37)
// [1, 2, 3, 4, 5, 6, 12, 34, 35, 36, 37]
RobCo's answer is correct and answers the question asked.
About the followup question you asked in the comment to his answer:
how we can use such solution in another list for example 1,2,3,4,5,6,12,34,35,36,37
You could write a new function that accepts ranges:
fun customListOf(vararg ranges: IntRange) = ranges.flatMap { it.toList() }
Then use it like this:
fun main() {
val list = customListOf(1..6, 12..12, 34..37)
println(list)
}
Output:
[1, 2, 3, 4, 5, 6, 12, 34, 35, 36, 37]
However, you need to pass a range even for a single value like 12..12 above.
If you wanted to be hacky, you could write a function that accepts a vararg range: Any, and use reflection to check the type at runtime. That would allow you to mix ranges and ints in the same call:
fun hackyCustomListOf(vararg rangeOrInt: Any) = rangeOrInt.flatMap {
when (it) {
is IntRange -> it.toList()
is Int -> listOf(it)
else -> throw IllegalArgumentException("Expected an IntRange or an Int, got ${it::class}")
}
}
Usage:
fun main() {
val list1 = hackyCustomListOf(1, 5, 12..15, 25, 99..102)
println(list1)
val list2 = hackyCustomListOf(1..3, "boom", 5.0)
println(list2)
}
Output:
[1, 5, 12, 13, 14, 15, 25, 99, 100, 101, 102]
Exception in thread "main" java.lang.IllegalArgumentException: Expected an IntRange or an Int, got class kotlin.String
at TestKt.hackyCustomListOf(test.kt:7)
at TestKt.main(test.kt:14)
at TestKt.main(test.kt)
This removes compile-time checks on the argument, so I don't think it's a good idea. Fun exercise, though.
I have the following data for my task:
Input: nums = [1,2,3,4]
Output: [1,3,6,10]
Explanation: Running sum is obtained as follows: [1, 1+2, 1+2+3, 1+2+3+4].
As u see, I need to return IntArray, the first thing I used was runningReduce() , but this function is used in the version of Kotlin 1.4.30.
fun runningSum(nums: IntArray): IntArray {
return nums.runningReduce { sum, element -> sum + element }.toIntArray()
}
Yes, this solution works, but how can I solve the same problem using reduce() or fold()?
Try the following:
nums.fold(listOf(0)) { acc, i -> acc + (acc.last() + i) }.drop(1).toIntArray()
The solution is sub-optimal though: it copies the list in each iteration. But looks fancy.
To avoid copying, you could write it as follows:
nums.fold(mutableListOf(0)) { acc, i -> acc += (acc.last() + i); acc }.drop(1).toIntArray()
I think I prefer the first version, the second one is not pure from functional perspective.
Mafor is right, fold() is not a good choice when producing a collection because it copies the collection every time so you need to workaround with mutable collections, which defeats the point of the functional style.
If you really want to work with arrays, which are mutable, doing it the old fashioned procedural way may be best:
val array = intArrayOf(1, 2, 3, 4)
for (i in array.indices.drop(1)) {
array[i] += array[i - 1]
}
println(array.joinToString(", "))
Here's a slightly modified version of Mafor's answer that gives you an IntArray and avoids the use of multiple statements - it still uses the mutable list though:
val input = intArrayOf(1, 2, 3, 4)
val output = input.fold(mutableListOf(0)) { acc, cur ->
acc.apply { add(last() + cur) }
}.drop(1).toIntArray()
println(output.joinToString(", "))
Both of these print 1, 3, 6, 10.
What is the Kotlin way of printing IntArray contents?
class Solution {
fun plusOne(digits: IntArray): IntArray {
println(digits.toString()) // does not work
println(Arrays.toString(digits)) // does work but its java way of doing
for(i in 0 until digits.size) {
...
}
return digits
}
}
Is there any kotlin method which works similar to Arrays.toString() ? I just want to see the content for debugging purpose.
There are multiple ways, depending on your requirement, you can use any. Note that you don't have to convert it to List just to print, unless you need it for other use cases.
println(arr.contentToString())
//this one prints number on each line
arr.forEach(::println) // same as arr.forEach{println(it)}
You can also use Arrays.toString but contentToString() is convenient, and it internally calls Arrays.toString
After doing a bit more research, I wanted to add joinToString() to above awesome answers :
val numbers = intArrayOf(1, 2, 3, 4, 5, 6)
println(numbers.joinToString())
This will print below output :
1, 2, 3, 4, 5, 6
This also allows you add prefix, suffix of your choice as below :
println(numbers.joinToString(prefix = "{", postfix = "}"))
This would output :
{1, 2, 3, 4, 5, 6}
You can use the contentToString function available for all types of Array
val digits = intArrayOf(1,2,3,4)
val javaToString = Arrays.toString(digits).also(::println) // [1, 2, 3, 4]
val kotlinToString = digits.contentToString().also(::println) // [1, 2, 3, 4]
println(javaToString == kotlinToString) // true
Here is a working example https://pl.kotl.in/dUu_aioq0
There are many ways to accomplish it.
One way would be to first build the output string using fold and then print it inside an also function:
val digits = intArrayOf(1,2,3,4,5,6,7)
digits.fold("[") { output, item -> "$output $item" }.also { println("$it ]") }
This would output:
[ 1 2 3 4 5 6 7 ]
Try to convert to list
fun main() {
val digits: IntArray = IntArray(10)
println(digits.toList()) // or digits.asList()
}
Output:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
I need to iterate part of an array backwards. I'd like to do that "functionally" as it's more comprehensible, like that
for (b in buf.sliceArray(0 until bufLimit).reversedArray()) {}
But both sliceArray and reversedArray are not lazy. Is there a lazy version or should I probably fall back to
for (bIdx in bufLimit - 1 downTo 0) {
val b = buf[bIdx]
}
which is more confusing and verbose?
If you use a list instead of an array, then you can reverse it and then convert to a Sequence:
val buf: List = listOf(1, 2, 3, 4, 5)
val bufLimit = 3
for (b in buf.asReversed().asSequence().drop(buf.size - bufLimit)) {
println(b)
}
Functions with the as prefix only wrap objects without copying, so the code above does not copy the buf content.
Note that you shouldn't loose any performance compared to Array if you use an ArrayList.
However this solution does involve several iterators, so it is somewhat less efficient than the index code you have suggested in the question:
for (bIdx in bufLimit - 1 downTo 0) {
val b = buf[bIdx]
}
I suggest creating an extension function to handle your specific use case. e.g.:
/**
* Performs the given [action] on each element at the specified [indices].
*/
inline fun ByteArray.forEachAt(indices: Iterable<Int>, action: (Byte) -> Unit): Unit {
indices.forEach { index -> action(this[index]) }
}
Usage:
buf.forEachAt((0 until bufLimit).reversed)) {}
// or
buf.forEachAt(bufLimit - 1 downTo 0) {}