Как перечислить перечисление со строковым типом?
enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}
Например, как я могу сделать что-то вроде:
for suit in Suit {
// do something with suit
print(suit.rawValue)
}
Результирующий пример:
♠
♥
♦
♣
41 ответ
Swift 4.2+
Начиная с Swift 4.2 (с Xcode 10), просто добавьте соответствие протокола в CaseIterable
извлечь выгоду из allCases
:
extension Suit: CaseIterable {}
Тогда это напечатает все возможные значения:
Suit.allCases.forEach {
print($0.rawValue)
}
Совместимость с более ранними версиями Swift (3.x и 4.x)
Просто имитируйте реализацию Swift 4.2:
#if !swift(>=4.2)
public protocol CaseIterable {
associatedtype AllCases: Collection where AllCases.Element == Self
static var allCases: AllCases { get }
}
extension CaseIterable where Self: Hashable {
static var allCases: [Self] {
return [Self](AnySequence { () -> AnyIterator<Self> in
var raw = 0
var first: Self?
return AnyIterator {
let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) }
if raw == 0 {
first = current
} else if current == first {
return nil
}
raw += 1
return current
}
})
}
}
#endif
Этот пост актуален здесь https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift
По сути, предлагаемое решение
enum ProductCategory : String {
case Washers = "washers", Dryers = "dryers", Toasters = "toasters"
static let allValues = [Washers, Dryers, Toasters]
}
for category in ProductCategory.allValues{
//Do something
}
Я сделал полезную функцию iterateEnum()
для итерации случаев для произвольного enum
типы.
Вот пример использования:
enum Suit:String {
case Spades = "♠"
case Hearts = "♥"
case Diamonds = "♦"
case Clubs = "♣"
}
for f in iterateEnum(Suit) {
println(f.rawValue)
}
выходы:
♠
♥
♦
♣
Но это только для целей отладки или тестирования: это зависит от нескольких недокументированных текущих (Swift1.1) поведений компилятора. Так что используйте его на свой страх и риск:)
Вот код:
func iterateEnum<T: Hashable>(_: T.Type) -> GeneratorOf<T> {
var cast: (Int -> T)!
switch sizeof(T) {
case 0: return GeneratorOf(GeneratorOfOne(unsafeBitCast((), T.self)))
case 1: cast = { unsafeBitCast(UInt8(truncatingBitPattern: $0), T.self) }
case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: $0), T.self) }
case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: $0), T.self) }
case 8: cast = { unsafeBitCast(UInt64($0), T.self) }
default: fatalError("cannot be here")
}
var i = 0
return GeneratorOf {
let next = cast(i)
return next.hashValue == i++ ? next : nil
}
}
Основная идея:
- Представление памяти
enum
- исключаяenum
s со связанными типами - это просто индекс случаев, когда количество случаев2...256
, это идентичноUInt8
, когда257...65536
, егоUInt16
и так далее. Итак, это может бытьunsafeBitcast
из соответствующих целых типов без знака. .hashValue
значений перечисления совпадает с индексом регистра..hashValue
перечисления значений, переданных из недопустимого индекса0
ДОБАВЛЕНО:
Пересмотрен для Swift2 и реализован идеи кастинга из ответа @Kametrixom
func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
var i = 0
return anyGenerator {
let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
return next.hashValue == i++ ? next : nil
}
}
ДОБАВЛЕНО: Пересмотрено для Swift3
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var i = 0
return AnyIterator {
let next = withUnsafePointer(to: &i) {
$0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee }
}
if next.hashValue != i { return nil }
i += 1
return next
}
}
ДОБАВЛЕНО: Пересмотрено для Swift3.0.1
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var i = 0
return AnyIterator {
let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) }
if next.hashValue != i { return nil }
i += 1
return next
}
}
Другие решения работают, но все они делают предположения, например, о количестве возможных рангов и мастей, или о том, каким может быть первый и последний ранг. Правда, расположение колоды карт, вероятно, не изменится в обозримом будущем. В целом, однако, лучше писать код, который делает как можно меньше предположений. Мое решение:
Я добавил необработанный тип в перечисление костюма, поэтому я могу использовать Suit(rawValue:) для доступа к случаям Suit:
enum Suit: Int {
case Spades = 1
case Hearts, Diamonds, Clubs
func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
func color() -> String {
switch self {
case .Spades:
return "black"
case .Clubs:
return "black"
case .Diamonds:
return "red"
case .Hearts:
return "red"
}
}
}
enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.rawValue)
}
}
}
Ниже приведена реализация метода CardD createDeck(). init(rawValue:) - сбойный инициализатор и возвращает необязательный параметр. Развернув и проверив его значение в обоих операторах while, нет необходимости предполагать количество вариантов Rank или Suit:
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
func createDeck() -> [Card] {
var n = 1
var deck = [Card]()
while let rank = Rank(rawValue: n) {
var m = 1
while let suit = Suit(rawValue: m) {
deck.append(Card(rank: rank, suit: suit))
m += 1
}
n += 1
}
return deck
}
}
Вот как вызвать метод createDeck:
let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()
Второй ответ, который действительно работает
Поэтому я наткнулся на биты и байты и создал расширение (которое я позже обнаружил, что оно очень похоже на rintaro). Это можно использовать так:
enum E : EnumCollection {
case A, B, C
}
Array(E.cases()) // [A, B, C]
Примечательно, что его можно использовать для любого перечисления (без связанных значений). Обратите внимание, что это не работает для перечислений, которые не имеют регистра.
отказ
As with rintaro's answer, this code uses the underlying representation of an enum. This representation isn't documented and might change in the future, which would break it -> I don't recommend the usage of this in production.
Code (Swift 2.2, Xcode 7.3.1, not working on Xcode 10)
protocol EnumCollection : Hashable {}
extension EnumCollection {
static func cases() -> AnySequence<Self> {
typealias S = Self
return AnySequence { () -> AnyGenerator<S> in
var raw = 0
return AnyGenerator {
let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
guard current.hashValue == raw else { return nil }
raw += 1
return current
}
}
}
}
Code (Swift 3, Xcode 8.1, not working on Xcode 10)
protocol EnumCollection : Hashable {}
extension EnumCollection {
static func cases() -> AnySequence<Self> {
typealias S = Self
return AnySequence { () -> AnyIterator<S> in
var raw = 0
return AnyIterator {
let current : Self = withUnsafePointer(to: &raw) { $0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee } }
guard current.hashValue == raw else { return nil }
raw += 1
return current
}
}
}
}
(I have no idea why I need the typealias
, but the compiler complains without it)
(I made big modification to this answer, look at edits for past versions)
Вы можете перебирать перечисление, реализуя ForwardIndexType
протокол.
ForwardIndexType
Протокол требует от вас определить successor()
функция, чтобы пройти через элементы.
enum Rank: Int, ForwardIndexType {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
// ... other functions
// Option 1 - Figure it out by hand
func successor() -> Rank {
switch self {
case .Ace:
return .Two
case .Two:
return .Three
// ... etc.
default:
return .King
}
}
// Option 2 - Define an operator!
func successor() -> Rank {
return self + 1
}
}
// NOTE: The operator is defined OUTSIDE the class
func + (left: Rank, right: Int) -> Rank {
// I'm using to/from raw here, but again, you can use a case statement
// or whatever else you can think of
return left == .King ? .King : Rank(rawValue: left.rawValue + right)!
}
Итерация по открытому или закрытому диапазону (..<
или же ...
) будет внутренне вызывать successor()
функция, которая позволяет написать это:
// Under the covers, successor(Rank.King) and successor(Rank.Ace) are called to establish limits
for r in Rank.Ace...Rank.King {
// Do something useful
}
Обновленный код: Swift 4.2 / Swift 5
enum Suit: String, CaseIterable {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}
Чтобы получить доступ к выходным данным по вопросу:
for suitKey in Suit.allCases {
print(suitKey.rawValue)
}
Выход:
♠
♥
♦
♣
CaseIterable:
предоставляет набор всех своих значений. Типы, соответствующие протоколу CaseIterable, обычно представляют собой перечисления без связанных значений. При использовании типа CaseIterable вы можете получить доступ к коллекции всех кейсов типа с помощью свойства типа allCases.
Для доступа к кейсам мы используем .allCases. Для получения дополнительной информации щелкните https://developer.apple.com/documentation/swift/caseiterable.
Мне нравится это решение, которое я собрал после нахождения этой страницы: Понимание списка в Swift
Он использует Int raw вместо Strings, но избегает ввода дважды, позволяет настраивать диапазоны и не жестко кодирует необработанные значения.
enum Suit: Int {
case None
case Spade, Heart, Diamond, Club
static let allRawValues = Suit.Spade.rawValue...Suit.Club.rawValue
static let allCases = Array(allRawValues.map{ Suit(rawValue: $0)! })
}
enum Rank: Int {
case Joker
case Two, Three, Four, Five, Six
case Seven, Eight, Nine, Ten
case Jack, Queen, King, Ace
static let allRawValues = Rank.Two.rawValue...Rank.Ace.rawValue
static let allCases = Array(allRawValues.map{ Rank(rawValue: $0)! })
}
func makeDeck(withJoker withJoker: Bool) -> [Card] {
var deck = [Card]()
for suit in Suit.allCases {
for rank in Rank.allCases {
deck.append(Card(suit: suit, rank: rank))
}
}
if withJoker {
deck.append(Card(suit: .None, rank: .Joker))
}
return deck
}
В принципе это можно сделать таким образом, предполагая, что вы не используете присваивание необработанных значений для случаев enum:
enum RankEnum: Int {
case Ace
case One
case Two
}
class RankEnumGenerator : Generator {
var i = 0
typealias Element = RankEnum
func next() -> Element? {
let r = RankEnum.fromRaw(i)
i += 1
return r
}
}
extension RankEnum {
static func enumerate() -> SequenceOf<RankEnum> {
return SequenceOf<RankEnum>({ RankEnumGenerator() })
}
}
for r in RankEnum.enumerate() {
println("\(r.toRaw())")
}
Если вы дадите enum необработанное значение Int, это значительно облегчит зацикливание.
Например, вы можете использовать anyGenerator
чтобы получить генератор, который может перечислять ваши значения:
enum Suit: Int, CustomStringConvertible {
case Spades, Hearts, Diamonds, Clubs
var description: String {
switch self {
case .Spades: return "Spades"
case .Hearts: return "Hearts"
case .Diamonds: return "Diamonds"
case .Clubs: return "Clubs"
}
}
static func enumerate() -> AnyGenerator<Suit> {
var nextIndex = Spades.rawValue
return anyGenerator { Suit(rawValue: nextIndex++) }
}
}
// You can now use it like this:
for suit in Suit.enumerate() {
suit.description
}
// or like this:
let allSuits: [Suit] = Array(Suit.enumerate())
Тем не менее, это выглядит как довольно распространенный паттерн, не было бы неплохо, если бы мы могли сделать любой перечисляемый тип перечисления простым соответствием протоколу? Хорошо с Swift 2.0 и расширениями протокола, теперь мы можем!
Просто добавьте это в ваш проект:
protocol EnumerableEnum {
init?(rawValue: Int)
static func firstValue() -> Int
}
extension EnumerableEnum {
static func enumerate() -> AnyGenerator<Self> {
var nextIndex = firstRawValue()
return anyGenerator { Self(rawValue: nextIndex++) }
}
static func firstRawValue() -> Int { return 0 }
}
Теперь каждый раз, когда вы создаете перечисление (если оно имеет необработанное значение Int), вы можете сделать его перечислимым, следуя протоколу:
enum Rank: Int, EnumerableEnum {
case Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
}
// ...
for rank in Rank.enumerate() { ... }
Если ваши значения enum не начинаются с 0
(по умолчанию), переопределить firstRawValue
метод:
enum DeckColor: Int, EnumerableEnum {
case Red = 10, Blue, Black
static func firstRawValue() -> Int { return Red.rawValue }
}
// ...
let colors = Array(DeckColor.enumerate())
Финальный костюм класса, в том числе замена simpleDescription
с более стандартным протоколом CustomStringConvertible, будет выглядеть так:
enum Suit: Int, CustomStringConvertible, EnumerableEnum {
case Spades, Hearts, Diamonds, Clubs
var description: String {
switch self {
case .Spades: return "Spades"
case .Hearts: return "Hearts"
case .Diamonds: return "Diamonds"
case .Clubs: return "Clubs"
}
}
}
// ...
for suit in Suit.enumerate() {
print(suit.description)
}
РЕДАКТИРОВАТЬ:
Swift 3
синтаксис:
protocol EnumerableEnum {
init?(rawValue: Int)
static func firstRawValue() -> Int
}
extension EnumerableEnum {
static func enumerate() -> AnyIterator<Self> {
var nextIndex = firstRawValue()
let iterator: AnyIterator<Self> = AnyIterator {
defer { nextIndex = nextIndex + 1 }
return Self(rawValue: nextIndex)
}
return iterator
}
static func firstRawValue() -> Int {
return 0
}
}
Обновлено до Swift 2.2+
func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
var i = 0
return AnyGenerator {
let next = withUnsafePointer(&i) {
UnsafePointer<T>($0).memory
}
if next.hashValue == i {
i += 1
return next
} else {
return nil
}
}
}
обновлен код до формы Swift 2.2 @Kametrixom's swer
Для Swift 3.0+ (большое спасибо Philip)
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var i = 0
return AnyIterator {
let next = withUnsafePointer(&i) {
UnsafePointer<T>($0).pointee
}
if next.hashValue == i {
i += 1
return next
} else {
return nil
}
}
}
Решение Swift 5: Решение в Swift 5 довольно просто:
enum Suit: String, CaseIterable {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}
// then access the cases like this:
for suitKey in LocalizationKey.allCases {
print(suitKey)
}
Xcode 10 с Swift 4.2
enum Filter: String, CaseIterable {
case salary = "Salary"
case experience = "Experience"
case technology = "Technology"
case unutilized = "Unutilized"
case unutilizedHV = "Unutilized High Value"
static let allValues = Filter.allCases.map { $0.rawValue }
}
Назови это
print(Filter.allValues)
Печать:
["Заработная плата", "Опыт", "Технология", "Неиспользованный", "Неиспользованная высокая стоимость"]
Старые версии
За enum
представляющий Int
enum Filter: Int {
case salary
case experience
case technology
case unutilized
case unutilizedHV
static let allRawValues = salary.rawValue...unutilizedHV.rawValue // First to last case
static let allValues = allRawValues.map { Filter(rawValue: $0)!.rawValue }
}
Назовите это так:
print(Filter.allValues)
Печать:
[0, 1, 2, 3, 4]
За enum
представляющий String
enum Filter: Int {
case salary
case experience
case technology
case unutilized
case unutilizedHV
static let allRawValues = salary.rawValue...unutilizedHV.rawValue // First to last case
static let allValues = allRawValues.map { Filter(rawValue: $0)!.description }
}
extension Filter: CustomStringConvertible {
var description: String {
switch self {
case .salary: return "Salary"
case .experience: return "Experience"
case .technology: return "Technology"
case .unutilized: return "Unutilized"
case .unutilizedHV: return "Unutilized High Value"
}
}
}
Назови это
print(Filter.allValues)
Печать:
["Заработная плата", "Опыт", "Технология", "Неиспользованный", "Неиспользованная высокая стоимость"]
Я обнаружил, что делаю .allValues
много по всему моему коду. Я наконец-то нашел способ просто соответствовать Iteratable
протокол и иметь rawValues()
метод.
protocol Iteratable {}
extension RawRepresentable where Self: RawRepresentable {
static func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var i = 0
return AnyIterator {
let next = withUnsafePointer(to: &i) {
$0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee }
}
if next.hashValue != i { return nil }
i += 1
return next
}
}
}
extension Iteratable where Self: RawRepresentable, Self: Hashable {
static func hashValues() -> AnyIterator<Self> {
return iterateEnum(self)
}
static func rawValues() -> [Self.RawValue] {
return hashValues().map({$0.rawValue})
}
}
// Example
enum Grocery: String, Iteratable {
case Kroger = "kroger"
case HEB = "h.e.b."
case Randalls = "randalls"
}
let groceryHashes = Grocery.hashValues() // AnyIterator<Grocery>
let groceryRawValues = Grocery.rawValues() // ["kroger", "h.e.b.", "randalls"]
РЕДАКТИРОВАТЬ: Swift Evolution Proposal SE-0194 Производная коллекция Enum Case предлагает поэтапное решение этой проблемы. Мы видим это в Swift 4.2 и новее. В предложении также указываются некоторые обходные пути, аналогичные тем, которые уже упоминались здесь, но, тем не менее, было бы интересно посмотреть.
Я также сохраню свой оригинальный пост для полноты картины.
Это еще один подход, основанный на ответе @Peymmankh, адаптированный к Swift 3.
public protocol EnumCollection : Hashable {}
extension EnumCollection {
public static func allValues() -> [Self] {
typealias S = Self
let retVal = AnySequence { () -> AnyIterator<S> in
var raw = 0
return AnyIterator {
let current = withUnsafePointer(to: &raw) {
$0.withMemoryRebound(to: S.self, capacity: 1) { $0.pointee }
}
guard current.hashValue == raw else { return nil }
raw += 1
return current
}
}
return [S](retVal)
}
}
Вы можете попробовать перечислить, как это
enum Planet: String {
case Mercury
case Venus
case Earth
case Mars
static var enumerate: [Planet] {
var a: [Planet] = []
switch Planet.Mercury {
case .Mercury: a.append(.Mercury); fallthrough
case .Venus: a.append(.Venus); fallthrough
case .Earth: a.append(.Earth); fallthrough
case .Mars: a.append(.Mars)
}
return a
}
}
Planet.enumerate // [Mercury, Venus, Earth, Mars]
enum Rank: Int {
...
static let ranks = (Rank.Ace.rawValue ... Rank.King.rawValue).map{Rank(rawValue: $0)! }
}
enum Suit {
...
static let suits = [Spades, Hearts, Diamonds, Clubs]
}
struct Card {
...
static func fullDesk() -> [Card] {
var desk: [Card] = []
for suit in Suit.suits {
for rank in Rank.ranks {
desk.append(Card(rank: rank,suit: suit))
}
}
return desk
}
}
Как насчет этого?
Извините, мой ответ был конкретным, как я использовал этот пост в том, что мне нужно было сделать. Для тех, кто сталкивается с этим вопросом, ищет способ найти дело в перечислении, вот способ сделать это (новое в Swift 2):
Редактировать: строчный camelCase теперь стандарт для значений перечисления Swift 3
// From apple docs: If the raw-value type is specified as String and you don’t assign values to the cases explicitly, each unassigned case is implicitly assigned a string with the same text as the name of that case.
enum Theme: String
{
case white, blue, green, lavender, grey
}
func loadTheme(theme: String)
{
// this checks the string against the raw value of each enum case (note that the check could result in a nil value, since it's an optional, which is why we introduce the if/let block
if let testTheme = Theme(rawValue: theme)
{
// testTheme is guaranteed to have an enum value at this point
self.someOtherFunction(testTheme)
}
}
Для тех, кто интересуется перечислением перечислений, ответы на этой странице, включающие статический var/let, содержащий массив всех значений перечисления, являются правильными. Последний пример кода Apple для tvOS содержит ту же самую технику.
При этом они должны встроить более удобный механизм в язык (Apple, вы слушаете?)!
Это то, что я в конечном итоге пошел; Я думаю, что это обеспечивает правильный баланс удобочитаемости и удобства обслуживания.
struct Card {
// ...
static func deck() -> Card[] {
var deck = Card[]()
for rank in Rank.Ace.toRaw()...Rank.King.toRaw() {
for suit in [Suit.Spades, .Hearts, .Clubs, .Diamonds] {
let card = Card(rank: Rank.fromRaw(rank)!, suit: suit)
deck.append(card)
}
}
return deck
}
let deck = Card.deck()
В Swift 3, когда базовое перечисление имеет {rawValue}s, вы можете реализовать протокол {Strideable}. Преимущества состоят в том, что массивы значений не создаются, как в некоторых других предложениях, и что работает стандартное выражение Swift "для i in ...", что обеспечивает хороший синтаксис.
// "Int" to get rawValue, and {Strideable} so we can iterate
enum MyColorEnum : Int, Strideable {
case Red
case Green
case Blue
case Black
//-------- required by {Strideable}
typealias Stride = Int
func advanced(by n:Stride) -> MyColorEnum {
var next = self.rawValue + n
if next > MyColorEnum.Black.rawValue {
next = MyColorEnum.Black.rawValue
}
return MyColorEnum(rawValue: next)!
}
func distance(to other: MyColorEnum) -> Int {
return other.rawValue - self.rawValue
}
//-------- just for printing
func simpleDescription() -> String {
switch self {
case .Red: return "Red"
case .Green: return "Green"
case .Blue: return "Blue"
case .Black: return "Black"
}
}
}
// this is how you use it:
for i in MyColorEnum.Red ... MyColorEnum.Black {
print("ENUM: \(i)")
}
Как и в случае ответа @Kametrixom, я считаю, что возвращать массив было бы лучше, чем возвращать AnySequence, так как вы можете получить доступ ко всем вкусностям Array, таким как count и т. Д.
Вот переписать:
public protocol EnumCollection : Hashable {}
extension EnumCollection {
public static func allValues() -> [Self] {
typealias S = Self
let retVal = AnySequence { () -> AnyGenerator<S> in
var raw = 0
return AnyGenerator {
let current : Self = withUnsafePointer(&raw) { UnsafePointer($0).memory }
guard current.hashValue == raw else { return nil }
raw += 1
return current
}
}
return [S](retVal)
}
}
Здесь метод, который я использую для итерации перечисления и предоставления нескольких типов значений из одного перечисления
enum IterateEnum: Int {
case Zero
case One
case Two
case Three
case Four
case Five
case Six
case Seven
//tuple allows multiple values to be derived from the enum case, and
//since it is using a switch with no default, if a new case is added,
//a compiler error will be returned if it doesn't have a value tuple set
var value: (french:String, spanish:String, japanese:String) {
switch self {
case .Zero: return (french:"zéro", spanish:"cero", japanese:"nuru")
case .One: return (french:"un", spanish:"uno", japanese:"ichi")
case .Two: return (french:"deux", spanish:"dos", japanese:"ni")
case .Three: return (french:"trois", spanish:"tres", japanese:"san")
case .Four: return (french:"quatre", spanish:"cuatro", japanese:"shi")
case .Five: return (french:"cinq", spanish:"cinco", japanese:"go")
case .Six: return (french:"six", spanish:"seis", japanese:"roku")
case .Seven: return (french:"sept", spanish:"siete", japanese:"shichi")
}
}
//Used to iterate enum or otherwise access enum case by index order.
//Iterate by looping until it returns nil
static func item(index:Int) -> IterateEnum? {
return IterateEnum.init(rawValue: index)
}
static func numberFromSpanish(number:String) -> IterateEnum? {
return findItem { $0.value.spanish == number }
}
//use block to test value property to retrieve the enum case
static func findItem(predicate:((_:IterateEnum)->Bool)) -> IterateEnum? {
var enumIndex:Int = -1
var enumCase:IterateEnum?
//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = IterateEnum.item(index: enumIndex)
if let eCase = enumCase {
if predicate(eCase) {
return eCase
}
}
} while enumCase != nil
return nil
}
}
var enumIndex:Int = -1
var enumCase:IterateEnum?
//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = IterateEnum.item(index: enumIndex)
if let eCase = enumCase {
print("The number \(eCase) in french: \(eCase.value.french), spanish: \(eCase.value.spanish), japanese: \(eCase.value.japanese)")
}
} while enumCase != nil
print("Total of \(enumIndex) cases")
let number = IterateEnum.numberFromSpanish(number: "siete")
print("siete in japanese: \((number?.value.japanese ?? "Unknown"))")
Это вывод:
Число ноль на французском: ноль, испанский: церо, японский: нуру
Номер один на французском: un, испанский: uno, японский: ichi
Номер два на французском: deux, испанский: dos, японский: ni
Номер три по-французски: trois, испанский: tres, японский: san
Номер четыре на французском: quatre, испанский: cuatro, японский: shi
Номер пять на французском: cinq, испанский: cinco, японский: go
Номер шесть по-французски: шесть, испанский: seis, японский: roku
Число семь на французском: сентябрь, испанский: сите, японский: шичи
Всего 8 случаев
сито по японски shichi
ОБНОВИТЬ
Недавно я создал протокол для обработки перечисления. Протокол требует перечисления с необработанным значением Int:
protocol EnumIteration {
//Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil
static func item(index:Int) -> Self?
static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {
static func findItem(predicate:((enumCase:Self)->Bool)) -> Self?
static func count() -> Int
}
extension EnumIteration where Self: RawRepresentable, Self.RawValue == Int {
//Used to iterate enum or otherwise access enum case by index order. Iterate by looping until it returns nil
static func item(index:Int) -> Self? {
return Self.init(rawValue: index)
}
static func iterate(item:((index:Int, enumCase:Self)->()), completion:(()->())?) {
var enumIndex:Int = -1
var enumCase:Self?
//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = Self.item(enumIndex)
if let eCase = enumCase {
item(index: enumIndex, enumCase: eCase)
}
} while enumCase != nil
completion?()
}
static func findItem(predicate:((enumCase:Self)->Bool)) -> Self? {
var enumIndex:Int = -1
var enumCase:Self?
//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = Self.item(enumIndex)
if let eCase = enumCase {
if predicate(enumCase:eCase) {
return eCase
}
}
} while enumCase != nil
return nil
}
static func count() -> Int {
var enumIndex:Int = -1
var enumCase:Self?
//Iterate until item returns nil
repeat {
enumIndex += 1
enumCase = Self.item(enumIndex)
} while enumCase != nil
//last enumIndex (when enumCase == nil) is equal to the enum count
return enumIndex
}
}
Эксперимент был: ЭКСПЕРИМЕНТ
Добавьте метод к Карте, который создает полную колоду карт, с одной картой каждой комбинации ранга и масти.
Поэтому, не изменяя и не улучшая данный код, кроме добавления метода (и не используя материал, который еще не изучен), я придумал это решение:
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
func createDeck() -> [Card] {
var deck: [Card] = []
for rank in Rank.Ace.rawValue...Rank.King.rawValue {
for suit in Suit.Spades.rawValue...Suit.Clubs.rawValue {
let card = Card(rank: Rank(rawValue: rank)!, suit: Suit(rawValue: suit)!)
//println(card.simpleDescription())
deck += [card]
}
}
return deck
}
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
let deck = threeOfSpades.createDeck()
Вот мой предложенный подход. Это не совсем удовлетворительно (я очень новичок в Swift и ООП!), Но, возможно, кто-то может улучшить это. Идея состоит в том, чтобы каждое перечисление предоставляло собственную информацию о диапазоне как свойства.first и.last. Он добавляет всего две строки кода к каждому перечислению: все еще немного жестко, но, по крайней мере, он не дублирует весь набор. Требуется изменить перечисление Suit, чтобы оно было Int, как перечисление Rank, а не нетипизировано.
Вместо того, чтобы отражать все решение, вот код, который я добавил к перечислению Rank где-то после операторов case (перечисление Suit аналогично):
var first: Int { return Ace.toRaw() }
var last: Int { return King.toRaw() }
и цикл, который я использовал для построения колоды в виде массива String. (В определении проблемы не указано, как должна быть структурирована колода.)
func createDeck() -> [String] {
var deck: [String] = []
var card: String
for r in Rank.Ace.first...Rank.Ace.last {
for s in Suit.Hearts.first...Suit.Hearts.last {
card = Rank.simpleDescription( Rank.fromRaw(r)!)() + " of " + Suit.simpleDescription( Suit.fromRaw(s)!)()
deck.append( card)
}
}
return deck
}
Это неудовлетворительно, потому что свойства связаны с элементом, а не с перечислением. Но это добавляет ясности в циклы for. Я бы хотел сказать Rank.first вместо Rank.Ace.first. Это работает (с любым элементом), но это некрасиво. Может кто-нибудь показать, как поднять это до уровня enum?
И чтобы заставить его работать, я вывел метод createDeck из структуры Card... не мог понять, как получить массив [String], возвращаемый из этой структуры, и это все равно кажется плохим местом для размещения такого метода.
Имея дело с Swift 2.0
вот мое предложение:
Я добавил необработанный тип в Suit
enum
enum Suit: Int {
затем:
struct Card {
var rank: Rank
var suit: Suit
func fullDeck()-> [Card] {
var deck = [Card]()
for i in Rank.Ace.rawValue...Rank.King.rawValue {
for j in Suit.Spades.rawValue...Suit.Clubs.rawValue {
deck.append(Card(rank:Rank(rawValue: i)! , suit: Suit(rawValue: j)!))
}
}
return deck
}
}
Это похоже на хак, но если вы используете необработанные значения, вы можете сделать что-то вроде этого
enum Suit: Int {
case Spades = 0, Hearts, Diamonds, Clubs
...
}
var suitIndex = 0
while var suit = Suit.fromRaw(suitIndex++) {
...
}
Другое решение:
enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
static var count: Int {
return 4
}
init(index: Int) {
switch index {
case 0: self = .spades
case 1: self = .hearts
case 2: self = .diamonds
default: self = .clubs
}
}
}
for i in 0..<Suit.count {
print(Suit(index: i).rawValue)
}
Я сделал это, используя свойство computed, которое возвращает массив всех значений (благодаря этому посту http://natecook.com/blog/2014/10/loopy-random-enum-ideas/). Однако он также использует int raw-values, но мне не нужно повторять все члены перечисления в отдельном свойстве.
ОБНОВЛЕНИЕ Xcode 6.1 немного изменил способ получения члена enum с использованием необработанного значения, поэтому я исправил листинг. Также исправлена небольшая ошибка с неправильным первым необработанным значением
enum ValidSuits:Int{
case Clubs=0, Spades, Hearts, Diamonds
func description()->String{
switch self{
case .Clubs:
return "♣︎"
case .Spades:
return "♠︎"
case .Diamonds:
return "♦︎"
case .Hearts:
return "♥︎"
}
}
static var allSuits:[ValidSuits]{
return Array(
SequenceOf {
() -> GeneratorOf<ValidSuits> in
var i=0
return GeneratorOf<ValidSuits>{
return ValidSuits(rawValue: i++)
}
}
)
}
}
Это довольно старый пост из Swift 2.0. Теперь здесь есть несколько лучших решений, использующих новые функции swift 3.0: перебор Enum в Swift 3.0
И в этом вопросе есть решение, которое использует новую функцию (еще не выпущенную, когда я пишу это редактирование) Swift 4.2: Как мне получить счетчик перечисления Swift?
Есть много хороших решений в этой теме и других, однако некоторые из них очень сложны. Мне нравится максимально упрощать. Вот решение, которое может работать или не работать для разных нужд, но я думаю, что оно работает хорошо в большинстве случаев:
enum Number: String {
case One
case Two
case Three
case Four
case EndIndex
func nextCase () -> Number
{
switch self {
case .One:
return .Two
case .Two:
return .Three
case .Three:
return .Four
case .Four:
return .EndIndex
/*
Add all additional cases above
*/
case .EndIndex:
return .EndIndex
}
}
static var allValues: [String] {
var array: [String] = Array()
var number = Number.One
while number != Number.EndIndex {
array.append(number.rawValue)
number = number.nextCase()
}
return array
}
}
Итерировать:
for item in Number.allValues {
print("number is: \(item)")
}
У перечислений есть методы toRaw() и fromRaw(), поэтому, если ваше необработанное значение - Int, вы можете перебирать от первого до последнего перечисления:
enum Suit: Int {
case Spades = 1
case Hearts, Diamonds, Clubs
func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
}
for i in Suit.Spades.toRaw()...Suit.Clubs.toRaw() {
if let covertedSuit = Suit.fromRaw(i) {
let description = covertedSuit.simpleDescription()
}
}
Одна проблема заключается в том, что вам нужно проверить дополнительные значения перед запуском метода simpleDescription, поэтому мы сначала устанавливаем для convertSuit наше значение, а затем устанавливаем константу для convertSuit.simpleDescription().