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.
The short answer is yes, you can nest an if inside of swtich / case statement (or vice versa).
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.
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.
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
}
@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.")
}
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
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")
}
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")
}
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")
}
if case .some = Optional(number) {
print("pass")
}
if case .some(let x) = Optional(number) {
print(x)
}
if case let x? = Optional(number) {
print(x)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With