Swift 4: template matches object with tuple (Tuple template cannot match values ​​of type other than tuple)

I have a custom structure with several fields, and I would like to map the template to it in the swift switch , so I can configure the comparison by comparing one of the fields with a regular expression.

eg. Given this structure:

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

I would like to map the pattern as follows:

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

I tried to get this to work using the pattern matching function as follows:

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

But then Xcode (9) does not compile with this error:

Sample template cannot match values ​​of non-tuple type "MyStruct"

The best I have been able to achieve is the following:

 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 } 

This allows me to map the pattern as follows:

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

While this is functional, I would prefer not to have the so-called MatchMyStruct wrappers.

Swift seems to have a magical secret sauce to match patterns with tuples that get in the way. Can I do anything here?

+5
source share
2 answers

Not relevant to the problem of matching tuples, but you can turn the pattern into a String array and still enjoy expressiveness:

 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", "[az]+"]: ... case ["h1", "[0-9]+"]: ... default: ... } 
0
source

You can make a computed property to return a tuple:

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

And then you can switch based on the tuple computed property:

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

Or, if you intend to execute a regular expression:

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

Or, if this is too much, you can define some string functions to match the regular expression pattern, for example:

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

And then:

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

Or do it anyway, but you just want to illustrate that if you want a match with a tuple, you can simply define the computed property to return the tuple and then do whatever you want in the where clauses.

0
source

Source: https://habr.com/ru/post/1272542/


All Articles