passing lambda to joinToString in kotlin? - kotlin

I am new to Kotlin and I am struggling to understand following code:
println((1..5).joinToString(", ") { (it * 2).toString() }) // 2, 4, 6, 8, 10
From my understanding the above code should have been written like this:
println((1..5).map { it * 2 }.joinToString(", ")) // 2, 4, 6, 10
To which function are we passing the lambda { (it * 2).toString() } and why do we have .toString() there? I didn't find any clue in documentation of joinToString either. So, how does this work?

joinToString has an optional parameter called transform of type ((T) -> CharSequence)?. If a non-null argument is passed for that parameter, joinToString will run it on each element of the receiver collection before joining the elements into a string. Let's look at both code snippets you've provided in more detail:
println((1..5).joinToString(", ") { (it * 2).toString() })
Here, you're calling joinToString on a range of (1..5). You're passing a transform function, { (it * 2).toString() }, so before joining the elements of the range into a string, joinToString will multiply each of them by 2. You'll also need to call toString() on the result of the multiplication since the signature of transform is (T) -> CharSequence.
A piece of general advice: whenever you're confused by Kotlin's terse syntax, reduce the level of terseness until you fully understand the code. In this case, changing the snippet to the following might make it easier to understand:
println((1..5).joinToString(", ", transform = { (it * 2).toString() }))
The second snippet produces the exact same result:
println((1..5).map { it * 2 }.joinToString(", "))
You don't need the transform function here since the elements are already multiplied by 2 inside map.
A note on performance (this is irrelevant here cause the size of the input is too small, but important for large inputs), map will have to copy the entire collection, hence the second variant will be slower and less memory efficient. The first variant does not have that problem.

Related

How do I make this Sequence lazy?

I was trying to generate all permutations of a list in Kotlin. There are a zillion examples out there which return a List<List<T>>, but my input list breaks those as they try to fit all the results in the output list. So I thought I would try to make a version returning Sequence<List<T>>...
fun <T> List<T>.allPermutations(): Sequence<List<T>> {
println("Permutations of $this")
if (isEmpty()) return emptySequence()
val list = this
return indices
.asSequence()
.flatMap { i ->
val elem = list[i]
(list - elem).allPermutations().map { perm -> perm + elem }
}
}
// Then try to print the first permutation
println((0..15).toList().allPermutations().first())
Problem is, Kotlin just seems to give up and asks for the complete contents of one of the nested sequences - so it never (or at least not for a very long time) ends up getting to the first element. (It will probably run out of memory before it gets there.)
I tried the same using Flow<T>, with the same outcome.
As far as I can tell, at no point does my code ask it to convert the sequence into a list, but it seems like something internal is doing it to me anyway, so how do I stop that?
As mentioned in the comments, you have handled the empty base case incorrectly. You should return a sequence of one empty list.
// an empty list has a single permutation - "itself"
if (isEmpty()) return sequenceOf(emptyList())
If you return an empty sequence, first will never find anything - your sequence is always empty - so it will keep evaluating the sequence until it ends, and throw an exception. (Try this with a smaller input like 0..2!)

The least amount of letters in a list of Palindromes

So the question is giving a BIG string, break it up, find the palindromes and then find the shortest length within those sets of palindromes. Here's the code
Main Function
fun main(){
val bigArray = "Simple, given a string of words, return the length of acdca the " +
"shortest valav words String will never be empty and you do not need dad to account for different data types."
println(leastP(bigArray))
}
The Custom Function
fun leastP(s: String): Int {
val sSplit = listOf(s.split(""))
val newArray = listOf<String>()
for (i in sSplit){
for (j in i.indices){
if (isPalindrome3(i[j])) newArray.plus(j)
}
}
return newArray.minOf { it.length }
}
private fun isPalindrome3(s: String): Boolean {
var i = 0
var j = s.length -1
while (i < j){
if (s[i++].lowercaseChar() != s[j--].lowercaseChar()) return false
}
return true
}
}
I get this error
Not sure whats going on or where I messed up. Any help is appreciated.
In addition to the array problem identified in Tenfour04's answer, the code has an additional problem:
split("") splits the string into individual characters, not just individual words. 
If you debug it, you'll find that isPalindrome3() is being called first on an empty string, then on "S", then on "i", and so on.
That's because the empty string "" matches at every point in the input.
The easiest fix is to call split(" "), which will split it at space characters.
However, that might not do exactly what you want, for several reasons: it will include empty strings if the input has runs of multiple spaces; it won't split at other white space characters such as tabs, newlines, non-breaking spaces, en spaces, etc.; and it will include punctuation such as commas and full stops. Splitting to give only words is harder, but you might try something like split(Regex("\\W") to include only letters, digits, and/or underscores. (You'll probably want something more sophisticated to include hyphens and apostrophes, and ensure that accented letters etc. are included.)
There's a further issue that may or may not be a problem: you don't specify a minimum length for your palindromes, and so words like a match. (As do empty strings, if the split produces any.) If you don't want the result to be 0 or 1, then you'll also have to exclude those.
Also, the code is currently case-sensitive: it would not count "Abba" as a palindrome, because the first A is in upper case but the last a isn't. If you wanted to check case-insensitively, you'd have to handle that.
As mentioned in a comment, this is the sort of thing that should be easy to test and debug. Short, self-contained functions with no external dependencies are pretty easy to write unit tests for. For example:
#Test fun testIsPalindrome3() {
// These should all count as palindromes:
for (s in listOf("abcba", "abba", "a", "", "DDDDDD"))
assertTrue(isPalindrome3(s))
// But these shouldn't:
for (s in listOf("abcbb", "Abba", "a,", "abcdba"))
assertFalse(isPalindrome3(s))
}
A test like that should give you a lot of confidence that the code actually works. (Especially because I've tried to include corner cases that would spot all the ways it could fail.) And it's worth keeping unit tests around once written, as they can verify that the code doesn't get broken by future changes.
And if the test shows that the code doesn't work, then you have to debug it! There are many approaches, but I've found printing out intermediate values (whether using a logging framework or simply println() calls) to be the simplest and most flexible.
And for reference, all this can be rewritten much more simply:
fun String.leastP() = split(Regex("\\W"))
.filter{ it.length >= 2 && it.isPalindrome() }
.minOfOrNull{ it.length }
private fun String.isPalindrome() = this == reversed()
Here both functions are extension functions on String, which makes them a bit simpler to write and to call. I've added a restriction to 2+ characters. And if the input is empty, minOfOrNull() returns null instead of throwing a NoSuchElementException.
That version of isPalindrome() isn't quite as efficient as yours, because it creates a new temporary String each time it's called. In most programs, the greater simplicity will win out, but it's worth bearing in mind. Here's one that's longer but as efficient as in the question:
private fun String.isPalindrome()
= (0 until length / 2).all{ i -> this[i] == this[length - i - 1]}
Your newArray is a read-only list. When you call plus on it, the function does not modify the original list (after all, it is read-only). The List.plus() function returns a new list, which you are promptly discarding by not assigning it to any variable or property.
Then it crashes because it is unsafe to call minOf on an empty list.
Two different ways to fix this:
Make the newArray variable a var and replace newArray.plus(j) with newArray += j. The += operator, when used on a read-only list that is assigned to a mutable var variable, calls plus() on it and assigns the result back to the variable.
Initialize newArray as a MutableList using mutableListOf() and replace newArray.plus(j) with newArray += j. The += operator, when used with a MutableList, calls add() or addAll() on the MutableList, so it directly changes the original instance.
I didn’t check any of your logic. I’m only answering the question about why it’s crashing.
But as Gidds points out, the logic can be simplified a ton to achieve the same thing you’re trying to do using functions like filter(). A few odd things you’re doing:
Putting the result ofstring.split("") in a list for no reason
Using "" to split your string so it’s just a list of one-character Strings instead of a list of words. And you’re ignoring punctuation.
Filling newArray with indices so minOf will simply give you the first index that corresponded with being a palindrome, so it will always be 0.
Here’s how I might write this function (didn’t test it):
fun leastP(s: String): Int {
return s.split(" ")
.map { it.filter { c -> c.isLetter() } }
.filter { isPalindrome3(it) }
.minOfOrNull { it.length } ?: 0
}

Why can't I use map on Kotlins Regex Result sequence

I worked with Kotlin's Regex API to get occurences of some regular expression. I wanted to convert the finding directly into another object so I intuitively used map() on the result sequence.
I was very surprised that the map function is never called but forEach is working. This example should make it clear:
val regex = "a.".toRegex()
val txt = "abacad"
var counter = 0
regex.findAll(txt).forEach { counter++ }
println(counter) // 3
regex.findAll(txt).map { counter++ }
println(counter) // still 3 since map is not called
regex.findAll(txt).forEach { counter++ }
println(counter) // 6
My question is why? Did I oversee it in the documentation?
(tested on Kotlin 1.5.30)
findAll() returns a Sequence<MatchResult>. Operations on Sequence are classified either as intermediate or terminal. The documentation for the functions declares which type they are. map and onEach are intermediate. Their action is deferred until a terminal operation is made. forEach is terminal.
Manipulating a Sequence with map returns a new Sequence that will perform the mapping function only when it is actually iterated, such as by a call to forEach or using it in a for loop.
This is the purpose of Sequence, to defer mutating functional calls. It can reduce allocations of intermediate Lists, or in some cases avoid applying the mutations on every single item, such as if the terminal call in the chain is a find() call.

Async Wait Efficient Execution

I need to iterate 100's of ids in parallel and collect the result in list. I am trying to do it in following way
val context = newFixedThreadPoolContext(5, "custom pool")
val list = mutableListOf<String>()
ids.map {
val result:Deferred<String> = async(context) {
getResult(it)
}
//list.add(result.await()
}.mapNotNull(result -> list.add(result.await())
I am getting error at
mapNotNull(result -> list.add(result.await())
as await method is not available. Why await is not applicable at this place? Instead commented line
//list.add(result.await()
is working fine.
What is the best way to run this block in parallel using coroutine with custom thread pool?
Generally, you go in the right direction: you need to create a list of Deferred and then await() on them.
If this is exactly the code you are using then you did not return anything from your first map { } block, so you don't get a List<Deferred> as you expect, but List<Unit> (list of nothing). Just remove val result:Deferred<String> = - this way you won't assign result to a variable, but return it from the lambda. Also, there are two syntactic errors in the last line: you used () instead of {} and there is a missing closing parenthesis.
After these changes I believe your code will work, but still, it is pretty weird. You seem to mix two distinct approaches to transform a collection into another. One is using higher-order functions like map() and another is using a loop and adding to a list. You use both of them at the same time. I think the following code should do exactly what you need (thanks #Joffrey for improving it):
val list = ids.map {
async(context) {
getResult(it)
}
}.awaitAll().filterNotNull()

How to rewrite this in terms of R.compose

var take = R.curry(function take(count, o) {
return R.pick(R.take(count, R.keys(o)), o);
});
This function takes count keys from an object, in the order, in which they appear. I use it to limit a dataset which was grouped.
I understand that there are placeholder arguments, like R.__, but I can't wrap my head around this particular case.
This is possible thanks to R.converge, but I don't recommend going point-free in this case.
// take :: Number -> Object -> Object
var take = R.curryN(2,
R.converge(R.pick,
R.converge(R.take,
R.nthArg(0),
R.pipe(R.nthArg(1),
R.keys)),
R.nthArg(1)));
One thing to note is that the behaviour of this function is undefined since the order of the list returned by R.keys is undefined.
I agree with #davidchambers that it is probably better not to do this points-free. This solution is a bit cleaner than that one, but is still not to my mind as nice as your original:
// take :: Number -> Object -> Object
var take = R.converge(
R.pick,
R.useWith(R.take, R.identity, R.keys),
R.nthArg(1)
);
useWith and converge are similar in that they accept a number of function parameters and pass the result of calling all but the first one into that first one. The difference is that converge passes all the parameters it receives to each one, and useWith splits them up, passing one to each function. This is the first time I've seen a use for combining them, but it seems to make sense here.
That property ordering issue is supposed to be resolved in ES6 (final draft now out!) but it's still controversial.
Update
You mention that it will take some time to figure this out. This should help at least show how it's equivalent to your original function, if not how to derive it:
var take = R.converge(
R.pick,
R.useWith(R.take, R.identity, R.keys),
R.nthArg(1)
);
// definition of `converge`
(count, obj) => R.pick(R.useWith(R.take, R.identity, R.keys)(count, obj),
R.nthArg(1)(count, obj));
// definition of `nthArg`
(count, obj) => R.pick(R.useWith(R.take, R.identity, R.keys)(count, obj), obj);
// definition of `useWith`
(count, obj) => R.pick(R.take(R.identity(count), R.keys(obj)), obj);
// definition of `identity`
(count, obj) => R.pick(R.take(count, R.keys(obj)), obj);
Update 2
As of version 18, both converge and useWith have changed to become binary. Each takes a target function and a list of helper functions. That would change the above slightly to this:
// take :: Number -> Object -> Object
var take = R.converge(R.pick, [
R.useWith(R.take, [R.identity, R.keys]),
R.nthArg(1)
]);