How to write Kotlin conditional
if (a) b else c
using when
when (a) {
true -> b
else -> c // or false -> c
}
as you see: if/else is easier to grasp in that particular case.
If a wouldn't just be a boolean, when may make more sense, e.g.:
when (a) {
is Scalable -> TODO("scale it")
else -> TODO("whatever makes sense")
}
New or other conditions can then easily be added... this however doesn't make much sense for simple boolean evaluations.
As you added a comment about readability and your sample shows just an assignment, maybe the following is more readable for you?
val c = if (x) "a"
else "b"
or if you want to simulate something similar as a ternary operator:
val c = x.takeIf { it }?.let { "a" }
?: "b"
I hope however that you get the point... if you just have a boolean use if/else. But if your condition is more complex, also something like takeIf might be helpful... (I am really sorry that I used x.takeIf { it } here... but it should rather be a demonstration of what is possible).
Related
mood = "leet"
modifier = { message ->
val regex = """(L|e|t)""".toRegex()
//Clueless about what to do after this
}
THIS IS WHAT I CAME UP WITH SO FAR, THE QUESTION IN THE BOOK BIG NERD RANCH KOTLIN EDITION 2 SAYS "leet (or 1337): The narrator will speak in leetspeak, replacing letters with numbers and symbols that look similar. For example, ‘L’ becomes ‘1’; ‘E’ becomes ‘3’; ‘T’ becomes ‘7’. (Hint: Take a look at String’s replace function. There is a version that accepts a lambda as the second parameter.)"
This is the function they're telling you to look at, specifically this one:
inline fun CharSequence.replace(
regex: Regex,
noinline transform: (MatchResult) -> CharSequence
): String
Returns a new string obtained by replacing each substring of this char sequence that matches the given regular expression with the result of the given function transform that takes MatchResult and returns a string to be used as a replacement for that match.
So the lambda you provide is a function that takes a MatchResult
and does something with it, and returns a CharSequence (which can be a one-character long String). The replace function calls that lambda for every match that regex makes.
You get the general idea of what you're supposed to do? You have two parts here - the thing that identifies parts of the input string to process, and the thing that takes those matches and changes them into something else. The result is the original string with those changes made. So you need to come up with a regex and a transform that work together.
Nobody (probably) is going to tell you the answer because the point is figuring it out for yourself, but if you have any questions about things like regexes people will be happy to help you out! And speaking of, this site is extremely useful (I just used it myself to check I knew what I was doing): https://regex101.com/
Here is the implementation as pointed by #cactustictacs :
5 -> {
mood = "leet"
val regex: Regex = """[LET]""".toRegex()
modifier = { message ->
message.uppercase().replace(regex) { m ->
when (m.value) {
"L" -> "1"
"E" -> "3"
"T" -> "7"
else -> ""
}
}
}
}
and here is the another method almost same but with minor change using regex.replace()
5 -> {
mood = "leet"
val regex: Regex = """[LET]""".toRegex()
modifier = { message ->
regex.replace(message.uppercase()){m ->
when (m.value) {
"L" -> "1"
"E" -> "3"
"T" -> "7"
else -> ""
}
}
}
}
You can use it in place of m to make it slightly more concise.
For example, Char has a member function isLetter(). Is there any way to call it in a case? The code below does not work.
var ch:Char = null;
when(ch)
{
'*' -> print("You typed an asterisk.");
isLetter() -> print("You typed a letter.");
else -> print("You typed something.");
}
The uses of the parameter to when are quite restrictive: equality, order with < &c, inclusion with in, type checking with is, and that's about it. So for anything more complex, it's usually easier not to specify a parameter. In this case, the following isn't much less concise or clear:
when {
ch == '*' -> print("You typed an asterisk.")
ch.isLetter() -> print("You typed a letter.")
else -> print("You typed something.")
}
Some points worth noting:
Semicolons are not needed in Kotlin (except for a few rare ambiguous cases such as putting multiple statements on a line — which is rarely a good idea).
print() does not add a trailing newline, which can cause problems; println() is more common.
var ch: Char = null won't compile, because the type Char is not nullable. Either give a non-null default value, or make it nullable by specifying the type as Char?. However, in the latter case, you won't be able to call ch.isLetter(), because that would risk a NullPointerException. So you'd either need to add a null case before that, or handle the null in that check, either with ch != null && ch.isLetter(), or ch?.isLetter() == true — both of which are ugly.
There is, but there's also the possibility of it not being applicable in your situation.
when can be used without an argument, like this:
fun main() {
val ch: Char = 'd'
when {
ch.isLetter() -> println("It's a letter")
ch.isDigit() -> println("It's a number")
else -> println("It's neither a letter, nor a number")
}
}
But if you make ch nullable like var ch: Char? = null, you won't be able to call member functions in that when clause.
You can check whether ch is in a specific CharCategory. For isLetter() those are the following five:
when(ch)
{
'*' -> print("You typed an asterisk.")
in CharCategory.UPPERCASE_LETTER,
in CharCategory.LOWERCASE_LETTER,
in CharCategory.TITLECASE_LETTER,
in CharCategory.MODIFIER_LETTER,
in CharCategory.OTHER_LETTER -> print("You typed a letter.")
else -> print("You typed something.")
}
first in your code var ch:Char = null
it must be Char?
the solution is simple don't put argument:
var ch:Char = 'c'
when {
ch == '*' -> println("*")
ch.isLetter() -> println("letter")
ch.isDigit() -> println("digit")
}
hope my answer helped you
Lets assume the following when-statement:
when(a)
{
x -> doNothing()
y -> doSomething()
else -> doSomethingElse()
}
Now i'm looking to eliminate the boilerplate-function "doNothing()", e.g.:
x -> //doesn't compile
x -> null //Android Studio warning: Expression is unused
x -> {} //does work, but my corporate codestyle places each '{‘ in a new line, looking terrible
//also, what is this actually doing?
Any better ideas?
I can't just eliminate x -> completely, as that would lead to else -> doSthElse()
Edit: directly after writing this Question, i figured out a possible answer x -> Unit. Any shortcomings with that?
Kotlin has two existing possibilities to express a "do nothing" construct in when statements. Either Unit or an empty pair of braces. An empty block will just execute nothing.
There's nothing else planned in that regard (see here).
To answer your question regarding "also, what is this actually doing?" for the empty block, looking at the bytecode and translating it into Java helps:
val x = 33
when(x)
{
1 -> {}
2 -> Int
3 -> Unit
else -> Double
}
Translates to
int x = 33;
switch(x) {
case 1:
case 3:
break;
case 2:
IntCompanionObject var10000 = IntCompanionObject.INSTANCE;
break;
default:
DoubleCompanionObject var1 = DoubleCompanionObject.INSTANCE;
}
Is it possible to make the following code to compile in Kotlin?
val variable: String? = "string"
val (a, b) = variable?.run {
1 to 2
}
The compiler does not allow destructuring because the expression on the right-hand side is typed as a nullable Pair<Int, Int>?, and it's unclear what values a and b should get in case variable is null.
To solve this, you need to get a not-null expression after =.
There's a lot of different ways to deal with nullable values and produce a not-null value from a nullable one, see: In Kotlin, what is the idiomatic way to deal with nullable values, referencing or converting them
For example, if you want to provide fallback values for a and b, then use the ?: operator as follows:
val (a, b) = variable?.run {
1 to 2
} ?: (0 to 0)
An alternative, for example, would be to check variable for null first:
val (a, b) = checkNotNull(variable) { "variable should never be null" }.run {
1 to 2
}
Null doesn't have any destructuring declarations. If you want a value of null to destructure like it's a pair of nulls, you could add these extensions:
operator fun <T> Pair<T, *>?.component1() = this?.component1()
operator fun <T> Pair<*, T>?.component2() = this?.component2()
Otherwise, as the other answer shows, you need to provide a default using the Elvis operator.
It's not automatic because it doesn't know what you want. Depending on what you're doing with it, 0 to 0 may be most appropriate, or maybe -1 to -1 or 0 to null or null to null.
The question is, what do you want to do if your variable is null? If you want to throw an exception, go with require or check as #hotkey suggested.
However I have the case where I just want to return if the value is null. So I wrote myself a little helper function that allows for this:
private inline fun <T> T?.exitIfNull(exitBlock: () -> Nothing): T {
if (this == null)
exitBlock()
else
return this
}
You can call this function as follows:
val (a, b) = variable?.run {
1 to 2
}.exitIfNull {
return
}
A nice little use of the Nothing keyword in Kotlin that I personally find quite fascinating
Is there a design pattern or methodology or language that allows you to write complex conditional logic beyond just nested Ifs?
At the very least, does this kind of question or problem have a name? I was unable to find anything here or through Google that described what I was trying to solve that wasn't just, replace your IF with a Switch statement.
I'm playing around with a script to generate a bunch of data. As part of this, I'd like to add in a lot of branching conditional logic that should provide variety as well as block off certain combinations.
Something like, If User is part of group A, then they can't be part of group B, and if they have Attribute C, then that limits them to characteristic 5 or 6, but nothing below or above that.
The answer is simple: refactoring.
Let's take an example (pseudo-code):
if (a) {
if (b) {
if (c) {
// do something
}
}
}
can be replaced by:
if (a && b && c) {
// do something
}
Now, say that a, b and c are complex predicates which makes the code hard to read, for example:
if (visitorIsInActiveTestCell(visitor) &&
!specialOptOutConditionsApply(request, visitor) &&
whatEverWeWantToCheckHere(bla, blabla)) {
// do something
}
we can refactor it as well and create a new method:
def shouldDoSomething(request, visitor, bla, blabla) {
return visitorIsInActiveTestCell(visitor) &&
!specialOptOutConditionsApply(request, visitor) &&
whatEverWeWantToCheckHere(bla, blabla)
}
and now our if condition isn't nested and becomes easier to read and understand:
if (shouldDoSomething(request, visitor, bla, blabla)) {
// do something
}
Sometimes it's not straightforward to extract such logic and refactor, and it may require taking some time to think about it, but I haven't yet ran into an example in which it was impossible.
All of the foregoing answers seem to miss the question.
One of the patterns that frequently occurs in hardware-interface looks like this:
if (something) {
step1;
if ( the result of step1) {
step2;
if (the result of step2) {
step3;
... and so on
}}}...
This structure cannot be collapsed into a logical conjunction, as each step is dependent on the result of the previous one, and may itself have internal conditions.
In assembly code, it would be a simple matter of test and branch to a common target; i.e., the dreaded "go to". In C, you end up with a pile of indented code that after about 8 levels is very difficult to read.
About the best that I've been able to come up with is:
while( true) {
if ( !something)
break;
step1
if ( ! result of step1)
break;
step2
if ( ! result of step2)
break;
step3
...
break;
}
Does anyone have a better solution?
It is possible you want to replace your conditional logic with polymorphism, assuming you are using an object-oriented language.
That is, instead of:
class Bird:
#...
def getSpeed(self):
if self.type == EUROPEAN:
return self.getBaseSpeed();
elif self.type == AFRICAN:
return self.getBaseSpeed() - self.getLoadFactor() * self.numberOfCoconuts;
elif self.type == NORWEGIAN_BLUE:
return 0 if isNailed else self.getBaseSpeed(self.voltage)
else:
raise Exception("Should be unreachable")
You can say:
class Bird:
#...
def getSpeed(self):
pass
class European(Bird):
def getSpeed(self):
return self.getBaseSpeed()
class African(Bird):
def getSpeed(self):
return self.getBaseSpeed() - self.getLoadFactor() * self.numberOfCoconuts
class NorwegianBlue(Bird):
def getSpeed():
return 0 if self.isNailed else self.getBaseSpeed(self.voltage)
# Somewhere in client code
speed = bird.getSpeed()
Taken from here.