switch statement optimization (Swift) - optimization

In swift switch statements, does the condition get called once or for each case? Which would be faster?
switch foo(param) {
case 0:
NSLog(0)
default:
NSLog("default")
}
or
let myNumber = foo(param)
switch myNumber {
case 0:
NSLog(0)
default:
NSLog("default")
}

switch evaluates its argument only once.
As Martin suggested in the comments, this example proves it
func foo(name: String) -> Int {
println(name)
return countElements(name)
}
switch foo("hello") {
case 0: println(0)
case 1: println(1)
default: println("default")
}
output
hello
default
In case it were evaluating for each case, you would expect
hello
hello
default
instead

Related

Run "should_panic" tests with different inputs

Assume the following function which gets the nth bit from a u8:
fn get_bit(&self, n: u8) -> bool {
if n > 7 {
panic!("Overflow detected while using `get_bit`: Tried to get the {n}th bit.");
}
let result = *self >> n & 1;
if result == 1 {
return true;
} else {
return false;
}
}
The basic (panic-case) test looks like this:
#[test]
#[should_panic]
fn get_bit_panic_ut() {
0b0000_0000.get_bit(8);
}
As you can see this only tests for 8 as input. I want to make sure that this function will panic for any input between 8 and 255. So my naive attempt was:
#[test]
#[should_panic]
fn get_bit_panic_ut() {
for i in 8..=255 {
println!("{i}"); // For demonstration purposes
0b0000_0000.get_bit(i);
}
}
If I run the above with the --nocapture flag, I can see that the test execution stops after 8. So my question is how do I test for all the other cases here?
One option I came across, which might work is the rtest crate. I wonder if rust provides any out of the box mechanics for scenarios like this.
No - but you can easily simulate it:
#[test]
fn get_bit_panic_ut() {
for i in 8..=255 {
println!("{i}"); // For demonstration purposes
assert!(std::panic::catch_unwind(|| 0b0000_0000.get_bit(i)).is_err());
}
}

Null check with ?. vs ?: fails in kotlin

I have following statement in my code:
safeOrderResult.accomplished?.let{ safeAccomplished->
//Do something with safeAccomplished when accomplished <> null
Log.i(TAG,"bind: safeOrderResult.accomplishedId.let?{}")
}?:let{
//Do something when accomplished == null
Log.i(TAG,"bind: safeOrderResult.accomplishedId?:let{} *null*" )
}
Here my code does something strange:
On a Samsung TAB A (i think not significant) it works as expected.
On a Samsung S9 it calls both let sections.
Snippet from Logcat Samsung S9 (android 10)
2021-05-06 14:11:35.427 9069-9069/no.norva24.mslam I/ViewHolder: bind: safeOrderResult.accomplishedId = 408
2021-05-06 14:11:35.427 9069-9069/no.norva24.mslam I/ViewHolder: bind: safeOrderResult.accomplishedId.let?.{}
2021-05-06 14:11:35.427 9069-9069/no.norva24.mslam I/ViewHolder: bind: handleDate = null <- inside above let: ok
2021-05-06 14:11:35.427 9069-9069/no.norva24.mslam I/ViewHolder: bind: safeOrderResult.accomplishedId?:let{} *null*
2021-05-06 14:11:35.427 9069-9069/no.norva24.mslam I/ViewHolder: bind: flagged = false or null
TabA: android 10
2021-05-06 14:21:16.676 2468-2468/no.norva24.mslam I/ViewHolder: bind: safeOrderResult.accomplishedId = 427
2021-05-06 14:21:16.676 2468-2468/no.norva24.mslam I/ViewHolder: bind: safeOrderResult.accomplishedId.let?.{}
2021-05-06 14:21:16.678 2468-2468/no.norva24.mslam I/ViewHolder: bind: handleDate = null <-- inside above let
2021-05-06 14:21:16.685 2468-2468/no.norva24.mslam I/ViewHolder: bind: flagged = false or null
The key point is, how can a value both be null and contain a value?, or can kotlin "change" to null and kick in in the second "null" let, if value has changed in the first first let (which I didn't do)
I am Using kotlin 1.5.0
EDIT 2021.05.06 18:55 GMT+2
I am not sure, but I might have learned something here today: ;)
safeOrderResult.accomplished?.let{ safeAccomplished->
//Do something with safeAccomplished when accomplished <> null
/*Here I have preserved my value in safeAccomplished
And actually returning a value below (a Unit()) from Log.i ?*/
Log.i(TAG,"bind: safeOrderResult.accomplishedId.let?{}")
}?:let{
//Do something when accomplished == null
/* But why did the code kick in here ?
After it was inside the let above ? I thought the '?:let' was
continuing if the '?.let' didn't kick in.
*/
Log.i(TAG,"bind: safeOrderResult.accomplishedId?:let{} *null*" )
}
/*
Below is the actual code which had the trouble (the code isn't finished therefore the "preserved" `let` values isn't used)
*/
safeOrderResult.accomplishedId?.let {
listItemOrderListLinearLayoutCompatStatus.apply {
visibility = View.VISIBLE
listItemOrderListMaterialTextViewOrderStatus.text =
context.resources.getStringArray(
R.array.basic_register_accomplish_status_names)[1]
listItemOrderListMaterialTextViewDate.text =
dateStringSplitSpace(safeOrderResult.registeredDate)
Log.i(TAG, "bind: handleDate = ${safeOrderResult.handleDate}")
listItemOrderListMaterialTextViewReason.text =
if(safeOrderResult.handleDate.isNullOrEmpty())
"Still possible to update"
else
"Assignment locked on ${dateStringSplitSpace(safeOrderResult.handleDate)}"
setBackgroundColor(
ContextCompat.getColor(
itemView.context,
if(safeOrderResult.handleDate.isNullOrEmpty())
R.color.list_item_register_field_accomplished_background
else
R.color.list_item_register_field_accomplished_locked_background
)
)
}
listItemOrderListLinearLayoutCompatStatusMore?.apply {
setBackgroundColor(
ContextCompat.getColor(
itemView.context,
if(safeOrderResult.handleDate.isNullOrEmpty())
R.color.list_item_register_field_accomplished_background
else
R.color.list_item_register_field_accomplished_locked_background
)
)
}
}?:let {
safeOrderResult.passedId?.let { safePassedId->
listItemOrderListLinearLayoutCompatStatus.apply {
visibility = View.VISIBLE
listItemOrderListMaterialTextViewOrderStatus.text =
context.resources.getStringArray(
R.array.basic_register_accomplish_status_names
)[2]
listItemOrderListMaterialTextViewDate.text =
dateStringSplitSpace(safeOrderResult.registeredDate)
listItemOrderListMaterialTextViewReason.text =
safeOrderResult.passedReason
setBackgroundColor(
ContextCompat.getColor(
itemView.context,
R.color.list_item_register_field_passed_background,
)
)
}
}?:let {
listItemOrderListLinearLayoutCompatStatus.apply {
visibility = View.GONE
}
}
}
** ADDENDUM 2020.05.06 19:30 GMT+2 **
In playground I got trouble with this:
/**
* You can edit, run, and share this code.
* play.kotlinlang.org
*/
class A {
fun f() {
let { println(it) }
}
}
data class DataClass(
var value1:String?=null,
var value2:String?=null
)
fun main() {
A().f()
var myData = DataClass()
myData.value1 = "1"
myData.value1?.let{ safeValue1->
println("value1 = "+safeValue1)
}?:let{
println("value1==null !!")
}
myData.value2?.let{ safeValue2->
println("value2 = "+safeValue2)
}?:let{
println("value2==null !!")
}
}
Where it kicked on the ?:let's above. This was ok in kotin v.1.5.0 at least...
ADDENDUM 2: 2020.05.06 19:40 GMT+2
So... dataClass.value?:let{ } isn't allowed ? in a 'standard' kotlin scenario to check for null existence ?, but still 'valid' in AS2020.3.1.15 w/kotlin 1.5.0 ...
ADDENDUM 3: 2020.05.06 19:55 GMT+2
When using another approach (omitting let keyword in ?:let{ I got this answer to the based on the playground code above:
Here I expected also the value2 to show up with value2==null !! but it didn`t...
Here's the playground code now:
/**
* You can edit, run, and share this code.
* play.kotlinlang.org
*/
class A {
fun f() {
let { println(it) }
}
}
data class DataClass(
var value1:String?=null,
var value2:String?=null
)
fun main() {
A().f()
var myData = DataClass()
myData.value1 = "1"
/*
myData.value1?.let{ safeValue1->
println("value1 = "+safeValue1)
}?:let{
println("value1==null !!")
}
myData.value2?.let{ safeValue2->
println("value2 = "+safeValue2)
}?:let{
println("value2==null !!")
}
*/
myData.value1?.let{ safeValue1->
println("value1 = "+safeValue1)
}
myData.value1?:{
println("value1==null !!")
}
myData.value2?.let{ safeValue2->
println("value2 = "+safeValue2)
}
myData.value2?:{
println("value2==null !!")
}
}
...still a little confused ...
The let function can indeed change your target to null. It changes the target to whatever it returns. A lambda implicitly returns the result of its last expression. Your code above has a Log.i() call as its last expression, so it returns Unit, so the second let function should never run if the first one does. Is it possible you've snipped off some code at the end of your first let lambda that could possibly return a null value?
A quick fix for the above problem would be to swap let for also, because also always returns its receiver.
I think most experienced Kotlin users will advise you not to chain scope function calls like this because it makes the code hard to follow and it is easy to introduce subtle bugs. You can write a more robust version like this:
val accomplished = safeOrderResult.accomplished
if (accomplished != null) {
//Do something with smart-cast non-nullable accomplished
} else {
//Do something when accomplished == null
}
At a guess, the first one is returning null at the end, which means the value produced by that whole expression is null, so the stuff after the ?: is triggered (since that's an "if the left side evaluates to null" condition).
Why that would only happen on some Samsung models - who knows, they have a history of messing with things in the Android library! I'd check exactly what's going on in the block and what it might evaluate to. You might need to return Unit at the end, or use a function like apply that returns the receiver instead of the result of the lambda.
This is why the if/else is a better fit - you have a condition at the start, and you decide whether to do one thing or another, exclusively. let produces a value, and it's often used to propagate a value down a chain, and return a result. ?: is a final default value, for if that result turns out to be null.
It's absolutely possible to run the let block and the code after the ?:, and sometimes that a thing you want to do. As a construction it's often used for returning a default value. So if/else is a little more explicit about what you're doing and how it's meant to work, and it helps avoid surprise bugs like this one!
If you don't want to bind accomplished to a variable as in #Tenfour04's answer, I'd write it as
safeOrderResult.accomplished.let {
if (it != null) {
// safeOrderResult.accomplished was not null, use it
} else {
// safeOrderResult.accomplished was null
}
}
or
safeOrderResult.accomplished.let { accomplished ->
if (accomplished != null) {
// safeOrderResult.accomplished was not null, use accomplished
} else {
// safeOrderResult.accomplished was null
}
}
Note .let and not ?.let. But pick on readability/convenience. I definitely wouldn't use
value?.let{ safeValue-> /*dowork*/}; value?:let{/*do null work*/}
as you suggest in another comment.
You can do an if-null-else with ?.let but it's not very readable in my opinion
var s: String? = "Str"
s?.let { println("A ok") } ?: run { println("A null") }
s = null
s?.let { println("B ok") } ?: run { println("B null") }
A ok
B null
It is also possible to introduce subtle bugs like this:
var s: String? = "Str"
s?.let { println("A ok"); null } ?: run { println("A null") }
A ok
A null
This is why you should use an if-else if you both need the non-null and null case. (?. is intended for the case where only the non-null case makes sense):
if (s == null) println("A null") else println("A ok")
if (s == null) {
println("A null")
} else {
println("A ok")
}
Thanx for many good answers above, and you all are right...
I landed on following solution for my problem, but still not quite happy though:
I use .apply to remove some value. overhead,
safeOrderResult.apply{
if(accomplished!=null){
//Do something with accomplished since accomplished <> null
Log.i(TAG,"bind: accomplished != null")
}else{
//Do something when accomplished == null
Log.i(TAG,"bind: accomplished == null" )
}
}
I mark accepted for #Alexey Romanov suggestion which is quite reasonable.

where must be break in switch objective-c

What is the difference between this solution:
switch (value)
{
case 1:
{
// some code
} break;
}
When break stay after brackets
And this:
switch (value)
{
case 1:
{
// some code
break;
}
}
When break stay in brackets
A break makes the switch statement to end its execution. If you don't add a break to your case, the next case will start executing. Consider:
NSInteger myInt = 0;
switch (myInt) {
case 0:
NSLog("0");
case 1:
NSLog("1");
default:
NSLog("Default");
}
will print:
0
1
Default
because there are no breaks.
It doesn't matter if you wrap break into a block { break; } because the block changes the scope of variables declared inside of it but has no effect on the break itself.
There is no difference between { ... } break; and { ... break; } because in both versions break is the last statement that is executed and that's all that matters.
However,
case 0:
if (myCondition) {
break;
}
case 1:
...
would have a very different meaning. If myCondition is NO, then the next case (case 1:) is executed too (fall-through) because the break statement has not been executed.

How to accept multiple enum types for one method parameter

I saw several questions on this problem, but none about iOS.
I have two enums like following :
// My first enum eColor
typedef enum
{
eColorRed = 1,
eColorGreen,
eColorBlue
} eColor;
// My second enum eShape
typedef enum
{
eShapeCircle = 1,
eShapeSquare,
eRectangle
} eShape;
I would like to have a method which accept both of these enums like this :
+ (NSString*) toStr:(bothEnum)e
{
NSString *result = nil;
if (bothEnum == eColor)
{
switch(e) {
case eColorRed:
result = #"red";
break;
case eColorGreen:
result = #"green";
break;
case eColorBlue:
result = #"blue";
break;
default:
result = #"unknown";
}
}
else if (bothEnum == eShape)
{
switch (e) {
case eShapeCircle:
result = #"circle";
break;
case eShapeSquare:
result = #"square";
break;
case eShapeRectangle:
result = #"rectangle";
break;
default:
result = #"unknown";
}
}
return result;
}
Is a thing like this possible ?
I don't want to have methods like colorToStr:, shapeToStr:, etc. My wish is to have only one method called toStr:, as above...
Enums are just constants and at run time they are just numbers. So your method doesn't know what is eColorRed, it knows it as 1, and your only option is to pass additional parameter, telling your method wether 1 passed in first argument is eColorRed or eShapeCircle. It can be just a string, like:
+ (NSString*) toStr:(NSUInteger)e fromEnumType:(NSString*)type
{
if([type isEqualToString:#"eColor"])
{
switch(e)
...
}
else if([type isEqualToString:#"eShape"])
{
switch(e)
...
}
}
You may try this approach: You make the second enum to start indexing from the last index of the first enum, then you just use single witch in your method. Remember enum type is just an int type indeed.
// My first enum
typedef enum
{
eColorRed = 1,
eColorGreen,
eColorBlue,
eColorLastIndex
} eColor;
// My second enum
typedef enum
{
eShapeCircle = eColorLastIndex,
eShapeSquare,
eShapeRectangle,
} eShape;
typedef int eTypes;
+(NSString*)toStr:(eTypes)e
{
NSString *result = nil;
switch(e) {
case eColorRed:
result = #"red";
break;
case eColorGreen:
result = #"green";
break;
case eColorBlue:
result = #"blue";
break;
case eShapeCircle:
result = #"circle";
break;
case eShapeSquare:
result = #"square";
break;
case eShapeRectangle:
result = #"rectangle";
break;
default:
result = #"unknown";
break;
}
return result;
}
Instead eGraphics you may use just int or eColorShape, whatever.
Update for 64-bit Change:
According to apple docs about 64-bit changes,
Enumerations Are Also Typed : In the LLVM compiler, enumerated types can
define the size of the enumeration. This means that some enumerated
types may also have a size that is larger than you expect. The
solution, as in all the other cases, is to make no assumptions about a
data type’s size. Instead, assign any enumerated values to a variable
with the proper data type
So you have to create enumeration with type as below syntax if you support for 64-bit.
typedef enum eColor : NSUInteger {
eColorRed = 1,
eColorGreen,
eColorBlue
} eColor;
then go with #user2260054's answer, otherwise, it will lead with below warning in 64-bit environment.
Otherwise, it will lead to warning as Implicit conversion loses integer precision: 'NSUInteger' (aka 'unsigned long') to eColor
This is not good for design point of view. Cohesion is broken over here. While programming OOP concept must be implemented for reuse of code and decoupling the objects as well.

Objective-C: How to return to beginning of application after switch statement is complete

For example,
In your program you have:
NSLog(#"Where are you going?");
NSLog(#" 1 = Location1, 2 = Location2");
printf("Make a selection:");
scanf("%i, &value);
switch (value) {
case 1:
NSLog(#"You are going to Location 1.")
break;
case 2:
NSLog(#"You are going to Location 2.");
break;
default:
NSLog(#"That is not a valid location");
break;
}
Normally after you input your integer your program will return 0 and the application ends. How do you go about having it "loop" back to the original printf to make a new selection. Or even better, a new printf IE 'printf("Where else would you like to go?:");'?
Why don't you keep it as a separate method and call it from itself when you want to loop. Just consider the following code,
void takeMeToPlaces() {
NSLog(#"Where are you going?");
NSLog(#"0 = Exit, 1 = Location1, 2 = Location2");
printf("Make a selection:");
scanf("%i, &value);
switch (value) {
case 0:
NSLog(#"You don't like to go anywhere");
break;
case 1:
NSLog(#"You are going to Location 1.");
takeMeToPlaces();
break;
case 2:
NSLog(#"You are going to Location 2.");
takeMeToPlaces();
break;
default:
NSLog(#"That is not a valid location");
takeMeToPlaces();
break;
}
}