设想我们有这样一个需求,通过对于一副扑克牌的花色和牌面大小的 enum
类型,凑出一套不含大小王的扑克牌的数组。
扑克牌花色和牌面大小分别由下面两个 enum
来定义:
enum Suit: String {
case Spades = "黑桃"
case Hearts = "红桃"
case Clubs = "草花"
case Diamonds = "方片"
}
enum Rank: Int, Printable {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
var description: String {
switch self {
case .Ace:
return "A"
case .Jack:
return "J"
case .Queen:
return "Q"
case .King:
return "K"
default:
return String(self.rawValue)
}
}
}
最容易想到的方式当然不外乎对两个 enum
进行两次循环,先循环取出 Suit
中的四种花色,然后在其中循环 Rank
类型取出数字后,就可以配合得到 52 张牌了。
在其他很多语言中,我们可以对 enum
类型或者其某个类似 values
的属性直接进行枚举,写出来的话,可能会是类似这样的代码:
for suit in Suit.values {
for rank in Rank.values {
// ...
// 处理数据
}
}
但是在 Swift 中,由于在 enum
中的某一个 case 中我们是可以添加具体值的 (就是 case Some(T)
这样的情况),因此直接使用 for...in
的方式在语义上是无法表达出所有情况的。不过因为在我们这个特定的情况中并没有带有参数的枚举类型,所以我们可以利用 static 的属性来获取一个可以进行循环的数据结构:
protocol EnumeratableEnumType {
static var allValues: [Self] {get}
}
extension Suit: EnumeratableEnumType {
static var allValues: [Suit] {
return [.Spades, .Hearts, .Clubs, .Diamonds]
}
}
extension Rank: EnumeratableEnumType {
static var allValues: [Rank] {
return [.Ace, .Two, .Three,
.Four, .Five, .Six,
.Seven, .Eight, .Nine,
.Ten, .Jack, .Queen, .King]
}
}
在这里我们使用了一个接口来更好地定义适用的接口。关于其中的 class
和 static
的使用情景,可以参看这一篇总结。在实现了 allValues
后,我们就可以按照上面的思路写出:
for suit in Suit.allValues {
for rank in Rank.allValues {
print("\(suit.rawValue)\(rank)")
}
}
// 输出:
// 黑桃A
// 黑桃2
// 黑桃3
// ...
// 方片K