I am currently learning kotlin and therefore following the kotlin track on exercism. The following exercise required me to calculate the Hamming difference between two Strings (so basically just counting the number of differences).
I got to the solution with the following code:
object Hamming {
fun compute(dnaOne: String, dnaTwo: String): Int {
if (dnaOne.length != dnaTwo.length) throw IllegalArgumentException("left and right strands must be of equal length.")
var counter = 0
for ((index, letter) in dnaOne.toCharArray().withIndex()) {
if (letter != dnaTwo.toCharArray()[index]) {
counter++
}
}
return counter
}
}
however, in the beginning I tried to do dnaOne.split("").withIndex() instead of dnaOne.toCharArray().withIndex() which did not work, it would literally stop after the first iteration and the following example
Hamming.compute("GGACGGATTCTG", "AGGACGGATTCT") would return 1 instead of the correct integer 9 (which only gets returned when using toCharArray)
I would appreciate any explanation
I was able to simplify this by using the built-in CharSequence.zip function because StringimplementsCharSequence` in Kotlin.
According to the documentation for zip:
Returns a list of pairs built from the characters of this and the [other] char sequences with the same index
The returned list has length of the shortest char sequence.
Which means we will get a List<Pair<Char,Char>> back (a list of pairs of letters in the same positions). Now that we have this, we can use Iterable.count to determine how many of them are different.
I implemented this as an extension function on String rather than in an object:
fun String.hamming(other: String): Int =
if(this.length != other.length) {
throw IllegalArgumentException("String lengths must match")
} else {
this.zip(other).count { it.first != it.second }
}
This also becomes a single expression now.
And to call this:
val ham = "GGACGGATTCTG".hamming("AGGACGGATTCT")
println("Hamming distance: $ham")
Related
I'm wanting to slice a range which I can do in Javascfript but am struggling in kotlin.
my current code is:
internal class blah {
fun longestPalindrome(s: String): String {
var longestP = ""
for (i in 0..s.length) {
for (j in 1..s.length) {
var subS = s.slice(i, j)
if (subS === subS.split("").reversed().joinToString("") && subS.length > longestP.length) {
longestP = subS
}
}
}
return longestP
}
and the error I get is:
Type mismatch.
Required:
IntRange
Found:
Int
Is there a way around this keeping most of the code I have?
As the error message says, slice wants an IntRange, not two Ints. So, pass it a range:
var subS = s.slice(i..j)
By the way, there are some bugs in your code:
You need to iterate up to the length minus 1 since the range starts at 0. But the easier way is to grab the indices range directly: for (i in s.indices)
I assume j should be i or bigger, not 1 or bigger, or you'll be checking some inverted Strings redundantly. It should look like for (j in i until s.length).
You need to use == instead of ===. The second operator is for referential equality, which will always be false for two computed Strings, even if they are identical.
I know this is probably just practice, but even with the above fixes, this code will fail if the String contains any multi-code-unit code points or any grapheme clusters. The proper way to do this would be by turning the String into a list of grapheme clusters and then performing the algorithm, but this is fairly complicated and should probably rely on some String processing code library.
class Solution {
fun longestPalindrome(s: String): String {
var longestPal = ""
for (i in 0 until s.length) {
for (j in i + 1..s.length) {
val substring = s.substring(i, j)
if (substring == substring.reversed() && substring.length > longestPal.length) {
longestPal = substring
}
}
}
return longestPal
}
}
This code is now functioning but unfortunately is not optimized enough to get through all test cases.
I have a function with a for-loop:
fun List<Int>.customSum(sumFunction: (Int) -> Boolean): Int {
var sum = 0
for (item in this) {
if (sumFunction(item))
sum += item
}
return sum
}
I want to know how I can write the above in functional style. I know that I have to use this.reduce(), but don't know exactly how to implement it.
return filter(sumFunction).sum()
Should be self-explanatory.
You can’t use reduce because it doesn’t let you reject the first element.
With fold it would be:
return fold(0) { a, b ->
if(sumFunction(b)) a + b else a
}
I can think if two ways to achieve that:
The first one is by using sumOf {...}:
.
fun List<Int>.customSum(sumFunction: (Int) -> Boolean): Int {
return sumOf {
if (sumFunction(it)) it else 0
}
}
The second one is by using filter {...} then sum():
.
fun List<Int>.customSum(sumFunction: (Int) -> Boolean): Int {
return filter(sumFunction).sum()
}
return this.reduce { sum, n -> if (sumFunction(n)) sum + n else 0}
If you really want to use reduce for some reason you can - but you need to add that 0 to the head of the list as your "start state":
fun List<Int>.customSum(sumFunction: (Int) -> Boolean): Int {
val stuff = listOf(0) + this
return stuff.reduce { a, b -> a + if (sumFunction(b)) b else 0 }
}
You have to do that because reduce is really there to combine a bunch of items, which is why for the first iteration you get the first two items in the list. You don't get to handle them separately, which is why you need to throw that 0 in there to get past that first step, and get to a point where you can just do your checking on the second parameter and ignore the first one, treating it as an accumulator instead of another item you also need to check.
That behaviour is what fold is for - with that function you pass in an initial state (which can be a completely different type from your items, since you're not just smushing them together to create a new value like with reduce) and then on each iteration you get that state and an item.
You can handle the item as you like, and then make changes to the accumulator state depending on the result. Which is exactly the behaviour of your for loop! fold is just a functional way to write one. Tenfour04's answer is how you'd do it - it's the right tool for the job here!
I would like to generate random numbers from a union of ranges in Kotlin. I know I can do something like
((1..10) + (50..100)).random()
but unfortunately this creates an intermediate list, which can be rather expensive when the ranges are large.
I know I could write a custom function to randomly select a range with a weight based on its width, followed by randomly choosing an element from that range, but I am wondering if there is a cleaner way to achieve this with Kotlin built-ins.
Suppose your ranges are nonoverlapped and sorted, if not, you could have some preprocessing to merge and sort.
This comes to an algorithm choosing:
O(1) time complexity and O(N) space complexity, where N is the total number, by expanding the range object to a set of numbers, and randomly pick one. To be compact, an array or list could be utilized as the container.
O(M) time complexity and O(1) space complexity, where M is the number of ranges, by calculating the position in a linear reduction.
O(M+log(M)) time complexity and O(M) space complexity, where M is the number of ranges, by calculating the position using a binary search. You could separate the preparation(O(M)) and generation(O(log(M))), if there are multiple generations on the same set of ranges.
For the last algorithm, imaging there's a sorted list of all available numbers, then this list can be partitioned into your ranges. So there's no need to really create this list, you just calculate the positions of your range s relative to this list. When you have a position within this list, and want to know which range it is in, do a binary search.
fun random(ranges: Array<IntRange>): Int {
// preparation
val positions = ranges.map {
it.last - it.first + 1
}.runningFold(0) { sum, item -> sum + item }
// generation
val randomPos = Random.nextInt(positions[ranges.size])
val found = positions.binarySearch(randomPos)
// binarySearch may return an "insertion point" in negative
val range = if (found < 0) -(found + 1) - 1 else found
return ranges[range].first + randomPos - positions[range]
}
Short solution
We can do it like this:
fun main() {
println(random(1..10, 50..100))
}
fun random(vararg ranges: IntRange): Int {
var index = Random.nextInt(ranges.sumOf { it.last - it.first } + ranges.size)
ranges.forEach {
val size = it.last - it.first + 1
if (index < size) {
return it.first + index
}
index -= size
}
throw IllegalStateException()
}
It uses the same approach you described, but it calls for random integer only once, not twice.
Long solution
As I said in the comment, I often miss utils in Java/Kotlin stdlib for creating collection views. If IntRange would have something like asList() and we would have a way to concatenate lists by creating a view, this would be really trivial, utilizing existing logic blocks. Views would do the trick for us, they would automatically calculate the size and translate the random number to the proper value.
I implemented a POC, maybe you will find it useful:
fun main() {
val list = listOf(1..10, 50..100).mergeAsView()
println(list.size) // 61
println(list[20]) // 60
println(list.random())
}
#JvmName("mergeIntRangesAsView")
fun Iterable<IntRange>.mergeAsView(): List<Int> = map { it.asList() }.mergeAsView()
#JvmName("mergeListsAsView")
fun <T> Iterable<List<T>>.mergeAsView(): List<T> = object : AbstractList<T>() {
override val size = this#mergeAsView.sumOf { it.size }
override fun get(index: Int): T {
if (index < 0 || index >= size) {
throw IndexOutOfBoundsException(index)
}
var remaining = index
this#mergeAsView.forEach { curr ->
if (remaining < curr.size) {
return curr[remaining]
}
remaining -= curr.size
}
throw IllegalStateException()
}
}
fun IntRange.asList(): List<Int> = object : AbstractList<Int>() {
override val size = endInclusive - start + 1
override fun get(index: Int): Int {
if (index < 0 || index >= size) {
throw IndexOutOfBoundsException(index)
}
return start + index
}
}
This code does almost exactly the same thing as short solution above. It only does this indirectly.
Once again: this is just a POC. This implementation of asList() and mergeAsView() is not at all production-ready. We should implement more methods, like for example iterator(), contains() and indexOf(), because right now they are much slower than they could be. But it should work efficiently already for your specific case. You should probably test it at least a little. Also, mergeAsView() assumes provided lists are immutable (they have fixed size) which may not be true.
It would be probably good to implement asList() for IntProgression and for other primitive types as well. Also you may prefer varargs version of mergeAsView() than extension function.
As a final note: I guess there are libraries that does this already - probably some related to immutable collections. But if you look for a relatively lightweight solution, it should work for you.
In the sample below, the function should return a non-null data.
Since the data could be changed in the process, it needs to be var, and can only be nullable to start with.
I can't use lateinit because the first call of if (d == null) will throw.
After the process it will be assigned a non-null data, but the return has to use the !! (double bang or non-null assertion operator).
What is the best approach to avoid the !!?
fun testGetLowest (dataArray: List<Data>) : Data {
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return d!!
}
If you don't like !! then supply a default value for it. You'll realize you can only supply the default value if the list is not empty, but, as you said, the list is already known to be non-empty. The good part of this story is that the type system doesn't track list size so when you say dataArray[0], it will take your word for it.
fun testGetLowest(dataArray: List<Data>) : Data {
var d: Data = dataArray[0]
for (i in 1 until dataArray.size) {
if (d.level < dataArray[i].level) {
d = dataArray[i]
}
}
return d
}
Normally, you can and should lean on the compiler to infer nullability. This is not always possible, and in the contrived example if the inner loop runs but once d is non-null. This is guaranteed to happen if dataArray has at least one member.
Using this knowledge you could refactor the code slightly using require to check the arguments (for at least one member of the array) and checkNotNull to assert the state of the dataArray as a post-condition.
fun testGetLowest (dataArray: List<Data>) : Data {
require(dataArray.size > 0, { "Expected dataArray to have size of at least 1: $dataArray")
var d: Data? = null
for (i in dataArray.indecs) {
if (d == null) {// first run
d = dataArray[i]
} else if {
d.level < dataArray[i].level
d = dataArray[i]
}
}
return checkNotNull(d, { "Expected d to be non-null through dataArray having at least one element and d being assigned in first iteration of loop" })
}
Remember you can return the result of a checkNotNull (and similar operators):
val checkedD = checkNotNull(d)
See Google Guava's Preconditions for something similar.
Even if you were to convert it to an Option, you would still have to deal with the case when dataArray is empty and so the value returned is undefined.
If you wanted to make this a complete function instead of throwing an exception, you can return an Option<Data> instead of a Data so that the case of an empty dataArray would return a None and leave it up to the caller to deal with how to handle the sad path.
How to do the same check, and cover the empty case
fun testGetLowest(dataArray: List<Data>)
= dataArray.minBy { it.level } ?: throw AssertionError("List was empty")
This uses the ?: operator to either get the minimum, or if the minimum is null (the list is empty) throws an error instead.
The accepted answer is completly fine but just to mentioned another way to solve your problem by changing one line in your code: return d ?: dataArray[0]
I was solving a problem on codeforces in which I had to sum up the digits of a big number (it can have up to 100k digits) and I'd have to repeat that process until there is only one digit left and count the number of times I did that and I came up with a working solution, however I'd like to know if some things could have been done in a more "Kotlin-ish like way", so given:
fun main(args: Array<String>) {
println(transform(readLine()!!))
}
fun transform(n: String): Int {
var count = 0
var sum : Int
var s = n
while(s.length > 1) {
sum = (0 until s.length).sumBy { s[it].toInt() - '0'.toInt() }
s = sum.toString()
count++
}
return count
}
sum = (0 until s.length).sumBy { s[it].toInt() - '0'.toInt() } is there a way to I guess map the sum of digits in the string to the sum variable, or in general a better approach than the one I used?
When converting a Char to an Int it converts it to the ASCII value so I had to add "-'0'.toInt()" is there a faster way (not that it's too much to write, asking out of curiosity)?
How to make the String n mutable without creating a new String s and manipulating it? Or is that the desired (and only) way?
P.S. I'm a beginner with Kotlin.
When converting a Char to an Int it converts it to the ASCII value so I had to add "-'0'.toInt()" is there a faster way (not that it's too much to write, asking out of curiosity)?
You can simply write s[it] - '0', because subtracting Chars in Kotlin already gives you an Int:
public class Char ... {
...
/** Subtracts the other Char value from this value resulting an Int. */
public operator fun minus(other: Char): Int
...
}
But why are looping over the indexes when you could loop over the Chars directly?
sum = s.sumBy { it - '0' }
This is a functional (and recursive) style to solve it:
private fun sum(num: String, count: Int) : Int {
return num
//digit to int
.map { "$it".toInt() }
//sum digits
.sum()
//sum to string
.toString()
//if sum's length is more than one, do it again with incremented count. Otherwise, return the current count
.let { if (it.length > 1) sum(it, count + 1) else count }
}
And you call it like this:
val number = "2937649827364918308623946..." //and so on
val count = sum(number, 0)
Hope it helps!