From Swift documentation:
Typically, you use the if statement to evaluate simple conditions with only a few possible outcomes. The switch statement is better suited to more complex conditions with multiple possible permutations and is useful in situations where pattern matching can help select an appropriate code branch to execute.
I'm trying to decide on if I should use the switch or if/else statements based on if I have a complex condition. So my question is, is my condition complex or not.
Here is an example of what I have:
var sampleVar = Measurement(value: amount, unit: UnitLength.megameters)
if(type == "inches"){
//do something
}
else if...
I have between 5 to 15 possible conditions I'm checking for so would that make it complex enough to justify using a switch statement? Or is complexity based on the condition and not how many conditions there are?
The ultimate test is to just write out both and compare.
When switch is better
When you're dealing with a situation that favours a switch over an if/else ladder, you code will look like:
if something == 1 {
foo()
} else if something == 2 {
bar()
} else if something == 3 {
baz()
} else {
quux()
}
As you can see, all the bracketing, repeated keywords (else, if) repeated operators == and repeated instance of the same identifier (something) add a bunch of noise with really little value. Compare to the switch:
switch something {
case 1: foo()
case 2: bar()
case 3: baz()
default: quux()
}
When if/else if/else is better
You'll find yourself writing a switch where the switched variable isn't really being matched much, but instead you have a bunch of where clauses that check a lot of unrelated conditions. Compare:
switch something {
case 1: foo()
case _ where case2_1() || case2_2(): bar()
case _ where case3(): baz()
case _ where case4(): quux()
}
vs.
if something == 1 || case1() { foo() }
else if case2_1() || case2_2() { bar() }
else if case3() { baz() }
else if case4() { quux() }
Don't forget about polymorphism!
Whenever possible, try to break up complex switching logic into dynamic calls to methods on an object. This allows you to separate the logic of each case into a separate class, where all related logic can be grouped.
Related
I looked at a few questions regarding this for other languages and some suggest using final but that doesn't seem to work with Dart.
I'm passing in arguments so surely the switch statement cannot contain constants only? A switch statement, much like an if statement is asking if it is or not..ie it's unknown so I don't see how a switch statement can be useful if they have to be constants...?
setCategory(arga, argb) {
int result;
switch (true) {
case (arga >= 0 && arga < 3 && argb < 35):
result = 16;
break;
case (arga >= 0 && arga < 3 && argb >= 35):
result = 15;
break;
etc
It's returning the error Case expressions must be constant regarding the values arga and argb in the case expressions. What's the best way to remedy this or do I have to use an if statement?
The switch case expressions must be constants for sure.
You have to use if/then chains to do multiple tests based on non-constant values.
You cannot use arguments from the surrounding function in a switch case. What you are trying to do here is not supported by the Dart switch statement.
The Dart switch statement is deliberately kept very simple, so that a compiler can know all the possible cases at compile-time. That's why they must be compile-time constants.
Switch statements are still useful for some kinds of switching, like on enums:
enum Nonse { foo, bar, baz; }
String fooText(Nonse non) {
switch (non) {
case Nonse.foo: return "foo";
case Nonse.bar: return "bar";
case Nonse.baz: return "baz";
}
throw ArgumentError.notNull("non");
}
You can also switch over constant string values or integer values.
There are some rules for Switch Case
The default case is optional.
All case expression must be unique.
The case statements can include only constants. It cannot be a variable or an expression.
The data type of the variable and the case expression must match.
There can be any number of case statements within a switch.
you should use 'If Else' statement
Switch statement requires a constant -> it means already initialized variable with final value
switch (expression) {
case ONE : {
statement(s);
}
break;
case TWO: {
statement(s);
}
break;
default : {
statement(s);
}
}
for(int x=0; x<1; x++){
if(ing5<8 && toWhatDegreeRand<=10 && toWhatDegreeRand>7){
toWhatDegreeRand=9;
}
if(ing4<8 && toWhatDegreeRand<=7 && toWhatDegreeRand>5){
toWhatDegreeRand=6;
}
if(ing3<8 && toWhatDegreeRand<=5 && toWhatDegreeRand>3){
toWhatDegreeRand=4;
}//....
}
It can be a little useful.
I'm new to coding and am about to finish the "Intro to App Development with Swift" iBook. I am currently in lesson 19, Enumerations and Switch and, at page 8 of the associated playground it shows the following code:
enum LunchChoice {
case pasta, burger, soup
}
func cookLunch(_ choice: LunchChoice) -> String {
if choice == .pasta {
return "π"
} else if choice == .burger {
return "π"
} else if choice == .soup {
return "π²"
}
return "Erm... how did we get here?"
}
cookLunch(.soup)
Per se, this is not a problem for me to understand but, once I call cookLunch(.soup), the last return statement doesn't show up.
The exercise just below ask me:
try to change the value passed in to cookLunch so that the final else statement is called
And that is where I got stuck because it seems impossible to pass something different to the cookLunch function other than the choices present in the enumeration.
Could you help me understand the sense behind all this and maybe provide me a solution?
You have two options:
Comment out the third comparison
// } else if choice == .soup {
// return "π²"
Add a fourth case which is not covered by the comparisons
enum LunchChoice {
case pasta, burger, soup, steak
}
and pass it:
cookLunch(.steak)
However nobody would seriously write such an if - else chain, in Swift a switch expression is appropriate
func cookLunch(_ choice: LunchChoice) -> String {
switch choice {
case .pasta: return "π"
case .burger: return "π"
case .soup: return "π²"
default: return "Erm... how did we get here?"
}
}
Your instructions say "so that the final else statement is called". That would be the soup return, not the "how did we get here" return. As you say, with 3 lunch choices and 3 if/else statements, one of them will always be invoked. You have to add a 4th lunch choice that doesn't have a corresponding if or else if in order for the "how did we get here" code to execute.
UPDATE This is fixed in Swift 3.1
In migrating an if-else to a switch statement, I noticed that type inference wasn't working. Why do I need to specify HKQuantityTypeIdentifier in each case when quantityTypeIdentifier is already of that type?
func process(samples: [HKSample]?, quantityTypeIdentifier: HKQuantityTypeIdentifier) {
DispatchQueue.main.async { [weak self] in
if let quantitySamples = samples as? [HKQuantitySample] {
for sample in quantitySamples {
switch quantityTypeIdentifier {
case HKQuantityTypeIdentifier.distanceWalkingRunning:
// code
case HKQuantityTypeIdentifier.activeEnergyBurned:
// code
case HKQuantityTypeIdentifier.heartRate:
// code
default:
fatalError("Quantity Type Identifier not implemented \(quantityTypeIdentifier)")
}
}
}
}
}
I am able to call the function like:
process(samples: samples, quantityTypeIdentifier: .distanceWalkingRunning)
I think you've found a bug, or at least you have a reasonable case to claim one. The inconsistency is nicely shown by a much shorter example:
let c : UIColor = .red
switch c {
case .red : print ("red") // error
default : break
}
That won't compile. You can say .red on the first line but not on the third line. That seems a clear inconsistency.
Now, having said that, I can certainly explain why the rules are different in the two different places. A case expression is resolved according to the ~= operator and the rules of forming a pattern. Those rules are different from anything else in Swift (hence, for example, there are situations where you say as in a case pattern but would say as? everywhere else). So evidently those are the rules that would need tweaking in order for this to work. They have been tweaked so far as to allow bare enum cases but not bare enum-like struct "cases" (that is, static members of structs that are RawRepresentable where those static members evaluate to an instance of the struct itself).
Finally, here's a skanky workaround that I like to use when case patterns become too onerous:
let c : UIColor = .red
switch true {
case c == .red : print ("red") // heh heh
default : break
}
By switching on true and writing out the entire boolean condition we break the bounds of pattern-matching and reenter the world of normal expressions.
Is there a way in Swift to assign conditional expressions similar to this
let foo = if (bar == 2) {
100
} else {
120
}
(or with a switch case).
(Don't want to have to use ternary operator for this).
This kind of assignement is good for functional style / immutability. The expressions have a return value in this case.
Note: it's a general question, this is just simplified example, imagine e.g. a switch case with a lot of values, pattern matching, etc. You can't do that with ternary operator.
Btw also note that there are languages that don't support ternary operator because if else returns a value, so it's not necessary, see e.g. Scala.
You can use a closure to initialize an immutable:
let foo: Int = {
if bar == 2 {
return 100
} else {
return 120
}
}()
The advantage of using a closure is that it's a function, so you can use any complex logic inside, implemented in a clean way and not through nested ternary operators. It can be a switch statement, it can be obtained as the return value of a function followed by some calculations, it can be a pattern matching case, it can be a combination of them all, etc.
Said in other words, it's the same as initializing with the return value of a function, but the difference is that the function is inline and not somewhere else, with readability benefits.
Just for completeness, if the variable is mutable, you can use deferred initialization:
var foo: Int
// Any statement here
if bar == 2 {
foo = 100
} else {
foo = 120
}
// Other statements here
myFunc(foo)
so you can declare a mutable variable, and initialize it anywhere in the same scope, but before using it the variable must be initialized.
Update: Since Swift 2.0, deferred initialization also works with immutables.
Swift requires exhaustive switch statements, and that each case have executable code.
'case' label in a 'switch' should have at least one executable statement
Has anybody settled on a good way to handle the cases where you don't want to actually do anything? I can put a println() in there, but that feels dirty.
According to the book, you need to use break there:
The scope of each case canβt be empty. As a result, you must include at least one statement following the colon (:) of each case label. Use a single break statement if you donβt intend to execute any code in the body of a matched case.
You can use a break statement:
let vegetable = "red pepper"
var vegetableComment: String = "Nothing"
switch vegetable {
case "cucumber", "watercress":
break // does nothing
case let x where x.hasSuffix("pepper"):
vegetableComment = "Is it a spicy \(x)?"
default:
vegetableComment = "Everything tastes good in soup."
}
Example modified from the docs
Below is one option for null statement, but maybe not a good solution. I cannot find a statement like python pass
{}()
for switch case, break is better choice.
break
Do nothing in exhaustive switch case statements:
Swift:
switch yourVariable {
case .someCase:
break
}
SwiftUI:
switch yourVariable {
case .someCase:
EmptyView() // break does not work with ViewBuilder
}
Using EmptyView() instead of break in SwiftUI views prevents the error:
Closure containing control flow statement cannot be used with function
builder ViewBuilder.
EmptyView() is a SwiftUI standard view (tested with Xcode 12, iOS 14) and does not need to be defined yourself.
In addition to break mentioned in other answers, I have also seen () used as a no-op statement:
switch 0 == 1 {
case true:
break
case false:
()
}
Use () if you find break confusing or want to save 3 characters.
The cleanest solution I've found is to simply include your last statement in the switch case as your default. This avoids the need to add break or other unnecessary statements while still covering all possible cases.
For example:
switch myVar {
case 0:
myOtherVar = "Red"
case 1:
myOtherVar = "Blue"
default:
myOtherVar = "Green"
}
You can check specific case, no need to be exhustive with switch cases
Say you have a enum like this,
enum Selection {
case one
case two
case three
}
var myCase = Selection.one
you can check like this,
if case .one = myCase {
print("one")
}
A clean solution I use for my default case is:
default: ()