//一、枚举(Enumerations)
//枚举将一组有关联的值定义为一个通用类型,让你的代码中的值可以用类型安全的方式工作
//如果你熟悉C,就会知道C中的枚举将一组相关的名称分配给一组整形值
//Swift中的枚举更加灵活,不必为枚举中每一种情况提供一个值。如果要为每一种枚举情况提供一个值(称为原始值),可以是字符串、字符、任何整形或者浮点型的值
//或者,枚举可以指定把任何有关联的值一起存储起来,就像其他语言中的联合或变体一样
//你可以将一组有关联的情况定义为枚举的一个部分,每一种情况都有一组与其有关联的是当类型的不同值
//二、枚举语法
//你可以使用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 可以出现在一行中,用逗号分隔:
enum Planet {
case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune
}
//每个枚举定义定义了一个新类型。与Swift中的其他类型一样,它们的名称(例如CompassPoint和Planet)以大写字母开头。
//为枚举类型指定单数而不是复数名称,以便读起来更简洁明了
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来实现这个能力
//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协议
//四、关联值
//上一节示例中显示了如何使用枚举自己的方式定义每一个情况的值和类型。
//您可以将Planet.earth设置为常量或变量,并稍后检查此值。
//但是,有时能够将其他类型的值与这些case值一起存储很有用。
//这种情况的附加信息称为关联值,每次您在代码中使用这种情况作为值时,它都会有所不同
//您可以定义Swift枚举来存储任何给定类型的关联值,如果需要,枚举的每种情况的值类型可以不同。
//在其他编程语言中,与这些类似的枚举被称为可区分联合、标记联合或变体
//在 Swift 中,定义任一类型产品条形码的枚举可能如下所示:
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
//以上的代码可以理解为:
//“定义一个名为Barcode的枚举类型,它可以采用upc带有关联值(Int, Int, Int, Int)类型的值,或者qrCode带有关联值String类型的值。”
//这个定义不用提供任何实际的Int和String值,只是定义了当存储为Barcode常量和变量等于Barcode.upc或者Barcode.qrCode的关联值类型
//现在你可以使用两种类型创建一个新的条码枚举:
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
//你同样可以分配不同的Barcode条码类型
productBarcode = .qrCode("ABCDEFGHIJKLMNOP")
//你可以取出每一个关联值作为常量或变量,在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."
//如果所有的枚举情况关联值被取出作为常量或者变量,为了简洁你可以在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"
}
//原始值可以是字符串、字符或任何整数或浮点数类型。每个原始值在其枚举声明中必须是唯一的。
//1.隐式分配的原始值
//当您使用枚举存储整数或字符串原始值的时,您不必为每种情况显式分配原始值,Swift 会自动为您分配值
//例如,当原始值为整数时,每种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的参数)并返回枚举 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.递归枚举
//递归枚举是另外一个具有一个或多个枚举情况关联值枚举的实例
//你可以在枚举case的前面插入indirect来表明它是一个递归,它告诉解释器需要插入一个中间层
//例如,这是一个存储简单算术表达式的枚举:
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
//您还可以在枚举开始之前插入indirect,以表明所有具有关联值的枚举案例是递归
indirect enum ArithmeticExpression {
case number(Int)
case addition(ArithmeticExpression, ArithmeticExpression)
case multiplication(ArithmeticExpression, ArithmeticExpression)
}
//下面的代码显示了正在创建的递归枚举:(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))
//递归函数是处理具有递归结构的数据的一种直接方式。例如,这是一个计算算术表达式的函数:
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"