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


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

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

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

결과 예 :



어떤 경우에 유형을 알 수 없습니까?
mtbennett

맞습니다.이 경우 문자열 유형입니다.
Lucien

스위프트에 아직 반영되지 않은 ...
Sulthan

22
그것들이 열거라고 불리는 것이 아이러니하지 않습니까? 그러나 그들은 스위프트에서 열거하기가 너무 성가신가요?
찰튼 프로 바 타스

3
@CharltonProvatas 만약 그것이 스위프트의 유일한 단점이라면, 나는 그것을 하루라고 부를 것입니다. 이 문제에 대해 다른 해결 방법 을 제공하는 사람들의 수를 살펴보면 키보드를 조금씩 아 먹고 있습니다.
qwerty_so

답변:


267

스위프트 4.2 이상

시작 스위프트 4.2 (엑스 코드 10), 단지에 프로토콜 적합성을 추가 CaseIterable혜택을 allCases. 이 프로토콜 적합성을 추가하려면 다음과 같이 작성하면됩니다.

extension Suit: CaseIterable {}

열거 형이 자신의 것이라면 선언에 직접 적합성을 지정할 수 있습니다.

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

그런 다음 다음 코드는 가능한 모든 값을 인쇄합니다.

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

이전 Swift 버전 (3.x 및 4.x)과의 호환성

Swift 3.x 또는 4.0을 지원해야하는 경우 다음 코드를 추가하여 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

@DmitryPetukhov 기꺼이 도와 드리겠습니다. 그러나 (1) 최신 버전의 코드를 받았습니까? (일부 충돌은 한 달 전에 수정되었습니다.) (2) 충돌을 재현 할 수있는 사용자 지정 유형의 MCVE와 Xcode 버전을 알려주십시오.
Cœur

디버그 빌드에는 적합하지만 릴리스를 생성하고 TestFlight에 업로드하면 충돌이 발생합니다. 애플이 어떻게 든 이것을 제거하고 있습니까?
Daniel Wood

1
내장 버전의 CaseIterator보다 긍정적 인 버전 인 것 같습니다. 다른 파일에 정의 된 열거 형을 버전으로 확장 할 수 있습니다. 확장에서 allCase를 공개하면 다른 프레임 워크에 정의 된 열거를 확장 할 수도 있습니다.
adamfowlerphoto

1
@CyberMew 나는 그것을 명확히하기 위해 대답을 업데이트했습니다. Swift 책과 Xcode 10 릴리스 노트에서 CaseIterable에 대한 언급은 내 대답보다 뒤떨어져 있으며 String현재 스택 오버플로 질문과 달리 열거 형이 백업되지 않은 간단한 예제를 사용했습니다 .
Cœur

1
"# if! swift (> = 4.2)"의 중요성을 강조하고 싶습니다. swift 4.2 이전에 코드를 작성하고 Xcode 버전 11.4를 사용하여 테스트 장치에 로컬로 빌드 할 때 "# if! swift (> = 4.2)"코드를 제거하는 것을 잊어 버린 경우 모든 것이 정상입니다. 그러나 앱을 앱 스토어 또는 테스트 비행에서 다운로드하면 해당 코드가 앱에 충돌을 일으 킵니다. 이런 종류의 버그는 발견하거나 디버그하기가 매우 어렵습니다.
올리버 장

524

이 게시물은 여기 관련이 있습니다 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
}

188
멋지지만 ... 열거에 요소를 두 번 입력해야합니다. 열거에 한 번, allValue에 한 번 입력해야합니다. 원하는만큼 우아하지는 않습니다.
Jay Imerman 2018 년

2
"그러나"에 동의하십시오. 그러나 기사에서 언급했듯이 열거 형은 실제로 설정되어 있기 때문에 순서가 정해지지 않은 문제가 있습니다.
rougeExciter

3
Java에서는 컴파일러 가이 작업을 수행하며 Swift 2.0 도이 작업을 수행합니다. 특히 Java에서 모든 열거 형은 문자열을 사례 이름 (Washers, ...)으로 제공하고 사례 세트가 자동으로 작성되는 설명 (Java의 toString) 메소드를 가져옵니다. Java는 또한 위치 인덱싱을 제공합니다. 내가 말했듯이 Swift 2.0 일 수 있습니다.
하워드 LOVATT

1
이상적으로는 Enum.Values(typeof(FooEnum))맵 또는 축소와 같은 확장 방법으로 노출 할 수는 있지만 C # 구현과 비슷한 것이 있습니다 . FooEnum.values() :: values(EnumType -> [EnumType])
rodrigoelp

1
이 기사는 각 열거 형 값이 단위 테스트로 allValues ​​배열에 있는지 확인하는 데 좋은 지적을합니다. 그러나 여전히 더 많은 요소를 추가하지만 단위 테스트에서 강제로 적용하지 않는 사람을 볼 수 있지만 모든 열거 형 값이 allValues에 있는지 확실하지 않은 채 처음부터 다시 우리를 떠날 수 있습니다.
DonnaLea

278

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

20
질문에 대답하는 대단하고 유일한 대답! 하지만 .. 손대지 마! 노력에 +1!
dotToString 2

방금 기본적으로 동일한 방식으로 작동하는 답변 을 게시했습니다 (나중에이 답변 만 보았습니다). Swift 2.0 베타 6과 언어의 현대적인 기능을 사용합니다.
Kametrixom

2
스위프트 3 버전이 잘 작동합니다. iterateEnum (Suit.self) {print (f.rawValue)}에서 f
Gene De Lisa

16
+1 이것은 매우 훌륭합니다. 또한 IMHO는 사용하기에 너무 영리합니다. 모든 주요 Swift 버전 변경에서 크게 깨짐으로 입증되었습니다. 스위프트 3 베타 밖으로 오기 전에 저자의 신용에, 스위프트 3 버전이 답을 가지고 모든 배우려고하는 경우에 ... 한 달에 이루어졌다 withUnsafePointer withMemoryRebound하고 pointee, 물건을 모든 수단으로 사용합니다. 그렇지 않으면 피할 것입니다.
Dan Rosenstark

5
나는 이것을 스위프트 4에서 깨뜨 렸지만 리눅스에서만 깨달았 기 때문에 위의 주석에 +1하면 너무 똑똑해 사용하기가 어렵다.
Andrew Plummer 1

130

다른 솔루션은 작동 하지만 모두 가능한 순위 및 수 또는 첫 번째 및 마지막 순위가 무엇인지를 가정합니다. 사실, 카드 데크의 레이아웃은 아마도 가까운 미래에 크게 변하지 않을 것입니다. 그러나 일반적으로 가능한 적은 가정을하는 코드를 작성하는 것이 더 좋습니다. 내 해결책 :

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

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

12
이 주제에 대한 다양한 스레드에서 본 최고의 BEST 답변. 매우 우아합니다. 이것은 Int 유형 열거와 함께 작동하지만 다른 유형 (문자열, 사용자 정의 유형 등)을 반복하는 방법이 궁금합니다.
Jay Imerman 2018 년

3
이것은 확실히 최고의 솔루션입니다. 한가지주의 할 점. 이 책의 예에서는 sdduursma와 같이 "case Spades = 1"이 없습니다. 처음에는 이것을 잡지 못했습니다. 즉, 하나 개의 옵션, 또는 당신은 "var에 m = 0"를 사용할 수 있습니다
여호수아 Goossen은

10
이는 원시 값이 순차적이라고 가정합니다. 이것이 사실이 아닌 경우, 예를 들어 열거 형이 비트 마스크 플래그를 나타내는 경우 루프가 너무 일찍 종료됩니다.
Jim

1
이 솔루션은의 정의를 수정할 수 있다고 가정합니다 Suit. 이 예에서는 가능하지만 운동은 enums마치 마치 외부에서 온 것처럼 자신 과 함께 일할 수 있도록하기위한 것입니다 .
Josh J

1
내가 불평하고 싶은 것은 정적 메소드로 호출 할 수 없지만 먼저 카드 객체를 만들어야한다는 것입니다.
qed

76

나는 비트와 바이트를 우연히 발견하고 나중에 @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모르겠지만 컴파일러는 컴파일러없이 불평합니다.


10
이 답변은 특히 일부 :) 캐스팅에, 내 대답보다 훨씬 낫다
린타로

그러나 이것이 작은 엔디안 환경에서만 작동한다고 생각합니까?
rintaro

5
Xcode 8 베타 6이 다시 변경되었습니다! 다음과 같은 오류 'init'를 사용할 수 없습니다. 'withMemoryRebound (to : capacity : _)'를 사용하여 메모리를 다른 레이아웃 호환 유형으로 임시로 봅니다. '
혼란스러운 Vorlon

1
대체 : @ConfusedVorlon는 : @Rintaro 이상 대답을 참조 withUnsafePointer... pointee}에 의해withUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } }
스테판

1
이것은 더 이상 Xcode 10에서 작동하지 않는 것 같습니다. (Swift 4.2에서는 필요하지 않다는 것을 알고 있습니다.) Xcode 10에서 Swift 4 (.1)를 사용할 때이 코드는 더 이상 작동하지 않습니다 (원시 값이 동일하지 않음)
benrudhart

26

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
}

2
나는 이것이 범위 내에서 사용될 때 결과 구문을 감안할 때 질문에 대한 가장 "적절한"응답, 심지어 가장 "우아한"(여기서는 다른 옵션에 필요한 추가 코드)조차도 발견합니다 (구문은 내가 무엇입니까 enumeratable sans workarounds 열거 형 경우 열거 할 수있을 것으로 예상됩니다). 감사! 연산자 오버로딩이 후임자 () 이외의 다른 곳에서 사용되는 경우 (유혹하는 것처럼 보이는 경우) 강제 래핑 해제가 위험하다는 점은 주목할 가치가 있습니다. 또한, 접두사는 불필요 해 보입니다 ...?
iOS 게이머

최신 Swift 언어 사양을 반영하도록 업데이트 된 답변
RndmTsk

올바르게 정의 된 successor()방법 (첫 번째 옵션) enum을 사용하면 연관된 유형을 가질 필요가 없습니다 . +1
nhgrif

1
그러나이 우아한 대답은 String 열거 형에서 작동하지 않습니다.
Ali

가장 "적절한"/ 모범 사례 솔루션! + 1-ed
Siu Ching Pong -Asuka Kenji-

18

이 문제는 훨씬 쉬워졌습니다. 다음은 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
}

오, 나는 내 것이 기본적으로 Sutean Rutjanalard와 동일하다는 것을 알았습니다.
adazacom December

1
사실, 나는 당신의 구현을 더 좋아했습니다. 더 명확하다고 생각합니다! 1 공감. 실제로 가장 많이 투표 된 답변은 너무 영리하며 앞으로 확실히 깨질 것입니다. 당신은 미래에 약간의 안정성을 약속합니다.
jvarela

17

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

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
이것은 좋지만 0에서 시작하는 연속 정수 열거에만 작동합니다.
Robert

@Robert, 내 의견은 위에서 언급했듯이 : "당신은 열거 형의 경우 원시 값 할당을 사용하지 않습니다"
Alfa07

예-기본 값을 int로 설정하고 원시 값을 사용하지 마십시오. 신속하게 소송 예제와 같이 열거 형에 대한 유형이 필요하지 않습니다. enum ItWontWorkForThisEnum {case a, b, c}
Robert

튜플이 열거 사례와 관련된 경우이 문제를 어떻게 해결합니까?
rouge 발매 자

튜플을 열거 형에 매우 쉽게 연결할 수 없습니다.
nhgrif

13

열거 형 에 원시 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)
}

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

nextIndex ++는 빠른 3에서 제거됩니다.-var nextIndex = firstRawValue () return anyGenerator {Self (rawValue : nextIndex ++)}
Avi

알아 냈습니다. 연기 {nextIndex + = 1} return AnyGenerator {Self (rawValue : nextIndex)}
Avi

12

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

@Kametrixom의 답변 으로 Swift 2.2 형식으로 코드가 업데이트되었습니다.

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

@silvansky 무슨 의미인지 설명해 주시겠습니까?
ale_stro 2016 년

죄송합니다. 재확인했는데 놀이터 버그가있었습니다. 실제 프로젝트에서이 코드는 예상대로 작동합니다. 감사합니다! =)
silvansky 2016 년

1
훌륭한 솔루션! 또한 최소한의 변경 ( 'AnyGenerator'의 이름은 'AnyIterator', '.memory'의 이름은 '.pointee')으로 Swift 3에서 잘 작동합니다.
Philip

9

스위프트 5 솔루션 :

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

// access cases like this:

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

7

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

7

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

인쇄물:

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


7

편집 : 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)
}

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

이건 어때요?


감사합니다. 필요에 따라 작동합니다. 그러나 map-closure에서 색인이 아닌 이름으로 값을 얻을 수있는 기회가 있습니까?
Mikhail

4

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

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
그것은 쓸모없는 코드입니다! 이는 static var enumerate = [Mercury, Venus, Earth, Mars]대부분의 투표 응답과 비교하여 하위 응답으로 만듭니다. stackoverflow.com/a/24137319/1033581
Cœur

@ Cœur이 답변은 당신이 사건을 놓치지 않도록 컴파일러를 사용하는 중요한 이점을 가지고 있습니다.
dchakarov

@ Cœur 사용자 오류를 허용하는 것과 같은 문제가 있습니다. 즉, return [Mercury, Venus, Mars]대신 쓰면 컴파일러가 불평하지 않습니다return [Mercury, Venus, Earth, Mars]
dchakarov

@ dchakarov 나는 명확성을 위해 개선 사항을 답변으로 게시하기로 결정했습니다. stackoverflow.com/a/50409525/1033581
Cœur

@ Cœur 새로운 답변에서 return 문을 this return [.spades, .hearts, .clubs]로 바꾸면 컴파일러가 아무 말도하지 않고 코드에서 사용하려고 할 때 [TestApp.Suit.spades, TestApp.Suit.hearts, TestApp.Suit.clubs]-내 요점-큰 것을 다루는 경우 열거 형을 사용하면 때때로 사례를 추가하거나 제거해야하므로 솔루션이 누락 오류가 발생하기 쉬운 반면 현재 답변은 간결하지는 않지만 안전합니다.
dchakarov

4

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

아, ForwardIndexType을 대체하려는 것입니다. 이제 내 반복은 사용법 사이트에서 잘 보입니다 ... 단순한 Swifty 방식입니다.
Andrew Duncan

4

이 솔루션은 가독성과 유지 관리 성의 적절한 균형을 유지합니다.

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

제 생각에는 이것이 최선의 해결책입니다. 빠른 코드를 볼 때 주로 가독성은 objc보다 낫지 않습니다. 그러나 프로그래머 가 코드 독자에게 더 많은 관심을 기울인다면 그럴 수 있습니다 . 그들의 미래 자아, 예를 들면 :)
Vilém Kurz

4

죄송합니다. 제 답변은 내가해야 할 일에서이 게시물을 어떻게 사용했는지에 대한 것입니다. 이 질문을 우연히 발견 하고 열거 형 내에서 사례 를 찾는 방법을 찾고있는 사람들에게는이 방법이 있습니다 (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, 듣고 있습니까?)!


3

실험은 : 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()

3

여기에 하나를 반복하고 enum여러 값 유형을 제공 하는 데 사용하는 방법이 있습니다.enum

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, japanese : 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
    }
}

2

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

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

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

2

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

2

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

2

다른 해결책 :

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

이것은 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)")
}

1
그것은 당신이 만든 개별 열거 형에 특정한 많은 일처럼 느껴집니다-반환되는 경우 [Number.One.rawValue, Number.Two.rawValue, ...]가 더 깨끗하지 않다고 확신하지 않습니다. .
Scott Austin

이것은 Swift 2.0의 꽤 오래된 게시물입니다. swift 3.0의 새로운 기능을 사용하는 더 나은 솔루션이 여기 있습니다 : stackoverflow.com/questions/41352594/… 이 질문에는 새로운 기능을 사용하는 솔루션이 있습니다. ) 스위프트 4.2 : stackoverflow.com/questions/27094878/…
애비 잭슨

2

열거 형 toRaw()fromRaw()메소드가 있습니다. 따라서 원시 값이 인 Int경우 처음부터 끝까지 반복 할 수 있습니다 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()
    }
}

한 가지 단점은 simpleDescription메소드 를 실행하기 전에 선택적 값을 테스트해야 하므로 convertedSuit먼저 값으로 설정 한 다음 상수를convertedSuit.simpleDescription()


2
원래 질문은 문자열 타입의 열거에 관하여이지 지능이었다
cynistersix

2

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

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

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

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


2

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

업데이트 Xcode 6.1은를 사용하여 열거 형 멤버를 얻는 방법을 약간 변경 rawValue했으므로 목록을 수정했습니다. 또한 먼저 잘못된 작은 오류를 수정했습니다 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 답변을 기반으로 함 : 이것은 5 배 빠릅니다.


Count사례를 추가하면 철저한 switch구현 및 CaseIterable준수 가 중단됩니다 .
Cœur
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.