Dize türü ile bir numaralandırma nasıl numaralandırılır?


530
enum Suit: String {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"
}

Örneğin, nasıl bir şey yapabilirim:

for suit in Suit {
    // do something with suit
    print(suit.rawValue)
}

Sonuç örneği:

♠
♥
♦
♣

Hangi durumda türü bilmezdiniz?
mtbennett

Haklısın, bu durumda String tipi.
Lucien

Swift'te henüz bir yansıma yok ...
Sulthan

22
Numaralandırma olarak adlandırılması ironik değil mi, ama Swift'te numaralandırmak için çok acı verici mi?
Charlton Provatas

3
@CharltonProvatas Swift'in tek dezavantajı bu olsaydı, bir gün derdim. Kaç kişinin bunun için farklı çözümler sunduğuna baktığımda, sadece klavyemi kemiriyorum.
qwerty_so

Yanıtlar:


267

Hızlı 4.2+

Swift 4.2 (Xcode 10 ile) ile başlayarak, CaseIterableyararlanabileceğiniz protokol uygunluğunu ekleyin allCases. Bu protokol uyumluluğunu eklemek için bir yere yazmanız yeterlidir:

extension Suit: CaseIterable {}

Numaralandırma size aitse, uygunluğu doğrudan bildirimde belirtebilirsiniz:

enum Suit: String, CaseIterable { case spades = "♠"; case hearts = "♥"; case diamonds = "♦"; case clubs = "♣" }

Ardından aşağıdaki kod olası tüm değerleri basacaktır:

Suit.allCases.forEach {
    print($0.rawValue)
}

Önceki Swift sürümleriyle uyumluluk (3.x ve 4.x)

Swift 3.x veya 4.0'ı desteklemeniz gerekiyorsa, aşağıdaki kodu ekleyerek Swift 4.2 uygulamasını taklit edebilirsiniz:

#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

@DmitryPetukhov Size yardımcı olmaktan memnuniyet duyarız, ancak: (1) Kodun en son sürümünü aldığınızdan emin misiniz? (bir kilitlenme bir ay önce düzeltildi) ve (2) lütfen kilitlenme oluşturabilecek özel türünüzden bir MCVE ve Xcode sürümünüzü verin.
Cœur

Hata ayıklama yapıları için bu benim için iyi çalışıyor, ancak bir sürüm oluşturup TestFlight'a yükler yüklemez çöküyor. Apple bunu bir şekilde çıkarıyor mu?
Daniel Wood

1
Sürümünüzün, CaseIterator'ın dahili sürümü üzerinde bir tane pozitif olduğu görülüyor. Sürümünüzle başka bir dosyada tanımlanan numaralandırmaları genişletebilirim. Uzantıdaki allCas'leri herkese açık hale getirirseniz, farklı çerçevelerde tanımlanan numaralandırmaları da genişletebilirsiniz.
adamfowlerphoto

1
@CyberMew Cevabı açıklığa kavuşturmak için güncelledim. Swift kitap ve CaseIterable'den bahsedilen Xcode 10 sürüm notları cevabımın arkasındadır Stringve mevcut Yığın Taşması sorusunun tersine , enumun desteklenmediği basitleştirilmiş örnekler kullandılar .
Cur

1
"# İf! Swift (> = 4.2)" nin önemini vurgulamak istiyorum. Kodunuzu hızlı 4.2'den önce yazdıysanız ve "# if! Swift (> = 4.2)" altındaki kodu kaldırmayı unuttuysanız, test cihazınızda yerel olarak oluşturmak için Xcode Sürüm 11.4'ü kullandığınızda her şey yoluna girecektir. Ancak uygulamanız uygulama mağazasından veya test uçuşundan indirildiğinde, bu kod parçası uygulamanızı kilitler. Bu tür bir hata tespit etmek veya hata ayıklamak çok zordur.
Oliver Zhang

524

Bu yazı burada alakalı https://www.swift-studies.com/blog/2014/6/10/enumerating-enums-in-swift

Esasen önerilen çözüm

enum ProductCategory : String {
     case Washers = "washers", Dryers = "dryers", Toasters = "toasters"

     static let allValues = [Washers, Dryers, Toasters]
}

for category in ProductCategory.allValues{
     //Do something
}

188
Güzel, ama ... numaralandırma elemanlarına iki kez girmelisin - bir kez numaralandırma için, bir kez allValues ​​için. Tam istediği kadar zarif değil.
Jay Imerman

2
"Ama" ile aynı fikirde ... ancak makalede belirtildiği gibi belki bir enum gerçekten bir dizi ve bu nedenle sırasız ... bir sorun var ... zihin ... tanımlanan davaları kötü bir başlangıç ​​olmaz!
rougeExciter

3
Java'da derleyici bunu sizin için yapar, belki Swift 2.0 da bunu yapar. Özellikle Java'da tüm numaralandırmalar, String'i vaka adları (Yıkayıcılar, ...) olarak veren ve bir dizi vaka otomatik olarak oluşturulan bir açıklama (Java'da toString) yöntemi alır. Java size konumsal indeksleme de sağlar. Dediğim gibi belki Swift 2.0.
Howard Lovatt

1
İdeal olarak, yapabileceğiniz Enum.Values(typeof(FooEnum))ancak bir genişletme yöntemi olarak (harita veya azaltma gibi) ortaya çıkabilen c # uygulamasına benzer bir şeyiniz olacaktır . FooEnum.values() :: values(EnumType -> [EnumType])
rodrigoelp

1
Makale sonunda her bir enum değeri bir birim sınaması ile allValues ​​dizisinde yapmak hakkında iyi bir noktaya değiniyor. Bununla birlikte, hala daha fazla öğe ekleyen birini görebiliyorum, ancak onları başlangıçta hala geri bırakan birim testine zorlamadım, tüm enum değerlerinin allValues'da tutulduğundan emin olmadan.
DonnaLea

278

Ben iterateEnum()rasgele enumtürleri için vakaları yineleme için bir yarar işlevi yaptım .

İşte örnek kullanım:

enum Suit: String {
    case Spades = "♠"
    case Hearts = "♥"
    case Diamonds = "♦"
    case Clubs = "♣"
}

for f in iterateEnum(Suit) {
    println(f.rawValue)
}

Hangi çıktılar:

♠
♥
♦
♣

Ancak, bu yalnızca hata ayıklama veya test amaçlıdır: Bu, belgelenmemiş birkaç Swift1.1 derleyici davranışına dayanmaktadır, bu nedenle bunu kendi sorumluluğunuzda kullanın.

İşte kod:

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
    }
}

Temel fikir:

  • İlişkili türlerle birlikte olanlar enumhariç olmak üzere , bellek temsili enum, yalnızca vakaların sayısı olduğu zaman , ne zaman , ne zaman , vb. İle 2...256aynıdır . Yani, olabilirUInt8257...65536UInt16unsafeBitcast karşılık gelen imzasız tamsayı türlerinden olabilir.
  • .hashValue enum değerlerinin değeri davanın diziniyle aynıdır.
  • .hashValuegeçersiz dizinden bitumum enum değerlerinin değeri 0.

Swift2 için revize edildi ve @ Kametrixom'un cevabından döküm fikirleri uyguladı :

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 için revize edildi:

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 için revize edildi:

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
    }
}

20
Müthiş ve soruyu cevaplayan tek cevap! Ama evet ... dokunmayacaksın! Çaba için +1 olsa da!
dotToString

Ben sadece temelde aynı şekilde çalışan cevabımı gönderdim (sadece daha sonra bu cevabı gördüm). Swift 2.0 beta 6'yı ve dilin modern özelliklerini kullanır.
Kametrixom

2
Swift 3 sürümü iyi çalışıyor. Kullanımı biraz değiştirmek gerekiyor: f için iterateEnum (Suit.self) {print (f.rawValue)}
Gene De Lisa

16
+1 Bu oldukça parlak. Ayrıca, IMHO, kullanmak için çok zekidir, her büyük Swift sürüm değişikliğinde önemli ölçüde kırıldığı kanıtlanmıştır. Yazarın kredisine göre Swift 3 sürümü, Swift 3 beta sürümünden bir ay önce yapıldı ... Eğer bu yanıtı alıp tüm bunları withUnsafePointer withMemoryReboundve pointeeşeyleri öğrenecekseniz , bunu elbette kullanın. Aksi halde bundan kaçınırdım.
Dan Rosenstark

5
Ben sadece bu hızlı 4, ama sadece linux kırık olduğunu eklemek istiyorum, bu yüzden +1 kullanmak için çok akıllı yukarıdaki yorumlara.
Andrew Plummer

130

Diğer çözümler işe yarıyor ancak hepsi olası sıra ve takımların sayısı veya ilk ve son sıralamanın ne olabileceği gibi varsayımlar yapıyorlar. Doğru, bir kart destesinin düzeni muhtemelen öngörülebilir gelecekte çok fazla değişmeyecek. Bununla birlikte, genel olarak, mümkün olduğunca az varsayım yapan kod yazmak daha temizdir. Çözümüm:

SuitNumaralamaya ham bir tür ekledim , böylece vakalara Suit(rawValue:)erişmek için kullanabilirim 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)
        }
    }
}

Kart createDeck()yönteminin uygulanmasının altında . init(rawValue:)failable başlatıcıdır ve isteğe bağlı olarak döner. Her iki while ifadesindeki değerinin paketini açıp kontrol ederek, sayı Rankveya Suitvaka sayısını varsaymanıza gerek yoktur :

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
    }
}

createDeckYöntemi nasıl çağıracağınız aşağıda açıklanmıştır :

let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()

12
Mutlak EN İYİ cevap Bu konuda çeşitli konularda gördüm. Çok zarif. Bu Int türü numaralandırmalar ile çalışır, ancak nasıl bir diğer türleri (dize, özel türleri, vb.) Yineleme olabilir merak ediyorum.
Jay Imerman

3
Bu kesinlikle en iyi çözümdür. Dikkat edilmesi gereken bir şey var. Kitaptaki örnekte sdduursma'da olduğu gibi "case Spades = 1" yoktur. İlk başta bunu yakalamadım. bu bir seçenek, ya da sadece "var m = 0" kullanabilirsiniz
Joshua Goossen

10
Bu ham değerlerin ardışık olduğunu varsayar. Bu doğru olmadığında, örneğin, numaralandırma bit maskesi bayraklarını temsil ettiğinde, döngü erken çıkar.
Jim

1
Bu çözüm, tanımını değiştirebileceğinizi varsayar Suit. Bu örnekte yapabilirsiniz, ancak alıştırma, size enumsdış kaynaktan gelmiş gibi , sizinle çalışmanızı sağlamak anlamına geliyordu .
Josh J

1
Şikayet etmek istediğim tek şey, bunu statik bir yöntem olarak adlandıramam, ancak önce bir kart nesnesi oluşturmak zorunda olduğum.
Çar

76

Bitlerde ve baytlarda tökezledim ve daha sonra @rintaro'nun cevabına çok benzer işler bulduğum bir uzantı oluşturdum . Bu şekilde kullanılır:

enum E : EnumCollection {
    case A, B, C
}

Array(E.cases())    // [A, B, C]

Dikkat çeken şey, ilişkili değerleri olmayan herhangi bir numarada kullanılabilir olmasıdır. Bunun, durumu olmayan numaralandırmalarda işe yaramadığını unutmayın.

@Rintaro'nun cevabında olduğu gibi , bu kod bir numaralandırmanın temel temsilini kullanır. Bu temsil belgelenmemiştir ve gelecekte değişebilir ve bu da onu bozar. Bunun üretimde kullanılmasını önermiyorum.

Kod (Swift 2.2, Xcode 7.3.1, Xcode 10 üzerinde çalışmıyor):

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
            }
        }
    }
}

Kod (Swift 3, Xcode 8.1, Xcode 10 üzerinde çalışmıyor):

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
            }
        }
    }
}

Neden ihtiyacım olduğu hakkında hiçbir fikrim yok typealias, ancak derleyici onsuz şikayet ediyor.


10
Bu cevap, özellikle de döküm bölümünde benim
cevabımdan

Ama bu sadece küçük endian ortamında işe yarıyor mu?
rintaro

5
Xcode 8 beta 6 bunu tekrar değiştirdi! Aşağıdaki hatayı alıyorum 'init' kullanılamıyor: belleği geçici olarak başka bir düzen uyumlu tip olarak görüntülemek için 'withMemoryRebound (to: kapasite: _)' kullanın. `
Confused

1
Değiştirin: @ConfusedVorlon: @Rintaro yukarıdaki cevaba bakınız withUnsafePointer... pointee}tarafındanwithUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } }
Stefan

1
Bu artık Xcode 10. itibariyle çalışıyor görünüyor ama Xcode 10'da (0,1) Swift 4 bu kodu kullanırken artık (ham değer eşitsiz olduğu) çalışıyor (Bu Swift 4.2 ile gerekli olmaz biliyorum)
benrudhart

26

ForwardIndexTypeProtokolü uygulayarak bir numaralandırma yoluyla yineleme yapabilirsiniz .

ForwardIndexTypeProtokol tanımlamak gerektirir successor()elemanları aracılığıyla aşamasından fonksiyonu.

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)!
}

Açık veya kapalı bir aralık ( ..<veya ...) üzerinde yineleme yapmak , dahili olarak successor()bunu yazmanıza izin veren işlevi çağırır :

// 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
}

2
Ben bir aralık içinde kullanıldığında sözdizimi verilen sözdizimi (sözdizimi ne ben numaralandırılabilen sans çözümlerinin bulunduğu numaralandırmalar yapmayı beklerdi). Teşekkürler! Her ne kadar operatör aşırı yüklemesi, halef () (cazip gibi) dışında başka bir yerde kullanılıyorsa, zorla açmanın tehlikeli olduğu açıktır. Ayrıca, infix gereksiz görünüyor ...?
iOS Gamer

En son Swift dil özelliklerini yansıtmak için güncellenen cevap
RndmTsk

Düzgün tanımlanmış bir successor()yöntem (ilk seçenek), enumilişkili bir türe sahip olma ihtiyacını ortadan kaldırır . +1
nhgrif

1
Ama bu zarif cevap String enums için işe yaramaz mı?
Ali

En "uygun" / en iyi uygulama çözümü! + 1-ed
Siu Ching Pong -Asuka Kenji-

18

Bu sorun artık çok daha kolay. İşte benim Swift 4.2 Çözümü:

enum Suit: Int, CaseIterable {
  case None
  case Spade, Heart, Diamond, Club

  static let allNonNullCases = Suit.allCases[Spade.rawValue...]
}

enum Rank: Int, CaseIterable {
  case Joker
  case Two, Three, Four, Five, Six, Seven, Eight
  case Nine, Ten, Jack, Queen, King, Ace

  static let allNonNullCases = Rank.allCases[Two.rawValue...]
}

func makeDeck(withJoker: Bool = false) -> [Card] {
  var deck = [Card]()
  for suit in Suit.allNonNullCases {
    for rank in Rank.allNonNullCases {
      deck.append(Card(suit: suit, rank: rank))
    }
  }
  if withJoker {
    deck.append(Card(suit: .None, rank: .Joker))
  }
  return deck
}

Ön 4.2:

" Swift'te Anlayış Listesini " bulduktan sonra bir araya getirdiğim bu çözümü beğendim .

Dizeler yerine Int satırları kullanır, ancak iki kez yazmaktan kaçınır, aralıkları özelleştirmeye izin verir ve ham değerleri sabit kodlamaz.

Bu, orijinal çözümümün Swift 4 sürümüdür, ancak yukarıdaki 4.2 iyileştirmeye bakın:

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: Bool = false) -> [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
}

Oh, şimdi görüyorum ki benimki Sutean Rutjanalard'ınkiyle aynı.
adazacom

1
Aslında, uygulamanızı daha çok beğendim. Bence daha net! 1 oy ver. Aslında en çok oylanan cevaplar çok zekidir ve gelecekte kesinlikle kırılacaktır. Sevgiler, gelecekte bir miktar istikrar vaat ediyor.
jvarela

17

Prensipte, enum vakaları için ham değerler ataması kullanmadığınızı varsayarak bu şekilde yapmak mümkündür:

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())")
}

7
Bu güzel ama sadece 0'dan başlayan sürekli Tamsayı numaralandırmalarında çalışır
Robert

@Robert, benim yorumum yukarıda belirtildiği gibi: "numaralandırma vakaları için ham değerler ataması
kullanmıyorsunuz

Evet - temel değerleri int olarak ayarlamanın yanı sıra ham değerleri kullanmayın. Hızlı bir şekilde takım elbise örneğinde olduğu gibi bir numaralandırma için bir türe ihtiyacınız yoktur. enum ItWontWorkForThisEnum {case a, b, c}
Robert

Sayım davasıyla bir demet ilişkilendirilirse bu sorunu nasıl giderir?
rougeExciter

Bir tupu bir enum ile kolayca ilişkilendiremezsiniz.
nhgrif

13

Enum'a ham bir Int değeri verirseniz, döngü çok daha kolay olacaktır.

Örneğin, anyGeneratordeğerleriniz genelinde numaralandırılabilen bir jeneratör elde etmek için kullanabilirsiniz :

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())

Ancak, bu oldukça yaygın bir örüntüye benziyor, sadece bir protokole uyarak herhangi bir enum tipini numaralandırırsak hoş olmaz mıydı? Swift 2.0 ve protokol uzantıları ile şimdi yapabiliriz!

Bunu projenize eklemeniz yeterlidir:

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 }
}

Artık bir enum oluşturduğunuzda (Int ham değeri olduğu sürece), protokole uyarak numaralandırılabilir hale getirebilirsiniz:

enum Rank: Int, EnumerableEnum {
    case Ace, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King
}
// ...
for rank in Rank.enumerate() { ... }

Numaralandırma değerleriniz 0(varsayılan) ile başlamazsa , firstRawValueyöntemi geçersiz kılın :

enum DeckColor: Int, EnumerableEnum {
    case Red = 10, Blue, Black
    static func firstRawValue() -> Int { return Red.rawValue }
}
// ...
let colors = Array(DeckColor.enumerate())

Daha standart CustomStringConvertible protokolüsimpleDescription ile değiştirmeyi de içeren son Suit sınıfı aşağıdaki gibi görünecektir:

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 sözdizimi:

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
    }
}

nextIndex ++ hızlı bir şekilde 3'te kaldırılacak - Ne değiştirmelisiniz - var nextIndex = firstRawValue () return anyGenerator {Self (rawValue: nextIndex ++)}
Avi

Anladım. defer {nextIndex + = 1} dönüş AnyGenerator {Self (rawValue: nextIndex)}
Avi

12

Güncellendi 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 formuna güncellenen kod @ Kametrixom'un cevabı

For Swift 3.0+ (çok teşekkürler @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
        }
    }
}

@silvansky ne demek istediğinizi açıklar mısınız?
ale_stro

Üzgünüz, tekrar kontrol ettim ve bir oyun alanı hatası vardı: gerçek projede bu kod beklendiği gibi çalışıyor, teşekkürler! =)
silvansky

1
Harika bir çözüm! Ayrıca, minimum değişikliklerle Swift 3'te iyi çalışır ('AnyGenerator', 'AnyIterator' olarak yeniden adlandırıldı ve '.memory', '.pointee' olarak yeniden adlandırıldı).
Philip

9

Swift 5 Çözümü:

enum Suit: String, CaseIterable {
    case spades = "♠"
    case hearts = "♥"
    case diamonds = "♦"
    case clubs = "♣"
}

// access cases like this:

for suitKey in Suit.allCases {
    print(suitKey)
}

7

Kendimi .allValueskodum boyunca çok şey yaparken buldum . Sonunda sadece bir Iteratableprotokole uymak ve bir rawValues()yöntem var bir yol buldum .

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"]

7

Swift 4.2 ile birlikte Xcode 10

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 }
}

Bunu aramak

print(Filter.allValues)

Baskılar:

["Maaş", "Deneyim", "Teknoloji", "Kullanılmayan", "Kullanılmayan Yüksek Değer"]


eski versiyonlar

için enumtemsilInt

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 }
}

Buna şöyle deyin:

print(Filter.allValues)

Baskılar:

[0, 1, 2, 3, 4]


için enumtemsilString

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"
        }
    }
}

Bunu aramak

print(Filter.allValues)

Baskılar:

["Maaş", "Deneyim", "Teknoloji", "Kullanılmayan", "Kullanılmayan Yüksek Değer"]


7

DÜZENLEME: Swift Evrim Önerisi SE-0194 Türetilmiş Enum Vakalarının Toplanması bu probleme düz başlı bir çözüm önermektedir . Swift 4.2 ve daha yeni sürümlerde görüyoruz. Teklif ayrıca bazı geçici çözümlere de dikkat çekiyor burada daha önce bahsedilenlere benzer ancak yine de görmek ilginç olabilir.

Orijinal görevimi tam olarak hatırlayacağım.


Bu, @ Peymmankh'ın Swift 3'e uyarlanmış cevabına dayanan başka bir yaklaşımdır .

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)
}

5
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
    }
}

Buna ne dersin?


Teşekkürler, ihtiyacım olduğu gibi çalışıyor. Ancak, haritayı kapatmak için değeri dizine göre değil ada göre alma fırsatı var mı?
Mikhail

4

Böyle numaralandırmayı deneyebilirsiniz

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]

1
Bu çok işe yaramaz kod! Bu eşdeğerdir static var enumerate = [Mercury, Venus, Earth, Mars]olarak en kıyasla subpar cevap verme, cevap stackoverflow.com/a/24137319/1033581
Coeur

@ C thisur, bu cevabı derleyiciyi bir vakayı kaçırmayacağınızı garanti etmek için kullanmanın önemli bir yararına sahiptir.
dchakarov

@ Cœur kullanıcı hatası yapmanıza izin verme sorunu aynıdır, yani derleyici yazmak return [Mercury, Venus, Mars]yerine şikayet etmeyecektirreturn [Mercury, Venus, Earth, Mars]
dchakarov

@dchakarov Açıklık için iyileştirmeyi bir cevap olarak göndermeye karar verdim: stackoverflow.com/a/50409525/1033581
Cœur

@ Cœur Yeni cevabınızda dönüş ifadesini bununla değiştirirseniz, return [.spades, .hearts, .clubs]derleyici bir şey söylemez ve daha sonra kodda kullanmaya çalıştığınızda, [TestApp.Suit.spades, TestApp.Suit.hearts, TestApp.Suit.clubs]- bu benim amacımdı - büyük bir şeyle uğraşıyorsanız enum ve zaman zaman vakaları eklemeniz veya kaldırmanız gerekiyorsa, çözümünüz özlü olmamakla birlikte daha güvenliyken, çözümünüz ihmal hatalarına eğilimlidir.
dchakarov

4

Swift 3'te, temeldeki numaralandırma olduğunda rawValue, Strideableprotokolü uygulayabilirsiniz . Avantajları, diğer bazı önerilerde olduğu gibi hiçbir değer dizisi oluşturulmaması ve standart Swift "for" döngüsünün çalışmasıdır, bu da hoş bir sözdizimi oluşturur.

// "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)")
}

Ahh, ForwardIndexType'ın yerini almak için aradığım tek şey. Şimdi yinelemelerim kullanım alanında iyi görünüyor ... sadece uygun Swifty yolu.
Andrew Duncan

4

Bu çözüm, okunabilirlik ve sürdürülebilirlik arasında doğru dengeyi sağlar.

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()

Bence bu en iyi çözüm. Hızlı kod gördüğümde çoğunlukla okunabilirlik objc'den daha iyi değildir. Ancak, programcılar kodlarının okuyucularına daha fazla dikkat ettiyse olabilir . Gelecekteki benlikleri, örneğin :)
Vilém Kurz

4

Maalesef cevabım, bu yazıyı ne yapmam gerektiğinde nasıl kullandığımla ilgili. Bu soruya rastlayanlar , bir numaralandırma içinde bir dava bulmanın bir yolunu arayanlar için , bunu yapmanın yolu (Swift 2'de yeni):

Düzenleme: küçük harfli camelCase artık Swift 3 numaralandırma değerleri için standart

// 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)
        }
    }

Bir numaralandırmada numaralandırmayı merak edenler için, bu sayfada verilen ve tüm numaralandırma değerlerinden oluşan bir dizi içeren statik bir var / let içeren yanıtlar doğrudur. TVOS için en son Apple örnek kodu, aynı tekniği içerir.

Bununla birlikte, dile daha uygun bir mekanizma inşa etmeleri gerekir (Apple, dinliyor musunuz?)!


3

Deney şuydu: DENEY

Kart'a, her bir sıralama ve takım kombinasyonundan bir kart ile tam bir kart destesi oluşturan bir yöntem ekleyin.

Bu nedenle, yöntemi eklemenin dışında verilen kodu değiştirmeden veya geliştirmeden (ve henüz öğretilmemiş materyalleri kullanmadan), bu çözümü buldum:

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()

3

İşte bir yineleme enumve bir birden çok değer türleri sağlamak için kullandığım bir yöntemenum

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"))")

Bu çıktı:

Fransızcadaki sıfır sayısı: sıfır, ispanyolca: cero, japonca: nuru
Fransızcada bir numara: un, İspanyolca: uno, japonca: ichi
Fransızcada iki numara: deux, İspanyolca: dos, japonca: ni
Fransızcada üç numara : trois, İspanyolca: tres, japon: san
Fransızcada dört numara: quatre, İspanyolca: cuatro, japonca: shi
Fransızcada beş numara: cinq, İspanyolca: cinco, japonca: git
Fransızcada 6 numara: altı, İspanyolca: seis, japonca: roku
Fransızca'daki yedi numara: Eylül, İspanyolca: Siete, Japonca: Shichi

Toplam 8 vaka

Japonca siete: shichi


GÜNCELLEME

Yakın zamanda numaralandırmayı ele almak için bir protokol oluşturdum. Protokol, bir Int ham değeri olan bir enum gerektirir:

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
    }
}

2

Bu bir hack gibi görünüyor, ancak ham değerleri kullanıyorsanız böyle bir şey yapabilirsiniz

enum Suit: Int {  
    case Spades = 0, Hearts, Diamonds, Clubs  
 ...  
}  

var suitIndex = 0  
while var suit = Suit.fromRaw(suitIndex++) {  
   ...  
}  

2

İle uğraşırken Swift 2.0Burada benim önerim:

Ham türü ekledim Suit enum

enum Suit: Int {

sonra:

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
    }
}

2

@Kametrixom cevap olduğu gibi burada böyle vb sayımı gibi Dizisinin hediyeler tümüne erişebilir beri, bir dizi AnySequence dönen daha iyi olurdu dönen inanıyorum

İşte yeniden yazma:

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)
    }
}

2

Başka bir çözüm:

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)
}

2

Bu Swift 2.0'dan oldukça eski bir yazı. Artık burada hızlı 3.0'ın daha yeni özelliklerini kullanan bazı daha iyi çözümler var: Swift 3.0'da bir Enum aracılığıyla yineleme

Ve bu soruda, Swift'in yeni bir özelliğini kullanan bir çözüm var (bu düzenlemeyi yazarken henüz yayınlanmadı) Swift 4.2: Swift numarasının sayısını nasıl alabilirim?


Bu iş parçacığında çok sayıda iyi çözüm var ve diğerleri de çok karmaşık. Mümkün olduğunca basitleştirmeyi seviyorum. İşte farklı ihtiyaçlar için işe yarayıp yaramayabilecek bir çözüm ama çoğu durumda işe yaradığını düşünüyorum:

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
    }
}

Yinelemek için:

for item in Number.allValues {
    print("number is: \(item)")
}

1
Bu, oluşturduğunuz tek tek numaraya özgü çok fazla iş gibi geliyor - bu durumda [Number.One.rawValue, Number.Two.rawValue, ...] dönüşünün daha temiz olmadığından emin değilim .
Scott Austin

Bu Swift 2.0'dan oldukça eski bir yazı. Şimdi burada, hızlı 3.0'ın daha yeni özelliklerini kullanan bazı daha iyi çözümler var: stackoverflow.com/questions/41352594/… Ve bu soruda (henüz bu düzenlemeyi yazarken henüz yayınlanmamış olan) ) Swift 4.2: stackoverflow.com/questions/27094878/…
Abbey Jackson

2

Numaralandırmalar toRaw()ve fromRaw()yöntemler. Ham değeriniz bir ise Int, ilkinden sonuncuya kadar yineleyebilirsiniz enum:

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()
    }
}

Bir sorun, simpleDescriptionyöntemi çalıştırmadan önce isteğe bağlı değerleri test etmeniz gerektiğidir , bu yüzden önce convertedSuitdeğerimize ayarladık ve sonraconvertedSuit.simpleDescription()


2
Orijinal soru Int
cynistersix

2

İşte benim önerilen yaklaşımım. Tamamen tatmin edici değil (Swift ve OOP için çok yeniyim!) Ama belki birisi onu geliştirebilir. Fikir, her bir enumun kendi menzil bilgilerini .firstve .lastözelliklerini sağlamasını sağlamaktır . Her numaraya yalnızca iki satır kod ekler: yine de biraz sabit kodlanmış, ancak en azından tüm seti çoğaltmıyor. Numaralandırılmamış yerine Suit, Rankenum gibi Int olarak değiştirilmesini gerektirir .

Tüm çözümü yankılamak yerine, burada .durum ifadelerinden sonra bir yere numaralandırmaya eklediğim kod ( Suitnumaralandırma benzerdir):

var first: Int { return Ace.toRaw() }
var last: Int { return King.toRaw() }

ve ben bir String dizisi olarak güverte oluşturmak için kullanılan döngü. (Sorun tanımı, destenin nasıl yapılandırılacağını belirtmedi.)

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
}

Bu tatmin edici değildir, çünkü özellikler enumdan ziyade bir elemanla ilişkilidir. Ancak 'for' döngülerine netlik katar. Bunun Rank.firstyerine söylemesini istiyorum Rank.Ace.first. (Herhangi bir elemanla) çalışır, ancak çirkin. Birisi bunu enum seviyesine nasıl yükselteceğini gösterebilir mi?

Ve çalışmasını sağlamak için createDeckyöntemi Kart yapısından kaldırdım . Bu yapıdan dönen bir [String] dizisi almak nasıl anlayamadım ve yine de böyle bir yöntem koymak için kötü bir yer gibi görünüyor.


2

Ben tüm değerleri dizi (bu yazı http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ sayesinde) döndüren computed özelliği kullanarak yaptım . Ancak, aynı zamanda int raw değerleri kullanır, ancak numaralandırma tüm üyeleri ayrı bir özellikte tekrarlamak gerekmez.

UPDATE Xcode 6.1 kullanarak numaralandırma üye almak için bir şekilde biraz değişti rawValue, bu yüzden liste sabit. Ayrıca önce yanlış küçük hata düzeltildi rawValue.

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++)
                }
            }
        )
    }
}

1
enum Rank: Int
{
    case Ace = 0
    case Two, Three, Four, Five, Six, Seve, Eight, Nine, Ten
    case Jack, Queen, King
    case Count
}

enum Suit : Int
{
    case Spades = 0
    case Hearts, Diamonds, Clubs
    case Count
}

struct Card
{
    var rank:Rank
    var suit:Suit
}

class Test
{
    func makeDeck() -> Card[]
    {
        let suitsCount:Int = Suit.Count.toRaw()
        let rankCount:Int = Rank.Count.toRaw()
        let repeatedCard:Card = Card(rank:Rank.Ace, suit:Suit.Spades)
        let deck:Card[] = Card[](count:suitsCount*rankCount, repeatedValue:repeatedCard)

        for i:Int in 0..rankCount
        {
            for j:Int in 0..suitsCount
            {
                deck[i*suitsCount+j] = Card(rank: Rank.fromRaw(i)!, suit: Suit.fromRaw(j)!)
            }
        }
        return deck
    }
}

Rick cevabına göre: bu 5 kat daha hızlı


CountVaka eklemek, kapsamlı switchuygulamaları ve CaseIterableuyumu bozacaktır.
Cœur
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.