enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}
たとえば、次のようにするにはどうすればよいですか。
for suit in Suit {
// do something with suit
print(suit.rawValue)
}
結果の例:
♠
♥
♦
♣
enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}
たとえば、次のようにするにはどうすればよいですか。
for suit in Suit {
// do something with suit
print(suit.rawValue)
}
結果の例:
♠
♥
♦
♣
回答:
始まるスイフト4.2(Xcodeの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.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
String
にあり、現在のスタックオーバーフローの質問とは対照的に、列挙型がで裏付けられていない簡略化された例を使用しています。
この投稿はここに関連しています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
}
Enum.Values(typeof(FooEnum))
が、拡張メソッド(マップやリデュースなど)として公開することができます。 FooEnum.values() :: values(EnumType -> [EnumType])
iterateEnum()
任意のenum
型のケースを反復するユーティリティ関数を作成しました。
次に使用例を示します。
enum Suit: String {
case Spades = "♠"
case Hearts = "♥"
case Diamonds = "♦"
case Clubs = "♣"
}
for f in iterateEnum(Suit) {
println(f.rawValue)
}
どの出力:
♠
♥
♦
♣
ただし、これはデバッグまたはテストの目的のみです。これは、文書化されていないSwift1.1コンパイラの動作に依存しているため、自己責任で使用してください。
これがコードです:
func iterateEnum<T: Hashable>(_: T.Type) -> GeneratorOf<T> {
var cast: (Int -> T)!
switch sizeof(T) {
case 0: return GeneratorOf(GeneratorOfOne(unsafeBitCast((), T.self)))
case 1: cast = { unsafeBitCast(UInt8(truncatingBitPattern: $0), T.self) }
case 2: cast = { unsafeBitCast(UInt16(truncatingBitPattern: $0), T.self) }
case 4: cast = { unsafeBitCast(UInt32(truncatingBitPattern: $0), T.self) }
case 8: cast = { unsafeBitCast(UInt64($0), T.self) }
default: fatalError("cannot be here")
}
var i = 0
return GeneratorOf {
let next = cast(i)
return next.hashValue == i++ ? next : nil
}
}
基本的なアイデアは次のとおりです。
enum
を除く、enum
関連するタイプでsが、例数があるとき例だけ指標である2...256
、それは同じだUInt8
とき、257...65536
それはだ、UInt16
というように。したがって、unsafeBitcast
対応する符号なし整数型からのものである可能性があります。.hashValue
列挙型の値は、ケースのインデックスと同じです。.hashValue
無効なインデックスからビットキャストされた列挙値のは0
です。Swift2用に改訂され、@ Kametrixomの回答からキャストのアイデアを実装しました。
func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
var i = 0
return anyGenerator {
let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
return next.hashValue == i++ ? next : nil
}
}
Swift3用に改訂:
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var i = 0
return AnyIterator {
let next = withUnsafePointer(to: &i) {
$0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee }
}
if next.hashValue != i { return nil }
i += 1
return next
}
}
Swift3.0.1用に改訂:
func iterateEnum<T: Hashable>(_: T.Type) -> AnyIterator<T> {
var i = 0
return AnyIterator {
let next = withUnsafeBytes(of: &i) { $0.load(as: T.self) }
if next.hashValue != i { return nil }
i += 1
return next
}
}
withUnsafePointer
withMemoryRebound
とpointee
、スタッフがすべての手段によって、これを使用しています。そうでなければ、私はそれを避けます。
他のソリューションは機能しますが、それらはすべて、たとえば可能なランクとスートの数、または最初と最後のランクが何であるかを想定しています。確かに、カードのデッキのレイアウトはおそらく予見可能な将来においてあまり変化しないでしょう。ただし、一般に、できるだけ少ない仮定を行うコードを記述する方が適切です。私の解決策:
Suit
enumにrawタイプを追加したので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()
Suit
。この例ではできますが、この演習はenums
、外部ソースから提供されているかのように、与えられたで作業することを目的としています。
ビットとバイトを偶然見つけて、@ 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
が、コンパイラーはそれがなければ文句を言います。
withUnsafePointer
… pointee}
を置き換えるwithUnsafePointer(to: &i) { $0.withMemoryRebound(to: T.self, capacity: 1) { $0.pointee } }
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
}
successor()
メソッド(最初のオプション)はenum
、が関連する型を持つ必要をなくします。+1
この問題ははるかに簡単になりました。これが私の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のリスト内包表記」を見つけてまとめたこのソリューションが気に入っています。
文字列の代わりにInt Rawを使用しますが、2回入力する必要がなく、範囲をカスタマイズでき、Raw値をハードコードしません。
これは私の元のソリューションの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
}
原則として、列挙型の場合に生の値の割り当てを使用しないことを前提として、この方法でこれを行うことができます。
enum RankEnum: Int {
case Ace
case One
case Two
}
class RankEnumGenerator: Generator {
var i = 0
typealias Element = RankEnum
func next() -> Element? {
let r = RankEnum.fromRaw(i)
i += 1
return r
}
}
extension RankEnum {
static func enumerate() -> SequenceOf<RankEnum> {
return SequenceOf<RankEnum>({ RankEnumGenerator() })
}
}
for r in RankEnum.enumerate() {
println("\(r.toRaw())")
}
enum ItWontWorkForThisEnum {case a, b, c}
列挙型に生の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
}
}
}
これは、@ 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
}
}
}
私は自分の.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"]
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 Evolution Proposal SE-0194 Derived Collection of 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
}
}
これはどう?
このように列挙してみることができます
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]
static var enumerate = [Mercury, Venus, Earth, Mars]
投票してほとんどのに比べて基準以下の答えながら、解答stackoverflow.com/a/24137319/1033581
return [Mercury, Venus, Mars]
代わりにreturn [Mercury, Venus, Earth, Mars]
return [.spades, .hearts, .clubs]
、コンパイラは何も言わないので、コードでそれを使用しようとすると、次のようになり[TestApp.Suit.spades, TestApp.Suit.hearts, TestApp.Suit.clubs]
ます。列挙型で、ケースを時々追加または削除する必要がある場合、現在の回答は簡潔ではありませんが安全ですが、ソリューションは省略のエラーになりがちです。
Swift 3では、基礎となる列挙型にがrawValue
ある場合、Strideable
プロトコルを実装できます。利点は、他のいくつかの提案のように値の配列が作成されないこと、および標準のSwift for forループが機能することです。これにより、優れた構文が作成されます。
// "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)")
}
このソリューションは、可読性と保守性の適切なバランスを実現します。
struct Card {
// ...
static func deck() -> Card[] {
var deck = Card[]()
for rank in Rank.Ace.toRaw()...Rank.King.toRaw() {
for suit in [Suit.Spades, .Hearts, .Clubs, .Diamonds] {
let card = Card(rank: Rank.fromRaw(rank)!, suit: suit)
deck.append(card)
}
}
return deck
}
}
let deck = Card.deck()
申し訳ありませんが、私の答えは、この投稿をどのように使用する必要があるかを具体的に示したものでした。この質問に遭遇し、列挙内でケースを見つける方法を探している人にとって、これはそれを行う方法です(Swift 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、聞いていますか?)!
実験は:実験
カードにメソッドを追加して、ランクとスーツの各組み合わせの1枚のカードでカードの完全なデッキを作成します。
したがって、メソッドを追加する以外にコードを変更または拡張せずに(そして、まだ教えられていないものを使用せずに)、私はこの解決策を思いつきました:
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()
ここに私が両方を反復し、enum
1つから複数の値タイプを提供するために使用する方法があります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
数字の1つのフランス語:un、スペイン語:uno、日本語:ichi
数字の2つのフランス語:deux、スペイン語:dos、日本語:ni
数字の3つのフランス語:trois、スペイン語:tres、japanese:san
ナンバー4フランス語:quatre、スペイン語:cuatro、japanese:shi
ナンバー5フランス語:cinq、スペイン語:cinco、日本語:go
ナンバー6フランス語:6、スペイン語: seis、日本語:roku
The number Seven in french:sept、スペイン語:siete、japanese:shichi
合計8件
日本語でsiete:七
更新
最近、列挙を処理するプロトコルを作成しました。プロトコルには、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
}
}
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の列挙型の反復
そしてこの質問には、(この編集を書いているときにまだリリースされていない)Swift 4.2の新機能を使用するソリューションがあります: Swift列挙のカウントを取得するにはどうすればよいですか?
このスレッドと他のスレッドには多くの優れたソリューションがありますが、それらのいくつかは非常に複雑です。できるだけ簡略化したい。さまざまなニーズで機能する場合と機能しない場合があるソリューションは次のとおりですが、ほとんどの場合はうまく機能すると思います。
enum Number: String {
case One
case Two
case Three
case Four
case EndIndex
func nextCase () -> Number
{
switch self {
case .One:
return .Two
case .Two:
return .Three
case .Three:
return .Four
case .Four:
return .EndIndex
/*
Add all additional cases above
*/
case .EndIndex:
return .EndIndex
}
}
static var allValues: [String] {
var array: [String] = Array()
var number = Number.One
while number != Number.EndIndex {
array.append(number.rawValue)
number = number.nextCase()
}
return array
}
}
反復するには:
for item in Number.allValues {
print("number is: \(item)")
}
列挙型にはメソッドtoRaw()
とfromRaw()
メソッドがあります。したがって、生の値がの場合Int
、最初から最後まで反復できますenum
。
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()
}
}
1つの落とし穴は、simpleDescription
メソッドを実行する前にオプションの値をテストする必要があるため、convertedSuit
最初に値を設定し、次に定数をconvertedSuit.simpleDescription()
これが私の提案するアプローチです。それは完全に満足のいくものではありません(私はSwiftとOOPに非常に新しいです!)が、誰かがそれを改良できるかもしれません。アイデアは、各列挙型に独自の範囲情報.first
と.last
プロパティを提供させることです。各列挙型に2行のコードを追加します。それでも、少しハードコードされていますが、少なくともセット全体を複製しているわけではありません。Suit
列挙型をIntに変更する必要があります。Rank
型なしではなく列挙型を列挙型とます。
ソリューション全体をエコーするのではなく.
、caseステートメントの後のどこかにあるenum に追加したコードを次に示します(Suit
enumも同様です)。
var first: Int { return Ace.toRaw() }
var last: Int { return King.toRaw() }
そして、デッキを文字列の配列として構築するために使用したループ。(問題の定義には、デッキの構成方法は記載されていませんでした。)
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]配列を返す方法を理解できなかったので、そのようなメソッドを配置するのは悪い場所のようです。
私はすべての値の配列を返す計算されたプロパティを使用してそれを行いました(この投稿http://natecook.com/blog/2014/10/loopy-random-enum-ideas/に感謝します)。ただし、これはint raw値も使用しますが、列挙のすべてのメンバーを個別のプロパティで繰り返す必要はありません。
UPDATE Xcode 6.1 rawValue
では、enumメンバーをを使用して取得する方法が少し変更されたため、リストを修正しました。最初に間違った小さなエラーも修正しました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++)
}
}
)
}
}
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
}
}
リックの回答に基づく:これは5倍高速です
Count
ケースを追加すると、完全なswitch
実装とCaseIterable
適合性が失われます。