393,Swift中if case let 的模式匹配模式

if case

在學(xué)習(xí) if case之前,我們先想想,那些地方使用到了case 這個關(guān)鍵字,毫無疑問,絕大多數(shù)使用case的時候,是在 switch語法中。
Swift中的if case主要用于模式匹配。跟switch很類似,但它本身有特別的使用場景,區(qū)別switch在于,switch用于對所有的對象可能值進(jìn)行判斷,而if case之需要對關(guān)注的可能值進(jìn)行判斷。老規(guī)矩,還是代碼上看看。
假設(shè)我有一個枚舉變量CarBrand,汽車品牌。它的結(jié)構(gòu)如下:

enum CarBrand{
   case  BMW
   case  AUDI
   case  BENZ
}

如果使用switch進(jìn)變量匹配,并按照不同的車的品牌輸出對應(yīng)的中文名字(這種需求當(dāng)然可以通過添加屬性值的方式更簡便的解決,但這里我們只討論if case的使用,暫時不關(guān)注其他的方式。)。我們可能是這樣做的:

let myBrand = CarBrand.BMW
switch myBrand{
    case .BMW: do{   print("寶馬")  }
    case .AUDI: do{   print("奧迪")  }
    case .BENZ: do{   print("奔馳")  }
}

實際上,我們可能僅僅是想找出BMW的品牌而已,但是我卻需要寫一部分跟這個品牌無關(guān)的代碼。我們可以進(jìn)行一點簡化:

switch myBrand{
    case .BMW: do{   print("寶馬")  }
    default: ()  // do  nothing
}

可能覺得這個方式還是過于繁瑣,有什么辦法可以像if那樣對枚舉值判斷嗎?
if case應(yīng)該能幫到你:

let myBrand = CarBrand.BMW
if case .BMW = myBrand{
   print("寶馬")
}

這樣不就舒服多了,不僅沒有冗余的代碼,還有具備了更好的可讀性。

if case let

if case let它實際上不是一個完整的關(guān)鍵字,它if caselet的組合。我們同樣會在對枚舉類型進(jìn)行模式匹配的時候用到它,不過這個情況稍微復(fù)雜一點。
假設(shè)我的CarBrand枚舉想要集合更多的東西,比如它的中文名字和生產(chǎn)地。我們可以這樣定義這個枚舉:

enum CarBrand{
    case  BMW(name:String,Production:String)
    case  AUDI(name:String,Production:String)
    case  BENZ(name:String,Production:String)
}

現(xiàn)在我定一個枚舉變量:

let myCar = CarBrand.BMW(name: "寶馬",Production: "德國")

如果我想輸出myCar的關(guān)聯(lián)屬性,直接使用點語法什么的都顯然是不行的。我們可在枚舉中自定義一些輸出關(guān)聯(lián)屬性的方法,但是這個做法比較繁瑣,并且破壞了枚舉結(jié)構(gòu)。
比較簡單的是可以使用switch:

switch myCar {
  case let CarBrand.BMW(name,Production):
    print("This car named \(name),from\(Production)")
  default: () // 不做任何處理
}

這樣確實可以的。同樣我們覺得這樣還是過于繁瑣了。

使用if case let

 let myBrand = CarBrand.BMW(name: "寶馬",Production: "德國")
 if case let CarBrand.BMW(name, Production) = myBrand{
     print("\(name),\(Production)")
}

是不是更加簡潔呢。當(dāng)然,if case let 還可一配合where從句寫出更加優(yōu)雅的代碼哦。

關(guān)于大于或者小于的匹配

//傳統(tǒng)用法
if x>=6 && x < 12 { 

}
//模式匹配用法
if case 6..<12 = x {

}

復(fù)雜一點,如果x是Optional對象,這里可以采用嵌套的模式匹配

//傳統(tǒng)用法
if let x = x, x>=6 && x < 12 { 

}
//模式匹配用法
if case .some(6..<12) = x {

}

pattern有很多種,看一下官方文檔

GRAMMAR OF A PATTERN
pattern → wildcard-pattern
pattern → identifier-pattern
pattern → value-binding-pattern
pattern → tuple-pattern
pattern → enum-case-pattern
pattern → optional-pattern
pattern → type-casting-pattern
pattern → expression-pattern

這里不詳細(xì)介紹這些pattern,更多的應(yīng)該switch里面去學(xué)習(xí),也就是說switch里面可以使用的在if里面都可以使用
這里舉一些例子

1. type-casting-pattern

var t : Any = 10
if case is Int = t {
    print("bingo")
}
if t is Int {

}

上面兩種用法結(jié)果是一樣的,一種是模式匹配,另外一種是常用的is operator

2. tuple-pattern

if case (1..<10, 1..<20) = (7, 8) {

}

在這里(1..<10, 1..<20)是一個pattern,而不是普通的tuple,這里會對左右兩邊的tuple的元素一一進(jìn)行patteren match
如果把前面換成一個tuple就會出錯里,試試看下面的代碼

let pattern = (1..<10, 1..<20)
if case pattern = (7,8) {

}

3. optional-pattern

var t : Any? = 10
// 判斷t是不是nil,和判斷 t != nil 等效
if case _? = t {

}
//判斷t是不是nil,如果有值則綁定到x
if case let x? = t {

}

4. expression-pattern

前面提到的case 6..<12 = x實際上就是這一種pattern,實際上這里調(diào)用了一個函數(shù),也是一個操作符~=

    func ~= (pattern: String, value: Int) -> Bool {
        return pattern == "\(value)"
    }
    if case "123" = 123 {

    }

通過重載~=操作符,我們可以實現(xiàn)很多自定義的模式匹配,結(jié)合起來可以有很多有趣的用法

上面通過一些例子介紹了if中的模式匹配用法,在代碼中也有常常遇到這種用法,這種用法可以看成是switch case的一種簡易寫法,這樣理解起來就比較容易了

if case expression1 = expression2 {
    statements
}
//等價于
switch expression2 {
    case expression1:
        statements
    default:
        break
}

available用法

這是一種特殊的if用法,用來判斷運行環(huán)境

if #available(platform name version, ..., *) {
    statements to execute if the APIs are available
} else {
    fallback statements to execute if the APIs are unavailable
}

這個比較簡單,不再介紹

混合使用

上面四種if的使用方法可以混合使用,在一個if語句中可以有多個condition,通過,分開,這些condition會依序執(zhí)行

var t : Any? = 10

if case let xs? = t, xs is Int {
    print("bingo")
}

這里會首先將t進(jìn)行optional-pattern的匹配和綁定,然后判斷xs是否是Int
condition之前常用&&操作符進(jìn)行的判斷也可以換成使用逗號分割,效果是一樣的

最后看一下官方文檔對ifcondition list的描述

可選類型的模式匹配

截屏2020-12-28 下午5.36.10.png
截屏2020-12-28 下午5.42.09.png

swift之模式(Pattern)

模式簡介

  • 模式代表單個值或者復(fù)合值的結(jié)構(gòu)。比如 (10,20)("Tom","Mary")在結(jié)構(gòu)上并無本質(zhì)差別,都是包含兩個值的元組。

  • swift中的模式分為兩類,一類能成功匹配任何類型的值,另一類在運行時匹配某個特定值時可能會失敗

  • 第一類用于解構(gòu)簡單變量、常量和可選綁定中的值。包括通配符模式標(biāo)識符模式以及包含這兩種模式的值綁定模式元組模式。可以為這類模式制定一個類型標(biāo)注,從而限定他們只匹配某種特定類型的值。

  • 第二類用于全模式匹配。,這種情況下你視圖匹配的值在運行時可能不存在。包括枚舉用例模式可選模式表達(dá)式模式類型轉(zhuǎn)換模式

具體實例

  • 通配符模式(Wildcard Pattern):不關(guān)注匹配的值
 for _ in 1...3 {
    // Do something three times
 }
  • 標(biāo)識符模式(Identifier Pattern):匹配任何值并且把匹配到的值綁定給變量或者常量。

  • 值綁定模式(Value-Binding pattern):把匹配到的值綁定給一個變量或者常量,綁定給常量時用let,綁定給變量時用var

 let point = (1,2)
 switch point {
 case let (x,y):
    print("The point is at (\(x), \(y))") //The point is at (1, 2)
 }
  • 元組模式
  • 逗號分隔的具有一個或者多個模式的列表
  • 可以使用類型標(biāo)注去限定一個元組模式可以匹配那些元組類型。
  • 元組模式被用于for-in語句或者變量火證常量聲明時,金可以包含通配符模式、標(biāo)識符模式、可選模式或者其他包含這些模式的元組模式。
 let points = [(0,0),(1,0),(2,0),(0,3),(1,1),(2,1)]
 for (x,y) in points where y == 0 || x == 0 {
    print("Point(\(x), \(y)) is on axis!")
 }

 for point in points {
    switch point {
    case (_,0), (0,_):
        print("Point(\(point.0), \(point.1)) is on axis!")
    default:
        break
    }
 }
  • 枚舉用例模式(Enumeration Case Pattern)
 enum SomuEnum {
    case left,right
 }
 let direction: SomuEnum? = .left
 switch direction {
 case .left:
    print("Turn left!")
 case .right:
    print("Turn right!")
 case nil:
    print("Keep going straiht!")
 }


var someOptional: Optional<Int> = 43
 //枚舉用例模式匹配
 switch someOptional {
 case .some(let x):
    print(x) // 43
 case .none:
    print("nil")
 }
 if case .some(let x) = someOptional {
    print(x) // 43
 }

  • 可選項模式(Optional Pattern)

  • 可選項模式匹配Optional<Wrapped>枚舉在some(Wrapped)中包裝的值。

 var someOptional: Optional<Int> = 43
 if  case let x? = someOptional {
    print(x) // 43
 }

 let arrayOfOptionalInts: [Int?] = [nil,2,3,5,nil]
 for case let number? in arrayOfOptionalInts {
    print("Found a \(number) !")
 }

  • 類型轉(zhuǎn)換模式(Type-Casting Pattern)

  • is模式:只出現(xiàn)在switch語句的case標(biāo)簽中,當(dāng)一個值的類型在運行時與模式右邊指定類型一致或者是其子類的情況下才會匹配。

  • as模式:當(dāng)一個值的類型在運行時和右邊指定類型一致或者是其子類的情況下,才會匹配。如果匹配成功,被匹配值的類型被撞換成as模式右邊指定的類型。

 class MediaItem {
    var name: String
    init(name: String) {
        self.name = name
    }
 }

 class Movie: MediaItem {
    var director: String
    init(name: String, director: String) {
        self.director = director
        super.init(name: name)
    }
 }
 
 class Song: MediaItem {
    var artist: String
    init( name: String, artist: String) {
        self.artist = artist
        super.init(name: name)
    }
 }

 let library: [MediaItem] = [
    Movie(name: "Casablanca", director: "Michael Curtiz"),
    Song(name: "Blue Suede Shoes", artist: "Elvis Presley"),
    Movie(name: "Citizen Kane", director: "Orson Welles"),
    Song(name: "The One And Only", artist: "Chesney Hawkes"),
    Song(name: "Never Gonna Give You Up", artist: "Rick Astley")
 ]

 for mediaItem in library {
    switch mediaItem {
    case is Song :
        print("Song: \(mediaItem.name)")
    case let movie as Movie:
        print("Movie: \(movie.name)  director: \(movie.director)")
    default:
        break
    }
 }

  • 表達(dá)式模式(Expression Pattern)

    • 只出現(xiàn)在switchcase標(biāo)簽中。

    • 表達(dá)式模式代表的表達(dá)式會使用Swift的標(biāo)準(zhǔn)庫中的~=運算符與輸入表達(dá)式的值進(jìn)行比較,如果~=預(yù)算內(nèi)算符返回true,則匹配成功。因此自定義類型要使用表達(dá)式模式匹配,需要重載~=運算符

let point = (0,0)
 func ~= (pattern: String, value: Int) -> Bool {
    return  pattern == "\(value)"
 }
 switch point {
 case ("0","0"):
    print("(0,0) is at the origin.")
 default:
    print("The point is at (\(point.0), \(point.1))")
 }

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容