Itération à travers un Enum dans Swift 3.0

j'ai un énum simple que je voudrais itérer. Dans ce but, J'ai adopté la séquence et IteratorProtocol comme indiqué dans le code ci-dessous. BTW, cela peut être copié/collé à un terrain de jeu dans Xcode 8.

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)
}

mais la boucle for-in génère le message d'erreur " Type 'Sections.Type ' ne correspond pas au protocole 'Sequence'" . La conformité au Protocole est dans mon extension; alors, qu'est-ce qui ne va pas avec ce code?

je sais qu'il y a d'autres façons de faire cela, mais j'aimerais comprendre ce qui ne va pas avec cette approche.

Merci.

5
demandé sur Phantom59 2016-12-28 00:48:08

6 réponses

notez que Martin's solution peut être refactorisé comme un protocole:

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
    }
}

puis, donné un enum

enum Fruits: Int {
    case apple, orange, pear
}

vous claquez le protocole et une typographie:

enum Fruits: Int, EnumSequence {
    typealias T = Fruits
    case apple, orange, pear
}

Fruits.all().forEach({ print("151920920") }) // apple orange pear
10
répondu Jano 2017-07-20 13:53:00

mise à jour: à partir de Swift 4.2, vous pouvez simplement ajouter la conformité au protocole à CaseIterable , voir comment énumérer un enum avec un type de chaîne de caractères? .


vous pouvez itérer sur une valeur d'un type qui se conforme à la Sequence protocole. Par conséquent

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

compilerait et donnerait le résultat attendu. Mais bien sûr, ce n'est pas vraiment ce que vous voulez, parce que le choix de la valeur est arbitraire et de la valeur elle-même non nécessaire dans la séquence.

autant que je sache, il n'y a aucun moyen d'itérer sur un type lui-même, de sorte que

for section in Sections {
  print(section)
}

compile. Cela exigerait que le "metatype " Sections.Type se conforme à Sequence . Peut-être que quelqu'un prouve que j'ai tort.

ce que vous pouvez faire est de définir une méthode de type qui retourne une séquence:

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)
}
7
répondu Martin R 2018-07-16 14:52:16

il suffit d'ajouter à enum: static var allTypes: [Sections] = [.Section0, .Section1, .Section2]

et que vous pouvez:

Sections.allTypes.forEach { (section) in
            print("\(section)")
}
3
répondu Сергей Билык 2018-07-16 13:59:30

Cela ressemble donc beaucoup plus simple:

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)
    }
}
2
répondu MonsterSale 2017-07-28 15:46:30

Itéré sur les solutions ci-dessus, voir ci-dessous un protocole qui peut être mis en œuvre par les énumérations pour ajouter le allValues de la séquence, mais aussi pour permettre la possibilité de convertir vers et à partir de la chaîne de valeur.

très pratique pour les énumérations à chaîne qui doivent supporter l'objectif c (seules les énumérations int y sont autorisées).

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: { "151900920".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)
    }
}

pour un exemple concret:

@objc
enum Fruit: Int, ObjcEnumeration {
    case apple, orange, pear
}

Maintenant vous pouvez faire:

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
répondu Werner Altewischer 2017-11-30 15:25:40

si votre enum est basé sur un Int, vous pouvez faire un tour efficace mais légèrement sale comme celui-ci.

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
    }
}

puis boucle sur MyEnum.allCases ()..

1
répondu Mike S 2018-02-13 14:40:24