Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How case works in if-case

Tags:

swift

An old C programmer could use some help with Swift.

I don't understanding something about the if-case syntax. E.g.:

if case 20...30 = age {
   print ("in range.")
}

The case 20...30 = age appears to be the conditional test for the if statement. So I was initially confused to see the assignment operator ('=') used instead of a comparison operator ('==').

Ok, I thought to myself, that probably means the case statement is actually a function call that returns a boolean value. The returned value will then satisfy the comparison test in the if statement.

As an experiment, I tried treating the the case statement like a regular conditional test and placed parentheses around it. Swift will happily accept if (x == 5) or if (true). But if (case 20...30 = age) generates an error. So the case statement doesn't seem to behave like function.

I'm just curious to understand what's happening here. Any insight would be greatly appreciated.

like image 257
oldCoder Avatar asked Jun 17 '16 18:06

oldCoder


People also ask

Can I use CASE statement in if statement?

The short answer is yes, you can nest an if inside of swtich / case statement (or vice versa).

How does CASE work in SAS?

If case-operand equals when-condition, then the WHEN clause is true. If the when-condition is true for the row that is being executed, then the result expression that follows THEN is executed. If when-condition is false, then PROC SQL evaluates the next when-condition until they are all evaluated.

How does a CASE statement execute?

The CASE statement selects a sequence of statements to execute. To select the sequence, the CASE statement uses a selector (an expression whose value is used to select one of several alternatives) or, in the searched CASE statement, multiple search conditions.


4 Answers

The operator is if case, so you can't put parentheses. The syntax and behavior are based on those of the case statement in a Swift switch statement (see my online book if you need details). In a case statement, 20...30 is an interval, used as a pattern, which operates by using contains against the interval. The equals sign is indeed truly confusing, but that was their first attempt at a syntax for expressing what the case statement should be comparing with (i.e. the tag that comes after the switch keyword in a switch statement).

So, if you understand this:

switch age {
case 20...30:
    // do stuff
default:break
}

... then you understand how it is morphed directly into this:

if case 20...30 = age {
   // do stuff
}
like image 72
matt Avatar answered Oct 18 '22 18:10

matt


@matt does a good job of explaining what that code does. I'm here to suggest a better alternative.

You can use the ~= operator to check ranges. It's a regular operator/function that just returns a Bool, with no special language magic.

if 20...30 ~= age {
   print ("in range.")
}
like image 30
Alexander Avatar answered Oct 18 '22 20:10

Alexander


Just another note about these Swift operators. One must remember that Swift has a very advanced and abstracted compiler, so keywords are just keywords, and their behavior depends on their usage. Unlike C (and other syntactically related languages from C++ to JavaScript), where if is simply used to test a Boolean value (or something that can be converted to one), in Swift, the concept of the if statement is much more broad. I generally think of it gating access to a scope by using a variety of techniques, including Boolean tests.

You can think of if, guard, while, and repeat-while as more general control flow statements, with much more advanced behavior. Certainly they can still test a Boolean value, but they can also test other conditions as well. In your scenario, the condition being tested is whether some variable matches a defined pattern (does age match the pattern 20...30).

You can also test whether a variable was successfully set to a non-nil value (if let). The result of the let operation doesn't ever return a Boolean, but since it occurs within the if statement, the Swift runtime knows that it's part of the control flow. Also note that this slightly changes the behavior of let, insofar as any code inside the if block now sees the new non-nil value and is assured it's not nil.

These behaviors can also be combined with commas, like this:

if !skipAgeTest,                 // `if` tests a Boolean
    let age = Double(ageString), // `if` tests optional assignment
    case 20...30 = age           // `if` tests pattern matching
    {
    // `age` variable exists and is not `nil` and is between `20` and `30`, inclusive
}

so the concept of if let or if case being separate operators is... not exactly thinking about it in the right way.

And like I said, this syntax is also valid in other control flows:

while !skipAgeTest,
    let age = Double(ageString),
    case 20...30 = age {
    // `age` is validated
    // Probably also change `ageString` while we're in here
}


guard !skipAgeTest,
    let age = Double(ageString),
    case 20...30 = age
    else {
    // `age` is invalid, and not available in this block
}
// `age` is valid
like image 35
Ky. Avatar answered Oct 18 '22 18:10

Ky.


Range Containment

Use the ~= operator, as @Alexander mentioned.

 if (1...10).contains(n) {
     print("pass")
 }

 switch n {
     case 1...10: print("pass")
     default: print("bug")
 }

 if case 1...10 = n {
     print("pass")
 }

 if 1...10 ~= n {
     print("pass")
 }

You can even do Ranges of Characters:

 if "a"..."z" ~= "a" {
     print("pass")
 }

Overloading ~=

I made a couple overloads, so that you can not only check for Range, but also Arrays and Sets

func ~=<T: Equatable> (pattern: [T], value: T) -> Bool { return pattern.contains(value) }
func ~=<T: Equatable> (pattern: Set<T>, value: T) -> Bool { return pattern.contains(value) }
func ~=<T: Equatable> (pattern: Set<T>, value: Set<T>) -> Bool { return pattern.union(value).count < pattern.count + value.count }

Which allows you to do this:

if [1, 2, 3] ~= 2 {
    print("pass")
}

if [1, 2, 3] ~= [3, 4, 5] {
    print("pass")
}

Tuples

However, when using tuples, you'll still have to use if case

 if case (1...10, 1...10) = (number1, number1) {
     print("0")
 }

You can even do the _ syntactic sugar

 if case (1...10, _) = (number1, number1) {
     print("0")
 }

Optionals

 if case .some = Optional(number) {
     print("pass")
 }

 if case .some(let x) = Optional(number) {
     print(x)
 }

 if case let x? = Optional(number) {
     print(x)
 }
like image 43
0-1 Avatar answered Oct 18 '22 18:10

0-1