enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}
たとえば、次のようにします。
for suit in Suit {
// do something with suit
print(suit.rawValue)
}
結果の例:
♠
♥
♦
♣
Swift 4.2 (Xcode 10以降)から始めて、CaseIterable
の恩恵を受けられるようにallCases
にプロトコル準拠を追加するだけです。
extension Suit: CaseIterable {}
それからこれはすべての可能な値を表示します。
Suit.allCases.forEach {
print($0.rawValue)
}
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
}
任意のenum
型のケースを繰り返すためのユーティリティ関数iterateEnum()
を作りました。
使用例は次のとおりです。
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
}
}
他の解決策 仕事 しかし、それらはすべて、例えば可能なランクやスーツの数、あるいは最初と最後のランクが何であるかという仮定をしています。確かに、カードのデッキのレイアウトはおそらく近い将来変わらないでしょう。しかし、一般的には、できる限り仮定を少なくするようなコードを書くのは適切ではありません。私の解決策:
私はスーツのenumに生の型を追加したので、SuitのケースにアクセスするためにSuit(rawValue :)を使用することができます。
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 :)は無効な初期化子であり、オプションを返します。両方の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 の答えと同様に、このコードはenumの基礎となる表現を使用します。この表現は文書化されておらず、将来変更される可能性があります。壊れてしまう可能性があります - >私は本番環境でこれを使用することはお勧めしません。
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
}
}
}
}
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の場合に生の値の割り当てを使用しないと仮定して、このようにすることができます。
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でのリスト内包表記
これは、文字列の代わりに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 生の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生の値を持つ限り)、プロトコルに準拠することによってそれを列挙可能にすることができます。
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())
simpleDescription
を より標準的なCustomStringConvertibleプロトコル に置き換えることを含む、最後の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
}
}
Swift 2.2 +に更新
func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
var i = 0
return AnyGenerator {
let next = withUnsafePointer(&i) {
UnsafePointer<T>($0).memory
}
if next.hashValue == i {
i += 1
return next
} else {
return nil
}
}
}
swift 2.2のフォームに更新されたコード @ Kametrixom's an swer
Swift 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"]
編集: スイフトエボリューションの提案 SE-0194列挙型の派生コレクション この問題に向けたレベルを提案する ソリューション 。 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内で 検索 ケースを検索する方法を探しているこの質問につまずく人のために、これはそれをする方法です(Swift 2で新しい):
編集:小文字のcamelCaseがSwift 3のenum値の標準になりました
// 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、あなたは聞いていますか?)
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 3では、基礎となるenumに{rawValue}があるとき、{Strideable}プロトコルを実装できます。利点は、他のいくつかの提案のように値の配列が作成されないこと、そして標準のSwiftの "for i in ..."ステートメントが機能することで、Nice構文になります。
// "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()
実験は次のとおりです。
ランクとスーツの各組み合わせの1枚のカードで、カードの完全なデッキを作成するメソッドをCardに追加します。
それで、メソッドを追加する以外に与えられたコードを修正したり拡張したりすることなく(そしてまだ教えられていないものを使わずに)、私はこの解決策を思い付きました:
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()
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 answer here と同じように、配列を返すほうがAnySequenceを返すよりも優れていると思います。カウントなどのArrayのすべての機能にアクセスできるからです。
これが書き換えです。
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)
}
}
すべての値の配列を返す計算プロパティを使用しました(この記事のおかげで http://natecook.com/blog/2014/10/loopy-random-enum-ideas/ )。しかし、それはint raw-valuesも使用しますが、列挙のすべてのメンバを別々のプロパティで繰り返す必要はありません。
_ update _ Xcode 6.1は生の値を使ってenumメンバーを取得する方法を少し変更したので、リストを修正しました。最初の生の値が間違っているという小さなエラーも修正
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++)
}
}
)
}
}
列挙型には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()
}
}
1つの問題は、simpleDescriptionメソッドを実行する前にオプションの値をテストする必要があることです。そのため、最初にconversionSuitを自分の値に設定し、次に定数をconvertSuit.simpleDescription()に設定します。
これはSwift 2.0からのかなり古い投稿です。ここにSwift 3.0のより新しい機能を使用するいくつかのより良い解決策があります: Swift 3.0のEnumを通して反復
そしてこの質問に関しては、(私がこの編集を書いているときにはまだリリースされていない)Swift 4.2の新機能を使った解決策があります: Swift enumのカウントをどうやって取得できますか?
このスレッドや他のスレッドには良い解決策がたくさんありますが、そのうちのいくつかは非常に複雑です。できるだけ単純化するのが好きです。これは、さまざまなニーズに対して機能する可能性がある、または機能しない可能性があるソリューションです。ただし、ほとんどの場合はうまく機能すると思います。
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 Suit: Int {
case Spades = 0, Hearts, Diamonds, Clubs
...
}
var suitIndex = 0
while var suit = Suit.fromRaw(suitIndex++) {
...
}
ここで私が使っているのは、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
ナンバーワンのフランス語:un、スペイン語:uno、日本語:ichi
フランス語の数字2:deux、スペイン語:dos、日本語:ni
フランス語の数字3:トロイ、スペイン語:トレス、日本語:サン
フランス語の数4:quatre、スペイン語:cuatro、日本語:shi
フランスの数5:cinq、スペイン語:cinco、日本語:go
フランス語の数6:6、スペイン語:seis、日本語:roku
フランス語の7つの数:9月、スペイン語:siete、日本語:shichi
計8件
日本語のシーエイト:shichi
_ update _
列挙を処理するためのプロトコルを最近作成しました。このプロトコルでは、生のInt値を持つenumが必要です。
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とOOPの初心者です)!おそらく誰かがそれを改良することができます。アイデアはそれぞれのenumに.firstと.lastプロパティとしてそれ自身の範囲情報を提供させることです。それは各enumに2行のコードを追加するだけです:それでも少しハードコードされています、しかし少なくともそれはセット全体を複製することではありません。 Rank列挙型のように、型指定されていない代わりに、Suit列挙型をIntに変更する必要があります。
ソリューション全体をエコーするのではなく、caseステートメントの後のどこかにランク列挙に追加したコードです(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と言ってもらいたいのですが。それは(どんな要素でも)働きますが、それは醜いです。誰かがそれをenumレベルに上げる方法を示すことができますか?
そしてそれを機能させるために、createDeckメソッドをCard構造体から外しました...その構造体から返される[String]配列を取得する方法を理解することができませんでした。
別の解決策:
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)
}
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倍高速です
(Karthik Kumar回答の改善)
この解決策はあなたがケースを見逃すことがないことを保証するためにコンパイラを使用しています。
enum Suit: String {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
static var enumerate: [Suit] {
switch Suit.spades {
// make sure the two lines are identical ^_^
case .spades, .hearts, .diamonds, .clubs:
return [.spades, .hearts, .diamonds, .clubs]
}
}
}
賢い方法があり、それがそうであるようにイライラすることは2つの異なる種類のenumの間の違いを示しています。
これを試して:
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
}
数字で裏付けられたenumは暗黙的に明示的に順序付けられているのに対し、数字で裏付けられたenumは暗黙的に明示的に順序付けられています。
例えば。 enum値に数を与えると、その数がどんな順序で並んでいるのかを理解するのに十分なほど賢い言語になります。空中で立ち上がって"はい、でもどちらを先に行きたいですか???"
これを実行できる他の言語(順序付けされていない列挙型の繰り返し)は、すべてが実際には地図や辞書であり、論理的な順序があるかどうかにかかわらず、地図のキーを繰り返し使用できます。
したがって、トリックは、明示的に順序付けされたもの、この場合は目的の順序で配列内のスーツのインスタンスを提供することです。あなたがそれを与えるとすぐに、Swiftは"そもそもなぜそんなこと言わなかったの?"
もう1つの簡単な方法は、fromRaw関数で強制演算子を使用することです。これは、列挙型に関するもう1つの「問題点」を示しています。渡すことが可能な値の範囲は、多くの場合、列挙型の範囲よりも大きいということです。たとえば、Rank.fromRaw(60)と言った場合、値は返されないので、言語の optional 機能を使用しています。オプションの使用を開始したところでは、すぐに強制が行われます。 (あるいは、やや奇妙に思えるかもしれませんが、代わりにlet ifという構造になります)
Swift 5の解決策: / Swift 5の解決策はとても簡単です。
enum Suit: String, CaseIterable {
case spades = "♠"
case hearts = "♥"
case diamonds = "♦"
case clubs = "♣"
}
// then access the cases like this:
for suitKey in LocalizationKey.allCases {
print(suitKey)
}
以下の方法を使用しました。仮定は、どちらがランク列挙の最後の値であり、すべてのランクが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
}
場合によっては、ソフトウェア開発のライフサイクルを通して変化する、基礎となる生の整数型を持つ列挙型を扱うことがあります。これはその場合によく働く例です:
public class MyClassThatLoadsTexturesEtc
{
//...
// Colors used for gems and sectors.
public enum Color: Int
{
// Colors arranged in order of the spectrum.
case First = 0
case Red, Orange, Yellow, Green, Blue, Purple, Pink
// --> Add more colors here, between the first and last markers.
case Last
}
//...
public func preloadGems()
{
// Preload all gems.
for i in (Color.First.toRaw() + 1) ..< (Color.Last.toRaw())
{
let color = Color.fromRaw(i)!
loadColoredTextures(forKey: color)
}
}
//...
}
Swiftの本が要求するような構造体の中の1つのメソッドよりも少しだけ時間がかかりましたが、私はenumに次の関数を設定しました。私はなぜか分からないプロトコルを使用していたでしょうが、intとしてランクセットを持つことはそれをめちゃくちゃにします
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.toRaw())
}
}
mutating func next() -> Rank {
var rank = self
var rawrank = rank.toRaw()
var nrank:Rank = self
rawrank = rawrank + 1
if let newRank = Rank.fromRaw(rawrank) {
println("\(newRank.simpleDescription())")
nrank = newRank
} else {
return self
}
return nrank
}
}
enum Suit {
case Spades, Hearts, Diamonds, Clubs
func color() -> String {
switch self{
case .Spades, .Clubs:
return "black"
default:
return "red"
}
}
func simpleDescription() -> String {
switch self{
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
mutating func next() -> Suit {
switch self{
case .Spades:
return Hearts
case .Hearts:
return Diamonds
case .Diamonds:
return Clubs
case .Clubs:
return Spades
}
}
}
struct Card {
var rank:Rank
var suit:Suit
func deck() -> Card[] {
var tRank = self.rank
var tSuit = self.suit
let tcards = 52 // we start from 0
var cards: Card[] = []
for i in 0..tcards{
var card = Card(rank: tRank, suit: tSuit)
cards.append(card)
tRank = tRank.next()
tSuit = tSuit.next()
}
return cards
}
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}
var card = Card(rank: .Ace, suit: .Spades)
var deck = card.deck()
これが基本的に私が少し一般的な知識を使った助けになることを願っていますが、それは訴訟にランクを掛けることによって容易に直すことができます。私が使った時間を節約するための異なる列挙型のメモはあなたが望むならスーツのためにあなたが同じことをすることができる生の値をランク付けするしかし例はそれを持っていなかった
関数count()を追加して、値を繰り返します。
public enum MetricType : Int {
case mvps = 0
case allNBA = 1
case championshipRings = 2
case finalAppearances = 3
case gamesPlayed = 4
case ppg = 5
static func count() -> Int {
return (ppg.rawValue) + 1
}
static var allValues: [MetricType] {
var array: [MetricType] = Array()
var item : MetricType = MetricType.mvps
while item.rawValue < MetricType.count() {
array.append(item)
item = MetricType(rawValue: (item.rawValue + 1))!
}
return array
}
}
私の解決策は、すべてのenumの可能性を持つ配列を宣言することです。そのため、それらすべてを通過することができます。
//Function inside struct Card
static func generateFullDeck() -> [Card] {
let allRanks = [Rank.Ace, Rank.Two, Rank.Three, Rank.Four, Rank.Five, Rank.Six, Rank.Seven, Rank.Eight, Rank.Nine, Rank.Ten, Rank.Jack, Rank.Queen, Rank.King]
let allSuits = [Suit.Hearts, Suit.Diamonds, Suit.Clubs, Suit.Spades]
var myFullDeck: [Card] = []
for myRank in allRanks {
for mySuit in allSuits {
myFullDeck.append(Card(rank: myRank, suit: mySuit))
}
}
return myFullDeck
}
//actual use:
let aFullDeck = Card.generateFullDeck() //Generate the desired full deck
var allDesc: [String] = []
for aCard in aFullDeck {
println(aCard.simpleDescription()) //You'll see all the results in playground
}
やや厄介だがもっと安全な方法で、値を2回入力したり、enum値のメモリを参照したりする必要がないので、壊れる可能性は非常に低い。
基本的には、enumを使用する代わりに、単一のインスタンスで構造体を作り、すべてのenum-values定数を作ります。変数はMirror
を使って問い合わせることができます。
public struct Suit{
// the values
let spades = "♠"
let hearts = "♥"
let diamonds = "♦"
let clubs = "♣"
// make a single instance of the Suit struct, Suit.instance
struct SStruct{static var instance: Suit = Suit()}
static var instance : Suit{
get{return SStruct.instance}
set{SStruct.instance = newValue}
}
// an array with all of the raw values
static var allValues: [String]{
var values = [String]()
let mirror = Mirror(reflecting: Suit.instance)
for (_, v) in mirror.children{
guard let suit = v as? String else{continue}
values.append(suit)
}
return values
}
}
この方法を使用する場合、単一の値を取得するにはSuit.instance.clubs
またはSuit.instance.spades
を使用する必要があります。
しかし、それはすべてとても退屈です...これをもっと本当のenumのようにするためのいくつかのことをしましょう!
public struct SuitType{
// store multiple things for each suit
let spades = Suit("♠", order: 4)
let hearts = Suit("♥", order: 3)
let diamonds = Suit("♦", order: 2)
let clubs = Suit("♣", order: 1)
struct SStruct{static var instance: SuitType = SuitType()}
static var instance : SuitType{
get{return SStruct.instance}
set{SStruct.instance = newValue}
}
// a dictionary mapping the raw values to the values
static var allValuesDictionary: [String : Suit]{
var values = [String : Suit]()
let mirror = Mirror(reflecting: SuitType.instance)
for (_, v) in mirror.children{
guard let suit = v as? Suit else{continue}
values[suit.rawValue] = suit
}
return values
}
}
public struct Suit: RawRepresentable, Hashable{
public var rawValue: String
public typealias RawValue = String
public var hashValue: Int{
// find some integer that can be used to uniquely identify
// each value. In this case, we could have used the order
// variable because it is a unique value, yet to make this
// apply to more cases, the hash table address of rawValue
// will be returned, which should work in almost all cases
//
// you could also add a hashValue parameter to init() and
// give each suit a different hash value
return rawValue.hash
}
public var order: Int
public init(_ value: String, order: Int){
self.rawValue = value
self.order = order
}
// an array of all of the Suit values
static var allValues: [Suit]{
var values = [Suit]()
let mirror = Mirror(reflecting: SuitType.instance)
for (_, v) in mirror.children{
guard let suit = v as? Suit else{continue}
values.append(suit)
}
return values
}
// allows for using Suit(rawValue: "♦"), like a normal enum
public init?(rawValue: String){
// get the Suit from allValuesDictionary in SuitType, or return nil if that raw value doesn't exist
guard let suit = SuitType.allValuesDictionary[rawValue] else{return nil}
// initialize a new Suit with the same properties as that with the same raw value
self.init(suit.rawValue, order: suit.order)
}
}
これで、こんなことができます
let allSuits: [Suit] = Suit.allValues
または
for suit in Suit.allValues{
print("The suit \(suit.rawValue) has the order \(suit.order)")
}
ただし、シングルを入手するには、SuitType.instance.spades
またはSuitType.instance.hearts
を使用する必要があります。これをもう少し直感的にするために、Suit
にSuit.type.*
の代わりにSuitType.instance.*
を使用できるようにするコードを追加できます
public struct Suit: RawRepresentable, Hashable{
// ...your code...
static var type = SuitType.instance
// ...more of your code...
}
Suit.type.diamonds
の代わりにSuitType.instance.diamonds
を、またはSuit.type.clubs
の代わりにSuitType.instance.clubs
を使用できるようになりました
@ rintaroの答え /をSwift 3に適応させるrintaro:
iterateEnum()
を書きましたSwift3
func iterateEnum<T: Hashable>(_: T.Type) -> AnyGenerator<T> {
var i = 0
return AnyGenerator {
let next = withUnsafePointer(&i) { UnsafePointer<T>($0).memory }
let value : T?
if next.hashValue == i {
value = next
} else {
value = nil
}
i = i + 1
return value
}
}