enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}
예를 들어, 다음과 같은 방법으로 어떻게 할 수 있습니까?
for suit in Suit {
// do something with suit
print(suit.rawValue)
}
결과 예 :
♠
♥
♦
♣
답변
스위프트 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
답변
이 게시물은 여기 관련이 있습니다 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)
}
}
}
카드 createDeck()
방법 의 구현 아래 . init(rawValue:)
실패한 이니셜 라이저이며 선택적을 반환합니다. 두 while 문에서 값을 풀고 값을 확인하면 다음과 같은 수 Rank
또는 Suit
경우 를 가정 할 필요가 없습니다 .
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
func createDeck() -> [Card] {
var n = 1
var deck = [Card]()
while let rank = Rank(rawValue: n) {
var m = 1
while let suit = Suit(rawValue: m) {
deck.append(Card(rank: rank, suit: suit))
m += 1
}
n += 1
}
return deck
}
}
createDeck
메소드 를 호출하는 방법 은 다음과 같습니다 .
let card = Card(rank: Rank.Ace, suit: Suit.Clubs)
let deck = card.createDeck()
답변
나는 비트와 바이트를 우연히 발견하고 나중에 @rintaro 의 답변 과 매우 유사한 작동을 발견 한 확장을 만들었습니다 . 다음과 같이 사용됩니다.
enum E : EnumCollection {
case A, B, C
}
Array(E.cases()) // [A, B, C]
주목할만한 것은 연관된 값이없는 모든 열거 형에서 사용할 수 있다는 것입니다. 케이스가없는 열거 형에는 작동하지 않습니다.
@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
}
답변
이 문제는 훨씬 쉬워졌습니다. 다음은 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
}