2017-10-12 5 views
5

J'ai une structure personnalisée avec quelques champs, et je voudrais créer une correspondance switch déclaration afin que je puisse personnaliser la correspondance, en comparant l'un des champs à une regex.swift 4: le motif correspond à un objet par rapport à un tuple (le motif de tuple ne peut pas correspondre aux valeurs du type non-tuple)

E.g. Compte tenu de cette structure:

struct MyStruct { 
    let header: String 
    let text: String 
} 

J'avais comme à correspondance de motif comme celui-ci:

switch(someInstance) { 
    case ("h1", "[a-z]+"): ... 
    case ("h1", "0-9+"): ... 
} 

J'ai essayé d'obtenir ce à travailler en utilisant une fonction d'appariement de formes comme suit:

func ~=(pattern: (String, String), value: MyStruct) -> Bool { 
    return value.header == pattern.0 && value.text.range(of: pattern.1, options: .regularExpression) != nil 
} 

Mais Xcode (9) ne parvient pas à compiler avec cette erreur:

motif Tuple ne peut pas correspondre à des valeurs du type non-tuple « MyStruct »

Le meilleur que je suis en mesure d'atteindre est le suivant:

struct MatchMyStruct { 
    let header: String 
    let regex: String 

    init(_ header: NSString, _ regex: String) { 
     self.header = header 
     self.regex = regex 
    } 
} 

func ~=(pattern: MatchMyStruct, value: MyStruct) -> Bool { 
    return value.header == pattern.header && value.text.range(of: pattern.regex, options: .regularExpression) != nil 
} 

Cela me permet de correspondance de motif comme celui-ci:

switch(someInstance) { 
    case MatchMyStruct("h1", "[a-z]+"): ... 
    case MatchMyStruct("h1", "0-9+"): ... 
} 

Bien que ce soit fonctionnel, je préférerais de loin ne pas avoir les wrappers MatchMyStruct explicite comme ça.

Il semble qu'il y ait une sauce secrète magique pour la correspondance de motifs avec des tuples qui gênent. Y a-t-il quelque chose que je puisse faire ici?

+0

Est-ce que 'switch ((someInstance.header, someInstance.text))' fonctionnerait? – vacawama

+0

@vacawama c'est une bonne idée. Je l'ai essayé mais malheureusement ça n'a pas fonctionné et j'avais toujours le motif 'Tuple pattern ne peut pas correspondre ...' –

Répondre

0

ne traite pas le problème de correspondance de tuple, mais vous pouvez transformer le modèle en un tableau String et toujours profiter de l'expressivité:

func ~=(pattern: [String], value: MyStruct) -> Bool { 
    return pattern.count == 2 && (value.header as String) == pattern[0] && value.text.range(of: pattern[1], options: .regularExpression) != nil 
} 

switch someInstance { 
    case ["h1", "[a-z]+"]: ... 
    case ["h1", "[0-9]+"]: ... 
    default: ... 
} 
+0

Aussi une bonne idée! Malheureusement dans mon implémentation actuelle (pas la version corrigée pour SO) le premier élément est un CFString et le second est un String, donc je ne peux pas utiliser un tableau :-( –

+0

@OrionEdwards 'let str = header as String' vous pouvez facilement convertir 'CFString' en' String' –

+0

@KleinMioke Merci pour cela! Cependant, j'essayais de réduire la quantité de bruit et de dactylographie nécessaire pour l'utiliser.Faire un 'as String' sur chaque ligne défait un peu –

0

Vous pouvez faire une propriété calculée pour retourner un tuple:

struct MyStruct { 
    let header: String 
    let text: String 

    var tuple: (String, String) { return (header, text) } 
} 

Et puis vous pouvez switch en fonction tuple propriété calculée:

switch(someInstance.tuple) { 
case ("h1", "[a-z]+"): 
    ... 
case ("h1", "0-9+"): 
    ... 
default: 
    ... 
} 

Ou, si votre intention était d'effectuer une correspondance régulière d'expression:

switch(someInstance.tuple) { 
case ("h1", let string) where string.range(of: "^[a-z]+$", options: .regularExpression) != nil: 
    print("alphabetic") 
case ("h1", let string) where string.range(of: "^[0-9]+$", options: .regularExpression) != nil: 
    print("numeric") 
default: 
    print("other") 
} 

Ou, si c'est trop d'une bouchée, vous pouvez définir des fonctions de chaîne pour le filtrage des expressions régulières, par exemple:

extension String { 
    func isMatch(regex pattern: String) -> Bool { 
     return range(of: "^" + pattern + "$", options: .regularExpression) != nil 
    } 
    func contains(regex pattern: String) -> Bool { 
     return range(of: pattern, options: .regularExpression) != nil 
    } 
} 

Et puis:

switch(someInstance.tuple) { 
case ("h1", let string) where string.isMatch(regex: "[a-z]+"): 
    print("alphabetic") 
case ("h1", let string) where string.isMatch(regex: "[0-9]+"): 
    print("numeric") 
default: 
    print("other") 
} 

Ou faire de toute façon que vous voulez, mais il est juste pour illustrer le fait que si vous voulez correspondant tuple, vous pouvez simplement définir c Propriété calculée pour renvoyer le tuple et ensuite faire ce que vous voulez dans les clauses where.