web-dev-qa-db-ja.com

Swift 3.0の列挙型を反復処理する

繰り返したい単純な列挙型があります。この目的のために、以下のコードに示すように、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 '"に準拠していません。プロトコルの適合性は私の拡張機能にあります。では、このコードの何が問題になっていますか?

これを行う方法は他にもあることは知っていますが、このアプローチの何が問題になっているのかを理解したいと思います。

ありがとう。

5
Phantom59

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
10
Jano

更新:Swift 4.2の時点で、プロトコル適合性をCaseIterableに追加するだけです。-を参照してください。 文字列型で列挙型を列挙する方法は?


Sequenceプロトコルに準拠するタイプのvalueを反復処理できます。したがって、

for section in Sections.Section0 {
  print(section)
}

コンパイルして期待どおりの結果が得られます。しかしもちろん、値の選択は任意であり、値自体はシーケンスで必要ないため、これは実際には必要なことではありません。

私の知る限り、型自体を反復処理する方法はないので、

for section in Sections {
  print(section)
}

コンパイルします。そのためには、「メタタイプ」Sections.TypeSequenceに準拠している必要があります。おそらく誰かが私が間違っていることを証明します。

できることは、シーケンスを返す型メソッドを定義することです。

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)
}
9
Martin R

列挙型に追加するだけです: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)
    }
}
3
MonsterSale

列挙型が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()をループします。

1
Mike S

上記のソリューションを繰り返し、以下を参照してください。列挙型で実装して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!")
    }
}
1