//一、枚舉(Enumerations)
//枚舉將一組有關聯(lián)的值定義為一個通用類型,讓你的代碼中的值可以用類型安全的方式工作
//如果你熟悉C,就會知道C中的枚舉將一組相關的名稱分配給一組整形值
//Swift中的枚舉更加靈活,不必為枚舉中每一種情況提供一個值。如果要為每一種枚舉情況提供一個值(稱為原始值),可以是字符串、字符、任何整形或者浮點型的值
//或者,枚舉可以指定把任何有關聯(lián)的值一起存儲起來,就像其他語言中的聯(lián)合或變體一樣
//你可以將一組有關聯(lián)的情況定義為枚舉的一個部分,每一種情況都有一組與其有關聯(lián)的是當類型的不同值
//二、枚舉語法
//你可以使用enum關鍵字定義枚舉,并將它的全部定義放到一對大括號中:
enum SomeEnumeration {
// enumeration definition goes here
}
//以下是指南針四個要點的示例:
enum CompassPoint {
case north
case south
case east
case west
}
//枚舉中定義的值是枚舉的多種情況(例如上面的north, south, east, 和west),你可以使用case關鍵字定義新的枚舉情況
//備注:Swift中的枚舉不像C和OC一樣,枚舉中的情況沒有一個默認的int值。
//在上面的CompassPoint示例中,CompassPoint的north, south, east, 和west四個枚舉情況不會隱式的等于0,1,2,3
//相反,每一個枚舉情況本身就是一個定義為CompassPoint類型的值
//多個 case 可以出現(xiàn)在一行中,用逗號分隔:
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
//每個枚舉定義定義了一個新類型。與Swift中的其他類型一樣,它們的名稱(例如CompassPoint和Planet)以大寫字母開頭。
//為枚舉類型指定單數(shù)而不是復數(shù)名稱,以便讀起來更簡潔明了
var directionToHead = CompassPoint.west
print(directionToHead)
//Prints "west"
//當directionToHead被CompassPoint的可能值之一初始化使用時,推斷出它的類型為CompassPoint。
//一旦directionToHead被聲明為CompassPoint,你可以使用更短的點語法將其設置為不同的CompassPoint值:
directionToHead = .east
print(directionToHead)
//Prints "east"
//directionToHead的類型是已知的,因此您可以在設置其值時刪除它的類型。這使得代碼在處理顯式類型的枚舉值時具有高度可讀性
//二、使用 Switch 語句匹配枚舉值
//您可以將單個枚舉值與switch語句匹配:
directionToHead = .south
switch directionToHead {
case .north:
print("Lots of planets have a north")
case .south:
print("Watch out for penguins")
case .east:
print("Where the sun rises")
case .west:
print("Where the skies are blue")
}
// Prints "Watch out for penguins"
//當不能為枚舉提供一個合適的case,可以提供一個default來涵蓋任何未明確的案例:
let somePlanet = Planet.earth
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
// Prints "Mostly harmless"
//三、迭代枚舉
//對于一些枚舉,需要一個集合來包含枚舉的所有情況。你可以在枚舉類型名字后面加:CaseIterable來實現(xiàn)這個能力
//Swift提供一個allCases屬性訪問枚舉類型中的所有情況
enum Beverage: CaseIterable {
case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
// Prints "3 beverages available"
//上面的示例代碼通過Beverage.allCases訪問Beverage枚舉類型中包含的所有情況
//你可以像訪問集合中的元素一樣用allCases訪問枚舉類型的所有情況,得到枚舉中的所有值
for beverage in Beverage.allCases {
print(beverage)
}
// coffee
// tea
// juice
//上面的示例中的語法,標記了枚舉遵從CaseIterable協(xié)議
//四、關聯(lián)值
//上一節(jié)示例中顯示了如何使用枚舉自己的方式定義每一個情況的值和類型。
//您可以將Planet.earth設置為常量或變量,并稍后檢查此值。
//但是,有時能夠?qū)⑵渌愋偷闹蹬c這些case值一起存儲很有用。
//這種情況的附加信息稱為關聯(lián)值,每次您在代碼中使用這種情況作為值時,它都會有所不同
//您可以定義Swift枚舉來存儲任何給定類型的關聯(lián)值,如果需要,枚舉的每種情況的值類型可以不同。
//在其他編程語言中,與這些類似的枚舉被稱為可區(qū)分聯(lián)合、標記聯(lián)合或變體
//在 Swift 中,定義任一類型產(chǎn)品條形碼的枚舉可能如下所示:
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
//以上的代碼可以理解為:
//“定義一個名為Barcode的枚舉類型,它可以采用upc帶有關聯(lián)值(Int, Int, Int, Int)類型的值,或者qrCode帶有關聯(lián)值String類型的值。”
//這個定義不用提供任何實際的Int和String值,只是定義了當存儲為Barcode常量和變量等于Barcode.upc或者Barcode.qrCode的關聯(lián)值類型
//現(xiàn)在你可以使用兩種類型創(chuàng)建一個新的條碼枚舉:
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
//你同樣可以分配不同的Barcode條碼類型
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
//你可以取出每一個關聯(lián)值作為常量或變量,在switch語句體中使用它們:
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
//如果所有的枚舉情況關聯(lián)值被取出作為常量或者變量,為了簡潔你可以在case名稱之前放一個var或者let注釋
switch productBarcode {
case let .upc(numberSystem, manufacturer, product, check):
print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).")
case let .qrCode(productCode):
print("QR code: \(productCode).")
}
// Prints "QR code: ABCDEFGHIJKLMNOP."
//五、原始值
//枚舉可以預先設置默認值,它們都是相同的類型,稱為原始值
//下面的示例展示了設置默認值的情況:
enum ASCIIControlCharacter: Character {
case tab = "\t"
case lineFeed = "\n"
case carriageReturn = "\r"
}
//原始值可以是字符串、字符或任何整數(shù)或浮點數(shù)類型。每個原始值在其枚舉聲明中必須是唯一的。
//1.隱式分配的原始值
//當您使用枚舉存儲整數(shù)或字符串原始值的時,您不必為每種情況顯式分配原始值,Swift 會自動為您分配值
//例如,當原始值為整數(shù)時,每種case的隱式原始值都比前面多1。如果第一種case沒有設置值,則其值為0。
enum Planet: Int {
case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}
//在上面的例子中,Planet.mercury有一個顯式的原始值1,Planet.venus有一個隱式的原始值2,后面的情況依次遞增
//當字符串用于原始值時,每個案例的隱含值是該案例名稱的文本。
//用字符串原始值來表示每個方向的名稱:
enum CompassPoint: String {
case north, south, east, west
}
//在上面的示例中,CompassPoint.south具有隱式的原始值"south",依此類推
//可以通過其rawValue屬性訪問枚舉案例的原始值:
let earthsOrder = Planet.earth.rawValue
// earthsOrder is 3
let sunsetDirection = CompassPoint.west.rawValue
// sunsetDirection is "west"
//2.從原始值初始化
//如果您使用原始值類型定義枚舉,則該枚舉會自動接收一個初始值設定項,該初始值設定項采用原始值類型的值(作為名為rawValue的參數(shù))并返回枚舉 case 或nil
//此示例從其原始值7識別uranus:
let possiblePlanet = Planet(rawValue: 7)
// possiblePlanet is of type Planet? and equals Planet.uranus
//如果您嘗試查找位置為11的Planet,則原始值初始化程序返回的可選Planet值將是nil
let positionToFind = 11
if let somePlanet = Planet(rawValue: positionToFind) {
switch somePlanet {
case .earth:
print("Mostly harmless")
default:
print("Not a safe place for humans")
}
} else {
print("There isn't a planet at position \(positionToFind)")
}
// Prints "There isn't a planet at position 11"
//原始值初始化器是一個可失敗的初始化器,因為不是每個原始值都會返回一個枚舉情況
//3.遞歸枚舉
//遞歸枚舉是另外一個具有一個或多個枚舉情況關聯(lián)值枚舉的實例
//你可以在枚舉case的前面插入indirect來表明它是一個遞歸,它告訴解釋器需要插入一個中間層
//例如,這是一個存儲簡單算術表達式的枚舉:
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
//您還可以在枚舉開始之前插入indirect,以表明所有具有關聯(lián)值的枚舉案例是遞歸
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
//下面的代碼顯示了正在創(chuàng)建的遞歸枚舉:(5 + 4) * 2
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
//遞歸函數(shù)是處理具有遞歸結構的數(shù)據(jù)的一種直接方式。例如,這是一個計算算術表達式的函數(shù):
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
print(evaluate(product))
// Prints "18"