Pattern matching

Pattern matching is a very common programming mode in Swift. Using pattern matching can help us write concise, clear and easy-to-read code, making our code concise and powerful.

Pattern matching in conditional judgment

Conditional judgment is the most common process control we use. In Swift, only values of type Bool are accepted as conditional bodies. In addition to checking Bool values directly, we can use conditional statements for optional binding, which is very common in our development.

Matching enumeration value

In Swift, enumerations created are non-comparable by default (the Comparable protocol is not implemented), which means that we cannot directly use the == operator to determine whether two enumerations are equal, in which case pattern matching is used:

Create an enumeration type:

enum Result {
    case success
    case failure
}
Copy the code

Initialize an enumeration value:

let result = Result.success
Copy the code

Use pattern matching to determine the value of the created enumeration value:

if case .success = result {
    print("Value of result is success.")}Copy the code

Optional binding

Create an optional value:

let optionalInt: Int? = 1
Copy the code

Unpack using optional binding:

if let val = optionalInt {
    print("The value of optionalInt is (val)")}func handleGuard(a) {
    guard let val = optionalInt else {
        return
    }
    print("The value of optionalInt is (val)")
}
handleGuard()
Copy the code

Another mode for optional binding, and this is the most basic mode for optional binding:

if case .some(let val) = optionalInt {
    print("The value of optionalInt is (val)")}Copy the code

It can also be simplified as:

if case let val? = optionalInt {
    print("The value of optionalInt is (val)")}Copy the code

Pattern matching in loops

The problem is that the optional binding of iflet mode can only implement one optional value binding. What if we need to match the optional value in an array? So instead of using the if let form, we need to use the if case let form

Create an array of optional values:

let values: [Int? ] = [1.nil.3.nil.5.nil.7.nil.9.nil]
Copy the code

To traverse:

for val in values {
    print("Value in values is (String(describing: val))")}Copy the code

Or:

var valuesIterator = values.makeIterator()
while let val = valuesIterator.next() {
    print("Value in values is (String(describing: val))")}Copy the code

We get all the values and the optional values. If we need to filter the optional values, we can do this:

for val in values.compactMap({ $0{})print("Value in values is (val)")}Copy the code

This adds time complexity, requiring two traversals to filter out the data. We can do this using pattern matching:

for case let val? in values {
    print("Value in values is (val)")}Copy the code

Or:

valuesIterator = values.makeIterator()
while letval = valuesIterator.next(), val ! =nil {
    print("Value in values is (String(describing: val))")}Copy the code

So you can filter out nil, isn’t that easy? You can also use for case to match an array of enumerated values:

let results: [Result] = [.success, .failure]
for case .success in results {
    print("Values in results contains success.")
    break
}
Copy the code

For complex enumerated types:

enum NetResource {
    case http(resource: String)
    case ftp(resource: String)}let nets: [NetResource] = [.http(resource: "https://www.baidu.com"), .http(resource: "https://www.apple.cn"), .ftp(resource: "ftp://192.0.0.1")]
Copy the code

Filter HTTP values:

for case .http(let resource) in nets {
    print("HTTP resource (resource)")}Copy the code

forrecyclingwhereclause

We can also match patterns with a where clause following the for loop:

for notNilValue in values wherenotNilValue ! =nil {
    print("Not nil value: (String(describing: notNilValue!) )")}Copy the code

Query all numbers divisible by 3 in an array:

let rangeValues = Array(0.999)
for threeDivideValue in rangeValues where threeDivideValue % 3= =0 {
    print("Three devide value: (threeDivideValue)")}Copy the code

Query all numbers containing 3:

for containsThree in rangeValues where String(containsThree).contains("3") {
    print("Value contains three: (containsThree)")}Copy the code

Pattern matching in the Switch

Pattern matching is also very common in Switch. Using pattern matching properly in Switch can bring us many benefits, which can make our code simpler, reduce the amount of code and increase the development efficiency.

Range match

let value = 188

switch value {
case 0..<50:
    print("The value is in range [0, 50)")
case 50..<100:
    print("The value is in range [50, 100)")
case 100..<150:
    print("The value is in range [100, 150)")
case 150..<200:
    print("The value is in range [150, 200)")
case 200. :print("The value is in range [200, ")
default: break
}

// The value is in range [150, 200)
Copy the code

Matches the tuple type

Create a tuple type:

let tuples: (Int.String) = (httpCode: 404, status: "Not Found.")
Copy the code

Make a match:

switch tuples {
case (400. .let status):
    print("The http code is 40x, http status is (status)")
default: break
}
Copy the code

Create a point:

let somePoint = (1.1)
Copy the code

Make a match:

switch somePoint {
case (0.0) :print("(somePoint) is at the origin")
case (_.0) :print("(somePoint) is on the x-axis")
case (0._) :print("(somePoint) is on the y-axis")
case (-2.2, -2.2) :print("(somePoint) is inside the box")
default:
    print("(somePoint) is outside of the box")}Copy the code

As shown above, we can ignore values using the underscore _ when matching:

switch tuples {
case (404._) :print("The http code is 404 not found.")
default: break
}
Copy the code

inswitch caseThe use ofwhereclause

Using where clauses in case makes our pattern matching look more compact and makes the pattern matching more compact:

let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
    print("((x), (y)) is on the line x == y")
case let (x, y) where x == -y:
    print("((x), (y)) is on the line x == -y")
case let (x, y):
    print("((x), (y)) is just some arbitrary point")}Copy the code

conclusion

The type of pattern matching in Swift

Pattern matching can be said to be a very powerful programming mode in Swift. Using good pattern matching can help us write brief and elegant code. Pattern matching in Swift includes the following types:

  • Conditional judgment:if.guard
  • Optional binding:if let.guard let.while let.
  • The loop body:for.while.repeat while
  • switch
  • do catch

When to usewhereClause?

As we can see from the previous example, where clause is also used in many places for pattern matching. The function of where clause is equivalent to adding conditions on the basis of pattern matching. Using where clause is equivalent to:

for notNilValue in values {
    ifnotNilValue ! =nil {
        print("Not nil value: (String(describing: notNilValue!) )")}}Copy the code

As you can see, using where clauses makes our code more concise and readable. When to use where? Or where can we use where? The Swift documentation does not cover the detailed use of WHERE, but it has been found in practice that WHERE can be used in the following ways:

  • forLooping statements
  • switchbranch

In the case of if, guard, and while, we cannot add a WHERE clause, because they can combine multiple conditions. Another use of where clauses is type constraints on generic types, which are covered in the section on generics.

The original address