swift 4:模式将对象与元组相匹配(元组模式不能匹配非元组类型的值)

问题描述:

我有一个自定义的结构体,其中包含几个字段,并且我希望在一个快速的switch语句,所以我可以通过比较其中一个字段与正则表达式来自定义匹配。swift 4:模式将对象与元组相匹配(元组模式不能匹配非元组类型的值)

E.g.鉴于这种结构:

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

我倒是喜欢模式匹配这样的:

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

我试图让这种使用模式匹配功能如下工作:

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

但是,然后Xcode(9)无法编译此错误:

元组模式无法比拟的非元组类型的值“MYSTRUCT”

我已经能够达到的最好的是以下几点:

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 
} 

这让我的模式匹配这样的:

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

虽然这是功能性的,但我更愿意不必让MatchMyStruct包装明确这样。

似乎swift有一些神奇的秘诀,用于与元组进行模式匹配。我能在这里做什么吗?

+0

'switch((someInstance.header,someInstance.text))'工作吗? – vacawama

+0

@vacawama这是一个很酷的想法。我尝试了它,但不幸的是它没有工作,仍然有'Tuple模式不能匹配...'错误 –

并不能解决元组匹配的问题,但你可以把图案变成String阵列,仍然享受表现力:

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

也是一个很酷的想法!不幸的是,在我的实际实现中(并非SO的整理版本),第一个元素是一个CFString,第二个元素是一个String,所以我不能使用数组:--( –

+0

@OrionEdwards'let str = header as String'你可以很容易地将'CFString'转换为'String' –

+0

@KleinMioke非常感谢!但是我试图减少使用它所需的噪音和打字量。在每一行上做一个'as String'会有点失败 –

你可以做一个计算的属性返回一个元组:

struct MyStruct { 
    let header: String 
    let text: String 

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

然后你就可以根据tuple计算财产switch

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

或者,如果你的目的是为了执行正则表达式匹配:

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

或者,如果这是太多了一口,你可以定义正则表达式匹配,例如一些字符串函数:

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

然后:

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

还是你想这无论如何,但它只是说明,如果你想匹配的元组,你可以定义ç买卖财产返回元组,然后在where子句中做任何你想要的。