Tuples and Switch Statements in Swift Programming Language
Powerful Pattern Matching Constructs
In this Swift basics tutorial, we will discuss Tuples an advanced data type in Swift and how can we use Tuples in Switch statement, which are similar to the classic switch construct with some interesting differences making Swift's Switch both swift and powerful. A tuple in Swift is an advanced type - a list of zero or more values separated with commas. These values need not be of same type. We’ve already used tuples in Swift in a previous tutorial while returning multiple values from a function. In fact, even if you return no value from a function, you’re making use of a tuple - containing no values - specified with empty tuple ()
We wrote a function in the tutorial Functions in Swift with Multiple Return Types and External Parameters named largestAndSmallest which returned a tuple of two integer values. We can accept this returned tuple in two separate variables enclosed with parenthesis and use them separately in our code.
var (l,s) = largestAndSmallest([32,45,77,3])
println("Sum of Largest and Smallest in [32,45,77,3] is \(l+s)")
//Prints: Sum of Largest and Smallest in [32,45,77,3] is 80
Ignoring some of Tuple’s Elements
If we’d required just the largest number, we can ignore the second element of the tuple using an underscore:
var (largest, _) = largestAndSmallest([2,3,5])
println(largest) // prints: 5
Now, we have just access to the first element returned by the function largestAndSmallest.
Tuples with Named Elements
Tuples can have named elements. If we name the return value elements of largestAndSmallest like this:
func LargestAndSmallest(inArray: Int) -> (largest: Int, smallest: Int)
We can accept the returned values in a variable and access the individual elements by name:
var ls = LargestAndSmallest([21,33,51,44])
println(ls.smallest) // prints 21
Let’s consider another example:
var point3d = (x:-1, y: 3, z: 1)
var point2d = (Point3d.x, Point3d.y)
// Gives an error: does not have a member named 'x'
println(point2d.0) // prints -1
Here, we’ve a three dimensional point represented by tuple named point3d. The co-ordinates are named and thus by accessing the x and y values of point3d, We can create a two dimensional point point2d. But since this tuple does not have named elements, we can’t access them using names. However, we can access them using numbers.
Note: This is not listed in the book “The Swift Programming Language”, so this behavior may or may not be available in future beta releases - although I don’t think that should be the case.
Consider the following code fragment:
var personInfo = (Name: "Jane McDonald", Address:(Street:"Park Street", Appartment:"561B"))
println("\(personInfo.Name) lives in Appartment number \(personInfo.Address.Appartment) at street \(personInfo.Address.Street)")
//prints: Jane McDonald lives in Appartment number 561B at Park Street
Notice how we are accessing the Street and Apartment which are elements of Address tuple nested in personInfo.
We can also make assignments to nested tuples:
personInfo = ("Mike Little", ("Times Square","432A"))
However, it’s not preferable to use tuples for more complex data structures, as they have temporary local scope. Classes, Structures etc. would be a rather apt choice for your intricate data structure design purposes.
Switch Statements in Swift
Switch Statements in Swift are similar to C/C++ based Switch statements, providing an alternative to a complicated nested if-else statement structure, but there are some important differences.
Firstly, Switch statements in Swift, by default, “break” after a matching case is found and executed. The “break” keyword is not required after each case. The following example illustrates that:
var alphabetNumber = 5
case 1, 5, 9, 15, 18: println("Vowel")
case 1...26: println("Consonant")
default: ("Not an alphabet!")
// Prints: Vowel
In a C/C++ switch statement, without breaks, we would have got all “Vowel”, “Consonant” and “Not an Alphabet” printed. But here, we got just the matched case executed, and the control jumped out of the Switch statement after that. Thus, cases in switch construct in Swift “break” by default. (Also note that we can use comma separated values and ranges for matching values in cases - which may cause a single value to match multiple cases, and that is allowed in Swift, unlike C/C++.)
However, if you do want the typical C/C++ default Switch behavior, you would have to use keyword “fallthrough” after each switch.
var shape = Shapes.Square
case .Square: println("The shape has all equal sides and,")
case .Rectangle: println("The shape has two sets of parallel sides,")
case .Quadrilateral: println("The number of sides the shape has is 4 ")
The above example will execute all the switch’s cases and thus print all the properties of the shape square, which is also a Rectangle, which is also a Quadrilateral.
(Once an attempt of matching is successful, no other match is attempted. So fallthrough simply executes the following case without performing any match. And if another fallthrough is found in this case, it executes the following case too.)
Unlike C/C++ Switch Statement, Swift’s switch has to be “exhaustive” - which means, it should either provide a case for each possible value of the type in consideration, or a default statement to cover all the leftover cases. Since all the cases which belong to the “Shapes” type are present in the Switch statement of above example, it is exhaustive, and thus a default statement is not required. (You could use a single break statement in the default case if there is nothing else you’d put there.)
Another important difference from C style switch is that the Switch statements in Swift can use non integer values including strings and Tuples. (The next section explains “Tuples in Switch Statements” in detail)
Here is a list of differences discussed above for quick reference.
- Cases in Swift switch statements break by default and fallthrough is needed for classic Switch behavior.
- A switch statement is Swift has to be exhaustive, handling all possible values in cases.
- Swift’s Switch statements can use not integral values like strings, enums and tuples too for matching.
- Cases in Switch statement of swift can use multiple values separated by commas, and open/closed ranges too.
- A single value can match multiple Switch statements, however, only the first match is considered.
Tuples in Switch Statements
Tuples can also be used in Switch statement, either by themselves (normal equality check), or along with underscores and where clauses to construct powerful pattern matching constructs.
Consider the following example.
case let (x,y) where x==y :
println("x is equal to y")
case let (x,y) where y==x*2:
println ("y is double of x")
case let (x,y) where y==x*3:
println("y is triple of x")
case let (x,y) where y%x==0:
println("y is multiple of x")
case let (_,y) where isPrime(number: y):
println ("y is prime number")
case (-9...9, -9...9):
println("Both x and y are single digit numbers")
case let (x,y):
println("We could deduce nothing Special about this Tuple")
In this example, we’ve used where clauses to check conditions and perform sophisticated matching. We can check for equality, execute expressions, and invoke functions. If a value is not required in a particular “case”, we ignore it with an underscore. We can use ranges for checking our values fall in particular set of numbers. And finally, a default case is not required here, as we’ve omitted the where clause in the last case, which essentially means that any value of x and y would be matched and handled in this case.
Now consider the following example. The tuple “name” contains two string values, which we’ve labeled with firstname and lastname.
var name = (firstname:"Anusha", lastname:"Jain")
println("Hi there, long time no see!")
case let (first, last) where first == last:
println("Hello \(first) \(last)! That's indeed an interesting name")
case let (_, _) where name.lastname == "":
println("Hello \(name.firstname), Why didn't you share you surname?")
case let(firstname, lastname) where firstname == "":
println("Hello \(lastname)! You didn't share your first name, wanna be formal, huh?")
case let (firstname, lastname):
println("Hello \(firstname) \(lastname), nice to meet you!")
println("Hello \(name.firstname) \(name.lastname), nice to meet you!") */
The first case performs a match with simple string literals. The second one though, equates the two strings, notice that it uses names different from the labels of tuple elements. These are temporary names, only accessible inside the body of the specific case.
We can also access the named tuple elements inside the switch cases using the dot operator with name. Thus, in the third case, we ignore both the values with underscores and access the elements by their labels while performing the match and printing the greeting. (While it won’t make much sense in most cases, but you can use other variables other than elements of the tuple being matching where clauses.)
Finally, we’ve commented out the default case, as with the absence of a where clause, the last case will handle any two pair of strings, making our switch exhaustive without a where clause.
We started with tuples in Swift. And then we moved on to the powerful Swtich statements in Swift, without which the discussion of tuples would be incomplete. That’s because of the powerful pattern matching constructs we can create by using Tuples in Switch cases. We also listed a few differences from the classic C/C++ style switch statements.
That’s all for this post, stay tuned.