Pass argument with multiple return points - kotlin

Assuming I have a function example() that has a parameter param: Int?. I would like to call it so that the argument can be passed with certain conditionals.
To try to explain better, let's define the example() function:
fun example(prev: Int, param: Int?) = doSomething(prev, param)
So assuming that under certain conditions, the value passed as an argument can be null or any other value. Naturally, I would do something like this:
val param = if (x() > 100) null
else if (x() < 0) 123
else 456
example(123, param)
Another way to get this to work would be inline val like this:
example(123, if (x() > 100) null
else if (x() < 0) 123
else 456)
The problem is that I have a larger conditional in my real code and it ends up being very "visually accumulated". Both examples works, but I wanted to not using else or when under these conditions. It would be something like:
example(123, run {
if (x() > 100)
return null
if (x() < 0)
return 123
return 456
})
However, the code above does not work as expected. The return points actually cause the entire flow of the function to return, and are not meant to return as the argument itself.
What I expect, in reality, would be something like the function below:
fun generateExampleArgument(): Int? {
if (x() > 100)
return null
if (x() < 0)
return 123
return 456
}
// Then:
example(123, generateExampleArgument())

In regard of readability and quote "... larger conditionals ..." the when clause is better than the if ... else if ... one:
example(
123,
when {
x() > 100 -> null
x() < 0 -> 123
else -> 456
}
)
Another possibility is to use a function to pre-calculate the result for the second argument of example() or a lambda function, which moves the execution of that function into example() itself:
fun example(prev: Int, fn: (Int) -> Int?, x: Int) = doSomething(prev, fn(x))
val myLambda = { x: Int ->
when {
x > 100 -> null
x < 0 -> 123
else -> 456
}
}
example(120, myLambda, 111)

After many attempts, I finally figured out how to solve it. My problem is that when using just return it actually instructs the return to the "larger function context", not on the argument itself, as expected.
The solution is to use run with return#run:
example(123, run {
if (x() > 100)
return#run null
if (x() < 0)
return#run 123
456
})

Related

assignments are not expressions, and only expressions are allowed in this context --- how to resolve this error

I am trying to find the find the result of num1 raised to the power num2:
This is my code ->
fun power(num1 : Int, num2: Int): Int {
var result = 1
while (num2 != 0) {
return result *= num1
num2--
}
}
But the above code is producing the following error -->
Calculator.kt:30:16: error: assignments are not expressions, and only expressions are allowed in this context
return result *= num1
^
Calculator.kt:33:5: error: a 'return' expression required in a function with a block body ('{...}')
}
^
I have read a number of articles but not able to understand. Any help will be highly appreciated.
Thank you
An expression is something that evaluates to a value. An assignment is something that assigns a value to a variable or property.
x *= y is an assignment that is shorthand for x = x * y.
You cannot return an assignment, because it does not evaluate to a value. An assignment contains an expression on the right side of the equals sign, but as a whole does not represent an expression.
There are some other syntax problems you have. You can't modify a function paramter's value (num2-- isn't allowed).
The logic also doesn't make sense. return returns an expression immediately. To fix your code, you need to create a local variable from num2, and move the return statement to the end.
fun power(num1 : Int, num2: Int): Int {
var result = 1
var count = num2
while (count != 0) {
result *= num1
count--
}
return result
}
FYI, there's a function called repeat that is simpler than using a while loop with a counter. It runs the code inside the brackets by the number of times you give.
fun power(num1 : Int, num2: Int): Int {
var result = 1
repeat(num2) {
result *= num1
}
return result
}
You function contains multiple errors, I suggest you to study Kotlin, here a reference. Kotlin website has some more material.
Back to your problem, I have modified your function:
fun power(num1 : Int, num2: Int): Int {
var result = 1
var num2_local = num2
while (num2_local != 0) {
result *= num1
num2_local--
}
return result
}
Problems with your version:
you return from the function immediately
basic types args passed to kotlin functions are passed by const copy, this means that you cannot modify them (e.g num2--)
If you keep a local modifiable reference (var keyword) withing your function, then you achieve your goal

Kotlin android if statement

Kotlin, Android studio, v. 4.0.1
I have a progress bar in my app that ranges from 0 to 10.
When it is at 0, the following code gives an error (which is logic):
val rand = Random().nextInt(seekBar.progress) + 1
resultsTextView.text = rand.toString()
So I want to add an if statement before to filter out the 0. If the progress bar is at 0, the 'rand' should be at 0 too.
I have the following but it does not work:
rollButton.setOnClickListener {
val ggg = seekBar.progress
if (ggg = 0) {
val rand = 0
} else {
val rand = Random().nextInt(seekBar.progress) + 1
}
resultsTextView.text = rand.toString()
}
Any idea?
rand is defined in the scope of if and else, you cannot use it outside that scope and instead of comparing ggg with 0 (==) you are setting its value to 0 (=). And if you want to reassign rand, it cannot be a val which can only be assigned once, make it a var instead.
Do it like this:
rollButton.setOnClickListener {
val ggg = seekBar.progress
var rand = 0;
if (ggg != 0) {
rand = Random().nextInt(seekBar.progress) + 1
}
resultsTextView.text = rand.toString()
}
Replace if (ggg = 0) { with if (ggg == 0) {.
A more Kotlin approach might be along the lines of:
rollButton.setOnClickListener {
val rand = seekBar.progress.let {
if (it == 0)
0
else
Random().nextInt(it) + 1
}
resultsTextView.text = rand.toString()
}
This uses an if() expression (not a statement) to avoid any mutable variables.  And by getting the value of seekBar.progress only once, it also avoids any issues if the bar gets moved while that's running.
However, I have to check if that's actually what you want:
Bar position | Possible values
0 | 0
1 | 1
2 | 1–2
3 | 1–3
… | …
That looks wrong to me…  Do you really want to exclude zero in all but the first case?  If not, then you could just move the addition inside the call — Random().nextInt(seekBar.progress + 1) — and avoid the special case entirely.

Error in Print prime number using high order functions in kotlin

val listNumbers = generateSequence(1) { it + 1 }
val listNumber1to100 = listNumbers.takeWhile { it < 100 }
val secNum:Unit = listNumber1to100.forEach {it}
println(listNumber1to100.asSequence().filter { it%(listNumber1to100.forEach { it })!=0 }.toList())
I have an error in reminder sign!
This is Error: None of the following functions can be called with the arguments supplied
In your first approach, the error appears in this line:
it%(listNumber1to100.forEach { it })
A Byte, Double, Float, Int, Long or Short is prefered right after the % operator, however, forEach is a function which the return type is Unit.
In your second approach, you have the correct expression in isPrime(Int). Here are some suggestions for you:
listNumber1to100 is excluding 100 in your code, if you want to include 100 in listNumber1to100, the lambda you pass to takeWhile should be changed like this:
val listNumber1to100 = listNumbers.takeWhile { it <= 100 }
listNumber1to100.asSequence() is redundant here since listNumber1too100 is itself a TakeWhileSequence which implements Sequence.
isPrime(Int) is a bit confusing since it is check for isComposite and it does not work for every input it takes(it works for 1 to 99 only). I will rewrite it in this way:
fun isPrime(num: Int): Boolean = if (num <= 1) false else !(2..num/2).any { num % it == 0 }
Since prime number must be positive and 1 is a special case(neither a prime nor composite number), it just return false if the input is smaller or equal to 1. If not, it checks if the input is divisible by a range of number from 2 to (input/2). The range ends before (input/2) is because if it is true for num % (num/2) == 0, it is also true for num % 2 == 0, vise versa. Finally, I add a ! operator before that because a prime number should not be divisible by any of those numbers.
Finally, you can filter a list by isPrime(Int) like this:
println(listNumber1to100.filter(::isPrime).toList())
PS. It is just for reference and there must be a better implementation than this.
To answer your question about it, it represents the only lambda parameter inside a lambda expression. It is always used for function literal which has only one parameter.
The error is because the expression: listNumber1to100.forEach { it } - is not a number, it is a Unit (ref).
The compiler try to match the modulo operator to the given function signatures, e.g.: mod(Byte) / mod(Int) / mod(Long) - etc.
val listNumbers = generateSequence(1) { it + 1 }
val listNumber1to100 = listNumbers.takeWhile { it < 100 }
fun isPrime(num: Int): Boolean = listNumber1to100.asSequence().any { num%it==0 && it!=num && it!=1 }
println(listNumber1to100.asSequence().filter { !isPrime(it)}.toList())
I found this solution and worked
But why can I have a non-number here in the right side of reminder

Using Kotlin WHEN clause for <, <=, >=, > comparisons

I'm trying to use the WHEN clause with a > or < comparison.
This doesn't compile. Is there a way of using the normal set of boolean operators (< <=, >= >) in a comparison to enable this?
val foo = 2
// doesn't compile
when (foo) {
> 0 -> doSomethingWhenPositive()
0 -> doSomethingWhenZero()
< 0 -> doSomethingWhenNegative()
}
I tried to find an unbounded range comparison, but couldn't make this work either? Is it possible to write this as an unbounded range?
// trying to get an unbounded range - doesn't compile
when (foo) {
in 1.. -> doSomethingWhenPositive()
else -> doSomethingElse()
}
You can put the whole expression in the second part, which is OK but seems like unnecessary duplication. At least it compiles and works.
when {
foo > 0 -> doSomethingWhenPositive()
foo < 0 -> doSomethingWhenNegative()
else -> doSomethingWhenZero()
}
But I'm not sure that is any simpler than the if-else alternative we have been doing for years. Something like:
if ( foo > 0 ) {
doSomethingWhenPositive()
}
else if (foo < 0) {
doSomethingWhenNegative()
}
else {
doSomethingWhenZero()
}
Of course, real world problems are more complex than the above, and the WHEN clause is attractive but doesn't work as I expect for this type of comparison.
Even a flexible language such as Kotlin doesn't have a "elegant" / DRY solution for each and every case.
You can write something like:
when (foo) {
in 0 .. Int.MAX_VALUE -> doSomethingWhenPositive()
0 -> doSomethingWhenZero()
else -> doSomethingWhenNegative()
}
But then you depend on the variable type.
I believe the following form is the most idiomatic in Kotlin:
when {
foo > 0 -> doSomethingWhenPositive()
foo == 0 -> doSomethingWhenZero()
else -> doSomethingWhenNegative()
}
Yeah... there is some (minimal) code duplication.
Some languages (Ruby?!) tried to provide an uber-elegant form for any case - but there is a tradeoff: the language becomes more complex and more difficult for a programmer to know end-to-end.
My 2 cents...
We can use let to achieve this behaviour.
response.code().let {
when {
it == 200 -> handleSuccess()
it == 401 -> handleUnauthorisedError()
it >= 500 -> handleInternalServerError()
else -> handleOtherErrors()
}
}
Hope this helps
The grammar for a when condition is as follows:
whenCondition (used by whenEntry)
: expression
: ("in" | "!in") expression
: ("is" | "!is") type
;
This means that you can only use is or in as special cases that do not have to be a full expression; everything else must be a normal expression. Since > 0 is not a valid expression this will not compile.
Furthermore, ranges are closed in Kotlin, so you cannot get away with trying to use an unbounded range.
Instead you should use the when statement with a full expression, as in your example:
when {
foo > 0 -> doSomethingWhenPositive()
foo < 0 -> doSomethingWhenNegative()
else -> doSomethingWhenZero()
}
Or alternatively:
when {
foo < 0 -> doSomethingWhenNegative()
foo == 0 -> doSomethingWhenZero()
foo > 0 -> doSomethingWhenPositive()
}
which may be more readable.
You want your code to be elegant, so why stay on the when expression. Kotlin is flexible enough to build a new one using extension.
First we should claim that we can only pass a Comparable<T> here because you have to compare the value.
Then, we have our framework:
fun <T: Comparable<T>> case(target: T, tester: Tester<T>.() -> Unit) {
val test = Tester(target)
test.tester()
test.funFiltered?.invoke() ?: return
}
class Tester<T : Comparable<T>>(val it: T) {
var funFiltered: (() -> Unit)? = null
infix operator fun Boolean.minus(block: () -> Unit) {
if (this && funFiltered == null) funFiltered = block
}
fun lt(arg: T) = it < arg
fun gt(arg: T) = it > arg
fun ge(arg: T) = it >= arg
fun le(arg: T) = it <= arg
fun eq(arg: T) = it == arg
fun ne(arg: T) = it != arg
fun inside(arg: Collection<T>) = it in arg
fun inside(arg: String) = it as String in arg
fun outside(arg: Collection<T>) = it !in arg
fun outside(arg: String) = it as String !in arg
}
After that we can have elegant code like:
case("g") {
(it is String) - { println("hello") } // normal comparison, like `is`
outside("gg") - { println("gg again") } // invoking the contains method
}
case(233) {
lt(500) - { println("less than 500!") }
// etc.
}
If you're happy, you can rename the minus function to compareTo and return 0. In such way, you can replace the - with =>, which looks like scala.
Mo code that works:
val fishMan = "trouttrout"
when (fishMan.length){
0 -> println("Error")
in (3..12) -> println("Good fish name")
else -> println ("OK fish name")
}
Result:
Good fish name
I use this:
val foo = 2
when (min(1, max(-1, foo))) {
+1 -> doSomethingWhenPositive()
0 -> doSomethingWhenZero()
-1 -> doSomethingWhenNegative()
}
The imports needed for this case are:
import java.lang.Integer.min
import java.lang.Integer.max
but they can be generalized to other types.
You're welcome!
I found a bit hacky way that can help you in mixing greater than, less than, or any other expression with other in expressions.
Simply, a when statement in Kotlin looks at the "case", and if it is a range, it sees if the variable is in that range, but if it isn't, it looks to see if the case is of the same type of the variable, and if it isn't, you get a syntax error. So, to get around this, you could do something like this:
when (foo) {
if(foo > 0) foo else 5 /*or any other out-of-range value*/ -> doSomethingWhenPositive()
in -10000..0 -> doSomethingWhenBetweenNegativeTenKAndZero()
if(foo < -10000) foo else -11000 -> doSomethingWhenNegative()
}
As you can see, this takes advantage of the fact that everything in Kotlin is an expression. So, IMO, this is a pretty good solution for now until this feature gets added to the language.

Kotlin null safety?

Let's have a function foo and a class Bar:
fun foo(key: String): String? {
// returns string or null
}
class Bar(x: String, y: String) {
// ...
}
Now, let's have the code:
val x = foo("x")
val y = foo("y")
if (x.isNotEmpty() && y.isNotEmpty())
return Bar(x, y)
The problem is that this code will not compile. Since it needs the Bar(x!!, y!!).
However when I replace the function with its content, !! are not needed.
val x = foo("x")
val y = foo("y")
if ((x != null && x.length() > 0) && (y != null && y.length() > 0))
return Bar(x, y)
Why it is not possible to resolve the null check from the function .isNotEmpty()?
This is possible in theory, but it would mean that either
1. The declaration of isNotEmpty() must convey to the compiler the fact that x is guaranteed to be non-null if the result is true
2. A change to a body of any function may cause its call sites to fail to compile.
Option 2 is definitely unacceptable. Option 1 requires a rather expressive mechanism in the type system, which we decided not to add at the moment, because it is likely to complicate things for the user.
We are planning to support something like this with inline functions, but it's still under consideration.