1
votes

Comment puis-je faire correspondre un modèle avec l'opérateur OR sur une énumération avec des valeurs associées?

J'ai cette énumération:

if [Foo.a, Foo.b, Foo.c].contains(foo) { ... }

et foo

if case .a, .b, .c = foo { ... }

Je veux vérifier si toto est soit a , b ou c , indépendamment de ce que x est.

Avec une instruction switch, je pourrais faire:

switch foo {
case .a, .b, .c:
    ...
case .d:
    break
}

mais c'est un peu long.

J'ai pensé que je pourrais faire la même chose avec if case :

let foo = Foo.a(x: 10)

Cela a produit une erreur de compilation.

J'ai alors trouvé cette question , et a essayé ceci:

enum Foo {
    case a(x: Int)
    case b(x: Int)
    case c
    case d
}

Le compilateur pensait que le tableau était de type [Any] , donc cela ne fonctionne pas non plus ...

Que puis-je faire sauf l'extraire comme méthode et appeler cette méthode? Y a-t-il quelque chose de nouveau dans Swift 4.2 qui résout ce problème?


7 commentaires

Le faire avec un if ;-) ... if ({switch foo {case .a, .b, .c: return true; default: return false}} ()) { print ("match")}


Il n'y a rien de nouveau Swift 4.2 à ce sujet.


case .a, .b est également incorrect, car ils doivent être saisis.


@AnkitThakur Je peux faire .a, .b dans une instruction switch


@Sweeper Votre problème lorsque vous utilisez l'approche contient est que Foo n'est pas égalable. Foo.a ce n'est même pas une syntaxe valide pour votre structure Foo.a (x: 1)


Il vous suffit donc de déclarer enum Foo: Equatable { et lors de l'appel de if [Foo.a (x: 1), Foo.b (x: 2), Foo.c] .contains (toto) {


@LeoDabus Je veux que le test soit vrai pour tous les x , pas seulement pour 2 et 1 .


3 Réponses :


-3
votes

Malheureusement, il n'y a pas d'autre moyen.

C'est parce que Foo.a est de type (Int) -> Foo. La raison pour laquelle vous ne pouvez pas utiliser le array.contains est parce qu'une fermeture et Foo sont des types différents donc le compilateur suppose que vous vouliez un tableau de Any.

Pour voir ce phénomène vous-même, essayez ce code:

print(type(of: Foo.a))

Vous obtiendrez (Int) -> Foo .


3 commentaires

Non, non. L'instruction switch affichée par @Sweeper fonctionne très bien.


Il y a une petite goutte d'informations importantes ici, qui concernent le type de Foo.a qui n'est pas Foo . Mais le reste est totalement faux, impliquant notamment que Foo.a est une fermeture.


C'est - regardez la saisie semi-automatique de Xcode lorsque vous le tapez



0
votes

Si vous prévoyez de répéter ce test plusieurs fois à plusieurs endroits, alors dupliquer la version longue et longue serait en effet ennuyeux; cependant, vous pouvez simplement encapsuler ce bout de code dans une extension.

enum Foo {
  case a(x: Int)
  case b(x: Int)
  case c
  case d

  var isABorC: Bool {
    switch self {
    case .a, .b, .c:
      return true
    case .d:
      return false
    }
  }
}

Alors maintenant, votre test devient quelque chose comme ceci:

if foo.isABorC { ... }

Ou vous pouvez simplement en faire une partie de la déclaration enum:

extension Foo {
  var isABorC: Bool {
    switch self {
    case .a, .b, .c:
      return true
    default:
      return false
    }
  }
}

Il y a un exemple est la documentation swift (4.2) utilisant une énumération imbriquée pour implémenter les rangs d'un jeu de cartes où on pourrait ajouter une isFaceCard var.

En fin de compte, vous n'avez pas besoin de dupliquer indéfiniment ce morceau de texte ad nauseam . Vous pouvez le masquer jusqu'à ce que vous identifiiez une solution plus élégante.


0 commentaires

1
votes

Swift ne prend pas en charge cela car les instances Foo ont besoin d'une correspondance de modèle, car elles ne sont pas Equatable . Et le seul séparateur qui autorise plusieurs correspondances de motifs est , , et cet opérateur correspond à une opération et , vous ne pouvez pas avoir de ou .

Une approche laide (et je dirais incorrecte ou trompeuse) serait d'ajouter la conformité à Equatable et d'ignorer les valeurs associées:

if [Foo.a(x: 0), Foo.b(x: 0), Foo.c].contains(where: foo.sameCase) { ... }
// or
if foo.sameCase(.a(x: 0)) || foo.sameCase(.b(x: 0)) || foo.sameCase(.c) { ... }

Vous pouvez alors faire quelque chose comme ceci:

enum Foo {
    case a(x: Int)
    case b(x: Int)
    case c
    case d

    func sameCase(_ foo: Foo) -> Bool {
        switch self {
        // a little bit more verbose, but don't risk missing new cases
        case .a: if case .a = foo { return true } else { return false }
        case .b: if case .b = foo { return true } else { return false }
        case .c: if case .c = foo { return true } else { return false }
        case .d: if case .d = foo { return true } else { return false }
        }
    }
}

Une autre approche consisterait à ajouter une propriété index et à l'utiliser lors du test:

if [Foo.a(x: 0), Foo.b(x: 0), Foo.c].matchesCase(foo) { ... }

Et utilisez-le dans le sens de

extension Array where Element == Foo {
    func matchesCase(_ foo: Foo) -> Bool {
        return contains {
            switch ($0, foo) {
            case (.a, .a): return true
            case (.b, .b): return true
            case (.c, .c): return true
            case (.d, .d): return true
            default: return false
            }
        }
    }
}

Les deux solutions sont plus verbeuses que le simple commutateur, et elles seraient faisable uniquement si vous devez les utiliser plusieurs fois.

Vous pouvez également étendre Array avec quelque chose comme ceci:

if [Foo.a(x: 0), Foo.b(x: 0), Foo.c].map({ $0.index }).contains(foo.index) { ... }

, et utilisez-le comme ceci:

enum Foo {
    case a(x: Int)
    case b(x: Int)
    case c
    case d

    var index: Int {
        switch self {
        case .a: return 0
        case .b: return 1
        case .c: return 2
        case .d: return 3
        }
    }
}

Et une quatrième solution :). Ajout d'une fonction sameCase :

if [Foo.a(x: 0), Foo.b(x: 0), Foo.c].contains(foo) { ... }

Utilisation:

enum Foo: Equatable {
    case a(x: Int)
    case b(x: Int)
    case c
    case d

    static func ==(_ lhs: Foo, _ rhs: Foo) -> Bool {
        switch (lhs, rhs) {
        case (.a, .a): return true
        case (.b, .b): return true
        case (.c, .c): return true
        case (.d, .d): return true
        default: return false
        }
    }
}


0 commentaires