前言
仅为视频学习笔记
原始值 (Raw Values)
★ 枚举成员可以使用相同类型的默认值预先关联,这个默认值叫做:原始值
例子-1
enum PokerSuit : Character { case spade = "♠️" case heart = "♥️" case diamond = "♦️" case club = "♣️" }
我以上面代码为例,我们会发现如下面这句代码PokerSuit和Character中间有一个冒号
enum PokerSuit : Character
注意,这里的冒号并不代表什么继承之类的。不要一想到冒号就是遵守协议什么的,不是这个意思。
这个冒号用在枚举里面,你就认为它是一个原始值类型,不存在什么继承的概念,不要搞混。
一旦,我们写上 :Character,就意味着这个枚举,到时候它的成员所关联的的值是什么呢?就是它的默认值、原始值是什么呢?就是Character类型的。
你看,下面这句
case spade = "♠️"
它关联的原始值是什么呢?就是♠️符号,如这个PokerSuit枚举中case 值依次对应的原始值为♠️、♥️、♦️、♣️。其实,这些是什么鬼呢?是扑克牌的花色。
我们会发现,四种枚举成员,都用同一种类型(Character),跟它预先关联了。
我们看一下怎么用
var suit = PokerSuit.spade print(suit) // spade print(suit.rawValue) // ♠️ print(PokerSuit.club.rawValue) //♣️
我们假设定义一个 suit变量直接PokerSuit.spade,意思是把第一个成员赋值给了变量suit,到时候我们打印出来就是// spade。
由于呢,我们每一个枚举成员,都有自己的一个原始值,例如:case spade = "♠️",是等于号后面的♠️。所以说,你这个变量如果当初存储的是spade的话,那么你就能通过rawValue这个属性访问的话,相当于你访问的就是spade这个家伙的原始值。因为你这个suit枚举变量存储的成员是spade,所以它的原始值(rawValue)就是♠️。即 print(suit.rawValue)打印出来就是 // ♠️这个家伙。
当然, PokerSuit枚举中每一个枚举成员都有一个原始值所以我可以PokerSuit.club.rawValue,也就是说,我们可以直接拿到club这个成员,它的原始值其实就是♣️这个梅花。所以 print(PokerSuit.club.rawValue) 打印出来就是这个♣️梅花
例子-2
enum Grade : String { case perfect = "A" case great = "B" case good = "C" case bad = "D" } print(Grade.perfect.rawValue) // A print(Grade.great.rawValue) // B print(Grade.good.rawValue) // C print(Grade.bad.rawValue) // D
我们看上面这段,代码。我们等级定义了四种,分别为 perfect、great、good、bad。然后呢,我们在Grade后面写了一个冒号加字符串( : String),意思就是枚举内的成员,到时候关联的原始值就是String,分别为A、B、C、D。
根据上面的print输出,我们可以看出,通过 (Grade.perfect.rawValue)打印出成员的原始值。其实,打印出来的就是A、B、C、D。
隐式原始值 (Implicitly Assigned Raw Values)
★ 如果枚举的原始值类型是Int、String,Swift会自动分配原始值
例子-1
enum Direction : String { case north = "north" case south = "south" case east = "east" case west = "west" }
我们看上面这个例子,方向,就是东、南、西、北四个方向,由于呢,我们上面** : String**这样写的,就意味着到时候枚举内部所预先关联的原始值是字符串类型。其实,上面这种写法等价于下面这种写法:
enum Direction : String { case north, south , east, west } print(Direction.north) // north print(Direction.north.rawValue) // north
也就是,说上面两段代码,完全等价,因为我们刚才说了,如果你的原始值类型是String的话,这个swift其实会自动分配north, south , east, west这几个成员的原始值。
那么,怎么个分配法呢?就是你的成员的名字叫什么,那么到时候给你预先关联的原始值就是什么 如下面,这两段代码的输出,一个打印成员,一个是打印成员的默认值,都为north。
print(Direction.north) // north print(Direction.north.rawValue) // north
例子- 2
enum Season : Int { case spring, summer, autumn, winer } print(Season.spring.rawValue) // 0 print(Season.summer.rawValue) // 1 print(Season.autumn.rawValue) // 2 print(Season.winer.rawValue) // 3
如果你枚举类型是Int类型,其实也是一样的。它会自动分配这个原始值。那么,如果是Int,它怎么分配呢?
如spring这个家伙,它默认会关联一个 0,summer关联一个 1,关联一个 2,关联一个 3,即按顺序 0,1,2,3。
所以,我们打印出来的Season内部成员的原始值依次为: 0,1,2,3。
例子- 3
enum Season : Int { case spring = 1, summer, autumn = 4, winer } print(Season.spring.rawValue) // 1 print(Season.summer.rawValue) // 2 print(Season.autumn.rawValue) // 4 print(Season.winer.rawValue) // 3
如上代码,如果你指定了spring的原始值为1的话,那么下一个就会递增,也就是说summer的原始值为2。如果autumn的原始值为4的话,那么下一个winer的原始值就为5。所以这几个家伙按顺序打印出来就是 // 1,2,4,5
枚举递归 (Recursive Enumeration)
indirect enum ArithExpr { case number(Int) case sum(ArithExpr,ArithExpr) case difference(ArithExpr,ArithExpr) }
什么叫做枚举递归呢?你看一下,我们现在定一个一个枚举类型,发现了吗?这个枚举变量的成员里面,它的关联值,也用到了ArithExpr枚举类型。也就是说,这个枚举成员里面也用到了这个枚举,这个叫做递归枚举。也就是你自己用到了你自己的类型。
那么递归枚举要定义成功的话,你得在枚举前面加上indirect这个关键字。如果你不加编译器会报错的,编译器会直接提醒你,你这个递归枚举必须得加这个关键字。
或者,还可以下面这样写:
enum ArithExpr { case number(Int) indirect case sum(ArithExpr,ArithExpr) indirect case difference(ArithExpr,ArithExpr) }
如上,只有
indirect case sum(ArithExpr,ArithExpr) indirect case difference(ArithExpr,ArithExpr)
这两个case 才用到了递归,也就是自己用自己,所以你完全可以如上,在这两个家伙前面加上indirect,也是可以的,但是一般简单的方式就是第一种,直接在最前面加上indirect就可以了。
那么,我么看一下这个枚举表达什么含义呢?ArithExpr你可以理解为算数表达式的简称。算数表达式的话,我们现在分为两种,一种是sum加法,一种是difference减法。那么加法的话 sum(ArithExpr,ArithExpr),你依然要传两个算数表达式给我,减法也是一样。
然后呢,算数表达式的一部分,还有一个情况是什么呢?这种情况,其实就是数值 number。
你思考一下,平常我们学的算数表达式,不是这样的吗?
1 + 2
那么,这个算数表达式它由什么组成,有具体的数值,也就是操作数,还有你具体想要做什么运算(+) ,是由这些东西组成的。
所以,我们看一下,我们定义的算数表达式,不就是这样的吗?
首先,一种情况,你可能是一个数字case number(Int),或者说你可能是一个具体的运算 indirect case sum(ArithExpr,ArithExpr)。
那我们看一下,那么,我们要定义一则运算的话,可以下面这样定义:
let five = ArithExpr.number(5) let four = ArithExpr.number(4) let two = ArithExpr.number(2) let sum = ArithExpr.sum(five, four) let difference = ArithExpr.difference(sum, two)
我们看第一个
let five = ArithExpr.number(5)
ArithExpr.number(5)相当于 five这个枚举变量其实是 case number(Int)这个成员。第二句和第三句如同。都是属于 case number(Int)这个成员。
接下来,再来一个sum,sum它是由于要接收两个枚举类型,那你想想,当定义的five和four,这两个不是刚好就是枚举类型吗?所以这个five和four是可以传给sum的将它们关联起来,所以这个 let sum = ArithExpr.sum(five, four),sum关联了five和four。那么,到时候你思考一下,那么它的语义,想表达的就是5+4吗? 所以,sum,其实代表的就是5+4
然后,最后一个呢? let difference = ArithExpr.difference(sum, two)。这个算数表达式difference,它是接收两个枚举类型,那么很明显这个sum和这个two,这两个家伙就是ArithExpr这个枚举类型的,那么我们可以猜想的到我们difference这个枚举变量,想表达的是什么?首先你这个sum执行的就是5+4。那么5+4又要减 2,应该是这样的一个意思。
那么最终,我们是想根据difference表达式的语义,算出最终的值,怎么办呢?这个时候,我们可以写一个函数,如下:
func calculate(_ expr: ArithExpr) -> Int { switch expr { case let .number(value): return value case let .sum(left, right): return calculate(left) + calculate(right) case let .difference(left, right): return calculate(left) - calculate(right) } } calculate(difference)
你传一个表达式给calculate,它来给你算,里面有一个switch,就不说了。所以呢,到时候,就把这个difference变量传进去,就进行详细的判断,根据你是哪一个case进行相应的判断。比如说,你是sum 那就作加法,difference就作减法,如果你number就返回具体的数值。
★ 本质是想说明,枚举类型中case用到它本身的话,这个递归枚举必须得加一个indirect 关键字。