繰り返したい単純な列挙型があります。この目的のために、以下のコードに示すように、SequenceとIteratorProtocolを採用しました。ところで、これはXcode8のPlaygroundにコピーして貼り付けることができます。
import UIKit
enum Sections: Int {
case Section0 = 0
case Section1
case Section2
}
extension Sections : Sequence {
func makeIterator() -> SectionsGenerator {
return SectionsGenerator()
}
struct SectionsGenerator: IteratorProtocol {
var currentSection = 0
mutating func next() -> Sections? {
guard let item = Sections(rawValue:currentSection) else {
return nil
}
currentSection += 1
return item
}
}
}
for section in Sections {
print(section)
}
ただし、for-inループはエラーメッセージを生成します"Type'Sections.Type 'はプロトコル' Sequence '"に準拠していません。プロトコルの適合性は私の拡張機能にあります。では、このコードの何が問題になっていますか?
これを行う方法は他にもあることは知っていますが、このアプローチの何が問題になっているのかを理解したいと思います。
ありがとう。
Martinのソリューション はプロトコルとしてリファクタリングできることに注意してください。
import Foundation
protocol EnumSequence
{
associatedtype T: RawRepresentable where T.RawValue == Int
static func all() -> AnySequence<T>
}
extension EnumSequence
{
static func all() -> AnySequence<T> {
return AnySequence { return EnumGenerator() }
}
}
private struct EnumGenerator<T: RawRepresentable>: IteratorProtocol where T.RawValue == Int {
var index = 0
mutating func next() -> T? {
guard let item = T(rawValue: index) else {
return nil
}
index += 1
return item
}
}
次に、列挙型が与えられます
enum Fruits: Int {
case Apple, orange, pear
}
プロトコルとtypealiasを叩きます:
enum Fruits: Int, EnumSequence {
typealias T = Fruits
case Apple, orange, pear
}
Fruits.all().forEach({ print($0) }) // Apple orange pear
更新:Swift 4.2の時点で、プロトコル適合性をCaseIterable
に追加するだけです。-を参照してください。 文字列型で列挙型を列挙する方法は? 。
Sequence
プロトコルに準拠するタイプのvalueを反復処理できます。したがって、
for section in Sections.Section0 {
print(section)
}
コンパイルして期待どおりの結果が得られます。しかしもちろん、値の選択は任意であり、値自体はシーケンスで必要ないため、これは実際には必要なことではありません。
私の知る限り、型自体を反復処理する方法はないので、
for section in Sections {
print(section)
}
コンパイルします。そのためには、「メタタイプ」Sections.Type
がSequence
に準拠している必要があります。おそらく誰かが私が間違っていることを証明します。
できることは、シーケンスを返す型メソッドを定義することです。
extension Sections {
static func all() -> AnySequence<Sections> {
return AnySequence {
return SectionsGenerator()
}
}
struct SectionsGenerator: IteratorProtocol {
var currentSection = 0
mutating func next() -> Sections? {
guard let item = Sections(rawValue:currentSection) else {
return nil
}
currentSection += 1
return item
}
}
}
for section in Sections.all() {
print(section)
}
列挙型に追加するだけです:static var allTypes: [Sections] = [.Section0, .Section1, .Section2]
そして、あなたができるより:
Sections.allTypes.forEach { (section) in
print("\(section)")
}
これはとても簡単に見えます:
public protocol EnumSequence {
init?(rawValue: Int)
}
public extension EnumSequence {
public static var items: [Self] {
var caseIndex: Int = 0
let interator: AnyIterator<Self> = AnyIterator {
let result = Self(rawValue: caseIndex)
caseIndex += 1
return result
}
return Array(interator)
}
}
列挙型がIntベースのものである場合、このような効果的ですが少し汚いトリックを実行できます。
enum MyEnum: Int {
case One
case Two
}
extension MyEnum {
func static allCases() -> [MyEnum] {
var allCases = [MyEnum]()
for i in 0..<10000 {
if let type = MyEnum(rawValue: i) {
allCases.append(type)
} else {
break
}
}
return allCases
}
}
次に、MyEnum.allCases()をループします。
上記のソリューションを繰り返し、以下を参照してください。列挙型で実装してallValuesシーケンスを追加するだけでなく、文字列値との間で変換できるようにすることもできます。
Objective Cをサポートする必要がある文字列のような列挙に非常に便利です(int列挙のみが許可されています)。
public protocol ObjcEnumeration: LosslessStringConvertible, RawRepresentable where RawValue == Int {
static var allValues: AnySequence<Self> { get }
}
public extension ObjcEnumeration {
public static var allValues: AnySequence<Self> {
return AnySequence {
return IntegerEnumIterator()
}
}
public init?(_ description: String) {
guard let enumValue = Self.allValues.first(where: { $0.description == description }) else {
return nil
}
self.init(rawValue: enumValue.rawValue)
}
public var description: String {
return String(describing: self)
}
}
fileprivate struct IntegerEnumIterator<T: RawRepresentable>: IteratorProtocol where T.RawValue == Int {
private var index = 0
mutating func next() -> T? {
defer {
index += 1
}
return T(rawValue: index)
}
}
具体的な例:
@objc
enum Fruit: Int, ObjcEnumeration {
case Apple, orange, pear
}
今、あなたはすることができます:
for fruit in Fruit.allValues {
//Prints: "Apple", "orange", "pear"
print("Fruit: \(fruit.description)")
if let otherFruit = Fruit(fruit.description), fruit == otherFruit {
print("Fruit could be constructed successfully from its description!")
}
}