development

문자열 유형으로 열거 형을 열거하는 방법은 무엇입니까?

big-blog 2020. 2. 12. 23:36
반응형

문자열 유형으로 열거 형을 열거하는 방법은 무엇입니까?


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

예를 들어, 다음과 같은 방법으로 어떻게 할 수 있습니까?

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

결과 예 :

♠
♥
♦
♣

스위프트 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의 관련 종류와 함께 -의 경우 단지 인덱스, 사건의 카운트가있을 때입니다 2...256, 그것은 동일의 UInt8경우, 257...65536그것의, UInt16등등. 따라서 unsafeBitcast해당 부호없는 정수 유형이 될 수 있습니다 .
  • .hashValue enum 값은 케이스의 인덱스와 동일합니다.
  • .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 열거 형에 원시 유형을 추가 했으므로 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)
        }
    }
}

Card의 createDeck () 메소드 구현 아래 init (rawValue :)는 실패한 이니셜 라이저이며 선택적을 반환합니다. unwrapping하고 두 while 문에서 값을 확인하면 순위 또는 소송 사례 수를 가정 할 필요가 없습니다.

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]

주목할만한 것은 모든 열거 형에서 (관련된 값없이) 사용할 수 있다는 것입니다. 사례가없는 열거 형에는 작동하지 않습니다.

기권

@rintaro 의 답변 마찬가지로이 코드는 열거 형의 기본 표현을 사용합니다. 이 표현은 문서화되어 있지 않으며 향후 변경 될 수 있으며 이로 인해 문제가 발생할 수 있습니다.-> 프로덕션 환경에서 사용하지 않는 것이 좋습니다.

코드 (Swift 2.2, Xcode 7.3.1, 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
            }
        }
    }
}

코드 (Swift 3, Xcode 8.1, 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
            }
        }
    }
}

(왜 필요한지 typealias모르겠지만 컴파일러는 그것없이 불평합니다)

(이 답변을 크게 수정했습니다. 이전 버전의 편집 내용을보십시오)


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
}

원칙적으로 열거 형의 경우 원시 값 할당을 사용하지 않는다고 가정하면이 방법으로 수행 할 수 있습니다.

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

이 문제는 훨씬 쉬워졌습니다. 여기 내 Swift 4.2 솔루션이 있습니다.

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
}

4.2 이전

나는이 페이지를 찾은 후에 함께 만든이 솔루션을 좋아합니다 : Swift의 List comprehension

문자열 대신 Int raws를 사용하지만 두 번 입력하지 않고 범위를 사용자 정의 할 수 있으며 원시 값을 하드 코딩하지 않습니다.

이것은 내 원래 솔루션의 Swift 4 버전이지만 위의 4.2 개선 사항을 참조하십시오.

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
}

열거 형 에 원시 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 raw 값이있는 한) 프로토콜을 준수하여 열거 형을 만들 수 있습니다.

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

열거 형 값이 0(기본값)으로 시작하지 않으면 firstRawValue메서드를 재정의하십시오 .

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

표준 CustomStringConvertible 프로토콜로 교체 simpleDescription하는 것을 포함하여 최종 Suit 클래스 는 다음과 같습니다.

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

로 업데이트 스위프트 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
        }
    }
}

그것은 스위프트 2.2 폼에 코드를 업데이트됩니다 Kametrixom이입니다 @ swer

대한 스위프트 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
        }
    }
}

.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 제안 SE-0194 Enum Cases 파생 컬렉션은 이 문제에 대한 수준의 해결책제안합니다 . 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 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
    }
}

이건 어때?


스위프트 4.2 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 }
}

불러라

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)

인쇄물:

[ "급여", "경험", "기술", "사용되지 않음", "사용되지 않은 높은 가치"]


죄송합니다. 제 답변은 내가해야 할 일에서이 게시물을 사용한 방법에 대한 것입니다. 이 질문을 우연히 발견 하고 열거 형 내에서 사례 찾는 방법을 찾고있는 사람들에게는이 방법이 있습니다 (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을 포함하는 대답이 정확합니다. tvOS 용 최신 Apple 예제 코드에는 이와 동일한 기술이 포함되어 있습니다.

즉, 그들은 언어에보다 편리한 메커니즘을 구축해야합니다 (Apple, 듣고 있습니까?)!


Swift 3에서 기본 열거 형에 {rawValue}가 있으면 {Strideable} 프로토콜을 구현할 수 있습니다. 장점은 다른 제안과 같이 값 배열이 생성되지 않으며 표준 Swift "for 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)")
}

이처럼 열거하려고 할 수 있습니다.

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]

이것이 내가 끝낸 것입니다. 나는 그것이 가독성과 유지 관리 성의 적절한 균형을 맞추고 있다고 생각합니다.

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

실험은 : EXPERIMENT

순위와 수트의 각 조합으로 한 장의 카드로 카드 한 벌을 만드는 방법을 카드에 추가하십시오.

따라서 메소드를 추가하는 것 이외의 주어진 코드를 수정하거나 향상시키지 않고 (그리고 아직 가르쳐지지 않은 것들을 사용하지 않고)이 솔루션을 생각해 냈습니다.

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

열거 형에는 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 메소드를 실행하기 전에 선택적 값을 테스트해야하므로 변환 된 값을 먼저 값으로 설정 한 다음 상수를 변환 된 값으로 설정 한 경우


이것은 해킹처럼 보이지만 원시 값을 사용하면 다음과 같이 할 수 있습니다

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

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

여기에 내가 제안한 접근법이 있습니다. 완전히 만족 스럽지는 않지만 (Swift와 OOP에 익숙하지 않습니다!) 어쩌면 누군가가 그것을 다듬을 수 있습니다. 아이디어는 각 열거 형이 .first 및 .last 속성으로 자체 범위 정보를 제공하도록하는 것입니다. 각 열거 형에 두 줄의 코드를 추가합니다. 여전히 약간 하드 코딩되어 있지만 적어도 전체 세트를 복제하지는 않습니다. 유형이 아닌 대신 등급 enum과 같은 Int로 Suit enum을 수정해야합니다.

전체 솔루션을 에코하는 대신 다음은 case 문 뒤의 Rank enum에 추가 한 코드입니다 (Suit enum은 유사합니다).

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.Ace.first 대신 Rank.first라고 말하고 싶습니다. 모든 요소와 함께 작동하지만 추악합니다. 누군가 열거 형 수준으로 올리는 방법을 보여줄 수 있습니까?

그리고 그것을 작동시키기 위해, 카드 구조체에서 createDeck 메소드를 들어 올렸습니다 ... 그 구조체에서 [String] 배열을 반환하는 방법을 알 수 없었습니다. 어쨌든 그러한 메서드를 넣기가 나쁜 곳 인 것 같습니다.


나는 계산 된 속성을 사용하여 모든 값의 배열을 반환합니다 (이 게시물 http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ 덕분에 ). 그러나 int 원시 값도 사용하지만 열거 형의 모든 멤버를 별도의 속성으로 반복 할 필요는 없습니다.

업데이트 Xcode 6.1은 원시 값을 사용하여 열거 형 멤버를 얻는 방법이 약간 변경되었으므로 목록을 수정했습니다. 첫 번째 원시 값이 잘못되어 작은 오류가 수정되었습니다.

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여기를 다루는 동안 내 제안은 다음 같습니다.

나는 원시 유형을 추가했습니다 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
    }
}

@Kametrixom 응답과 마찬가지로 여기 당신이 등 수, 같은 배열의 케이크 모두에 액세스 할 수 있기 때문에 나는, 배열 AnySequence을 반환하는 것보다 더 나은 것 반환 믿는다

재 작성은 다음과 같습니다.

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

이것은 Swift 2.0의 꽤 오래된 게시물입니다. swift 3.0의 새로운 기능을 사용하는 더 나은 솔루션이 여기 있습니다 : Swift 3.0에서 Enum 반복하기

그리고이 질문에는 (이 편집을 작성할 때 아직 출시되지 않은) 새로운 기능을 사용하는 솔루션이 있습니다 .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)")
}

여기에 열거 형을 반복하고 하나의 열거 형에서 여러 값 유형을 제공하는 데 사용하는 방법

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

이것은 출력입니다.

프랑스어로 된 숫자 : zéro, 스페인어 : cero, 일본어 : nuru
프랑스어로 된 숫자 : un, 스페인어 : uno, 일본어 : ichi
프랑스어에서 숫자 2 : deux, 스페인어 : dos, japanese : ni
숫자 프랑스어에서 3 : trois, 스페인어 : tres, japanese : san
프랑스어 4 번 : quatre, 스페인어 : cuatro, 일본어 : shi
5 번 프랑스어 : cinq, 스페인어 : cinco, japanese : go
숫자 6 번 프랑스어 : 6, 스페인어 : seis, japanese : roku
The number Seven in french : sept, spanish : siete, japanese : 시치

총 8 건

siete in japanese : 시치


최신 정보

최근에 열거를 처리하는 프로토콜을 만들었습니다. 프로토콜에는 Int raw 값을 가진 열거 형이 필요합니다.

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

아래 방법을 사용했습니다. 가정은 Rank 열거의 마지막 값을 알고 있으며 Ace 다음에 모든 순위에 증분 값이 있다고 가정합니다.

깨끗하고 작고 이해하기 쉽기 때문에이 방법을 선호합니다.

 func cardDeck() -> Card[] {
    var cards: Card[] = []
    let minRank = Rank.Ace.toRaw()
    let maxRank = Rank.King.toRaw()

    for rank in minRank...maxRank {
        if var convertedRank: Rank = Rank.fromRaw(rank) {
            cards.append(Card(rank: convertedRank, suite: Suite.Clubs))
            cards.append(Card(rank: convertedRank, suite: Suite.Diamonds))
            cards.append(Card(rank: convertedRank, suite: Suite.Hearts))
            cards.append(Card(rank: convertedRank, suite: Suite.Spades))
        }
    }

    return cards
}

영리한 방법이 있으며 두 가지 종류의 열거 형의 차이점을 보여주기 때문에 좌절합니다.

이 시도:

    func makeDeck() -> Card[] {
      var deck: Card[] = []
      var suits: Suit[] = [.Hearts, .Diamonds, .Clubs, .Spades]
      for i in 1...13 {
        for suit in suits {
          deck += Card(rank: Rank.fromRaw(i)!, suit: suit)
        }
      }
      return deck
    }

숫자로 뒷받침되는 열거 형 (원시 값)은 암시 적으로 명시 적으로 정렬되는 반면, 숫자로 뒷받침되지 않는 열거 형은 명시 적으로 암시 적으로 정렬되지 않습니다.

예를 들어 열거 형 값에 숫자를 줄 때, 언어는 숫자가 어떤 순서인지 알아낼 정도로 교활합니다. 반면에 순서를 지정하지 않으면 언어가 던지는 값을 반복하려고 할 때 공중에 손을 들어 "예,하지만 어느 쪽을 먼저 가고 싶습니까 ???"

이 작업을 수행 할 수있는 다른 언어 (정렬되지 않은 열거 형을 반복하여)는 실제로 모든 것이 '후드'아래에있는 동일한 언어 일 수 있으며 논리적 순서가 있는지 여부에 관계없이지도의 키를 반복 할 수 있습니다.

따라서 트릭은 명시 적으로 주문 된 무언가를 제공하는 것입니다.이 경우 원하는 순서로 배열의 소송 인스턴스가 필요합니다. 당신이 그것을 말하자마자 Swift는 "왜 처음에 그렇게 말하지 않았습니까?"와 같습니다.

또 다른 속임수는 fromRaw 함수에서 강제 연산자를 사용하는 것입니다. 이것은 열거 할 수있는 또 다른 'gotcha'를 보여줍니다. 전달 가능한 값의 범위는 종종 열거의 범위보다 큽니다. 예를 들어 Rank.fromRaw (60)라고 말하면 값이 반환되지 않으므로 언어 선택적 기능을 사용하고 있으며 옵션을 사용하기 시작하면 곧 강제 적용됩니다. (또는 그래도 여전히 조금 이상해 보이는 if let 구성)

참고 URL : https://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type



반응형