A common pattern when doing coding challenges is to read many lines of input. Assuming you don't know in advance how many lines, you want to read until EOF (readLine returns null).
Also as a preface, I don't want to rely on java.utils.* since I'm coding in KotlinNative, so no Scanner.
I would like to maybe do something like
val lines = arrayListOf<String>()
for (var line = readLine(); line != null; line = readLine()) {
lines.add(line)
}
But that clearly isn't valid Kotlin. The cleanest I can come up with is:
while (true) {
val line = readLine()
if (line == null) break
lines.add(line)
}
This works, but it just doesn't seem very idiomatic. Is there a better way to read all lines into an array, without using a while/break loop?
generateSequence has the nice property that it will complete if the internal generator returns null and accepts only a single iteration, so the following code could be valid:
val input = generateSequence(::readLine)
val lines = input.toList()
Then like s1m0nw1's answer you can use any of the available Sequence<String> methods to refine this as desired for your solution.
I guess you're talking about reading from System.in (stdin) here. You could make that work with sequences:
val lines = generateSequence(readLine()) {
readLine()
}
lines.take(5).forEach { println("read: $it") }
We begin our sequence with a first readLine (the sequence's seed) and then read the next line until null is encountered. The sequence is possibly infinite, therefore we just take the first five inputs in the example. Read about details on Sequence here.
Related
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!)
Background info
A common pattern in some programming languages is to have a function that when called, returns the next value until the end of the finite sequence is reached, in which case it keeps returning null.
A common example in Java is this:
void printAll(BufferedReader reader) {
String line;
// Assigns readLine value to line, and then check if not null
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
It is similar to the iterator in the Iterator design pattern, but the iterator has a next(): Object and a hasNext(): Boolean, whereas the BufferedReader has no hasNext() check functionality, but only the form next(): Object?, in which the returned object can be null to mark the end of the sequence. I call functions such as next() a "next function" (or maybe "yield" function), but I don't know if there is an word for this pattern.
In Java, an expression can contain assignments, which allows constructs such as: (line = reader.readLine()) != null. This code assigns the nullable value of readLine() to line, and then check whether the value in line is not null. But Kotlin doesn't allow such constructs, because in Kotlin, an assignment is not an expression, so it cannot be used as loop condition in Kotlin.
Question
What are the possible patterns in Kotlin to loop through the finite number of values returned by a next function, such as readLine()?
(Next functions can also be found for example in ZipInputStream, to go to the next zip entry.)
I'm not simply looking for a Kotlin workaround for this problem, because I can program that myself without problems. I'm looking to explore the possible patterns so that people can select one that suits their needs.
I have found some patterns myself, which I'll post here as an answer, but there may be more patterns out there, which would be interesting to know.
I've ordered to solutions by (what I believe) the best solution in descending order.
Solution 1: using built-in generateSequence (recommended)
I just found out that Kotlin has a built-in standalone generateSequence() function (located in the kotlin.sequences package).
generateSequence { br.readLine() }
.forEach { line ->
println("Line: $line")
}
generateSequence accepts a code block that you can provide, that must generates a value. In this case, br.readLine() is the code block, and generates either a String, or null if the end is reached. generateSequence generates a sequence that internally calls readLine() when the next value is requested from the sequence, until readLine() return null, which terminates the sequence. So sequences in Kotlin are lazy: they don't read neither know all the values ahead of time, only a single readLine() is called when for example forEach processes a single line. This laziness is usually exactly what you want, because it saves memory and minimizes an initial delay. To change it to eagerly, you can append generateSequence { br.readLine() } with .toList().
Pros 1: no additional variables.
Pros 2: just one construct (generateSequence).
Pros 3: returns a Sequence, so you can chain additional methods such as filter().
Pros 4: any sign of nullability is abstracted away. (No null keywords, nor ? nor ! operators.)
Pros 5: adheres a functional programming style.
IMO, this is the cleanest solution that I've seen so far.
Solution 2: while true loop with elvis break call
while (true) {
val line = br.readLine() ?: break
println("Line: $line")
}
Pros: no additional variables.
Cons: some people don't like while-true loops and break statements.
Solution 3: do-while with safe call also
do {
val line = br.readLine()?.also { line ->
println("Line: $line")
}
} while (line != null)
Pros: no additional variables.
Cons: less readable than other solutions.
Solution 4: next before start and at end of each iteration
This is probably the most common solution for Java programmers who are new to Kotlin.
var line = br.readLine()
while (line != null) {
println("Line: $line")
line = br.readLine()
}
Cons 1: duplicated next (readLine) call and a duplicated assignment.
Cons 2: reassignable var.
Solution 5: while loop with assignment using also
This is the solution generated by IntelliJ when converting Java to Kotlin code:
var line: String?
while (br.readLine().also { line = it } != null) {
println("Line: $line")
}
Cons: line is declared as nullable, even though it can never be null inside the loop. So you'll often have to use the not-null assertion operator if you want to access members of line, which you can limit to one assertion using:
var nullableLine: String?
while (br.readLine().also { nullableLine = it } != null) {
val line = nullableLine!!
println("Line: $line")
}
Cons 1: requires not-null assertion even though it can never be null inside the loop.
Cons 2: reassignable var.
Cons 3: less readable than other solutions.
Note that if you change var line: String? to var line: String, the code still compiles, but it will throw a NPE when line becomes null, even though there are no not-null assertions used.
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
}
I need to read a file from disk, filter out some rows based on conditions, then return the result as a single stream/sequence, not a sequence of strings. The file is too large to hold in memory all at once, so it must be treated as a Stream/Sequence throughout processing. This is what I tried.
File(filename).bufferedReader()
// break into lines
.lineSequence()
// filter each line based on condition
.filter{meetsSomeCondition(it)}
// add newline back in
.map{(it+"\n").byteInputStream()}
// reduce back into a single stream with Java's SequenceInputStream
.reduce<InputStream, ByteArrayInputStream> { acc, i -> SequenceInputStream(acc, i) }
This works when testing on a small file, but when using a large file it errors with a StackOverFlow exception. It seems that Java's SequenceInputStream can't handle repeatedly nesting itself like I do with the reduce call.
I see that SequenceInputStream also has a way of accepting an Enumeration argument that takes a List of elements. But that's the problem, as far as I can tell, it doesn't seem to accept a Stream.
Your code does not really do what you think it does. reduce() is a terminal operation, meaning that it consumes all elements in the sequence. After reduce() line the whole file has been read already. Also, it is not SequenceInputStream that does not support such reduce operation. You created a very long chain of objects. SequenceInputStream objects does not really know they were chained like this and they can't do too much about it.
Instead, you need to keep a sequence "alive" and create an InputStream that will read from the source sequence whenever required. I don't think there is an utility like this in the stdlib. It is a very specialized requirement.
The easiest is to create a sequence of bytes and then provide InputStream which reads from it:
File(filename).bufferedReader()
.lineSequence()
.filter{meetsSomeCondition(it)}
.flatMap { "$it\n".toByteArray().asIterable() }
.asInputStream()
fun Sequence<Byte>.asInputStream() = object : InputStream() {
val iter = iterator()
override fun read() = if (iter.hasNext()) iter.next().toUByte().toInt() else -1
}
However, sequence of bytes isn't really the best for performance. We can optimize it by reading line by line, so creating a sequence of strings or byte arrays:
File(filename).bufferedReader()
.lineSequence()
.filter{meetsSomeCondition(it)}
.map { "$it\n".toByteArray() }
.asInputStream()
fun Sequence<ByteArray>.asInputStream() = object : InputStream() {
val iter = iterator()
var curr = iter.next()
var pos = 0
override fun read(): Int {
return when {
pos < curr.size -> curr[pos++].toUByte().toInt()
!iter.hasNext() -> -1
else -> {
curr = iter.next()
pos = 0
read()
}
}
}
}
(Note this implementation of asInputStream() will fail for empty sequence)
Still, there is much room for improvement regarding the performance. We read from sequence line by line, but we still read from InputStream byte by byte. To improve it further we would need to implement more methods of InputStream to read in bigger chunks. If you really care about the performance then I suggest looking into BufferedInputStream implementation and try to re-use some of its codebase.
Also, remember to close the file reader that was created in the first step. It won't close automatically when InputStream will be closed.
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()