在Swift中编写自定义模式匹配

您也可以在我的博客SwiftRocks中阅读此文章,这是一个凉爽的地方!

模式匹配在Swift中随处可见,您可能已经使用了无数次来解构和绑定值(如switch例)。 尽管常规的switch例是模式的最常用用法,但是Swift有几种类型的模式可以混合使用,甚至可以在switches外使用,从而产生真正的简短代码。 我特别感兴趣的一件事是模式匹配可以用于各种各样的事情。

乍一看,很容易将模式匹配视为简单的相等性检查:

 开关80 { 
案例100:
//
案例80:
//匹配,因为80 == 80
默认:
打破
}

鉴于此,您可能会认为case "eighty"这样的case "eighty"将无法编译,毕竟"eighty"80甚至都不是同一类型–如果您现在尝试使用,确实会发生以下情况:

 如果案例“八十” = 80 { 
//错误:类型为“字符串”的表达式模式不能与类型为“ Int”的值匹配
}

但这并不一定。 在开发项目时,您可能已经注意到某些类型之间具有特殊的交互作用,例如Ranges及其关联的类型:

 开关80 { 
情况0 ... 20:
打破
案例21 ... 50:
打破
案例51 ... 100:
//匹配,因为80位于51 ... 100之内
默认:
打破
}

原因是~= 模式匹配运算符 。 这个运算符在常规项目中没有看到太多用处(您可能之前已经看到过,因为这个确切的例子是范围内的数字 ),但是在Swift中它在内部被大量使用,这正是用于确认case语句的内容。

在大多数情况下,运算符是用于进行相等性检查的简单包装器(例如Int示例),但是Range在针对自己的关联类型使用~=时,具有特殊的实现,可让其在模式匹配时具有以下自定义行为:

 扩展名RangeExpression { 
@inlinable
公共静态函数〜=(模式:自我,值:绑定)->布尔{
返回pattern.contains(value)
}
}

由于~=在全球范围内可用,因此您可以重载它以编写自己的模式匹配逻辑!

例如,要使"eighty"80匹配,您要做的就是添加一个运算符版本,该运算符将String模式与Int值进行匹配:

  func〜=(模式:字符串,值:整数)->布尔{ 
如果模式==“八十” {
返回值== 80
} else if pattern ==“不是八十” {
返回值!= 80
}其他{
返回假
}
}开关80 {
案例“八十”:
//编译并匹配!
案例“不是八十”:
//
默认:
打破
}

现在,假设我的应用以路径字符串的形式接收了一个深层链接,并且我需要确定该深层链接也属于我的tabBar的UIViewControllers -以AppTab类型的形式:

 枚举AppTab:字符串{ 
案例家
案例顺序历史
案例简介
} let deepLink = DeepLink(path:“ home”,参数:[:])

有多种方法可以执行此操作,包括向深层链接本身添加correspondingTabDeepLink属性或对DeepLink类型进行子类DeepLink ,但是通过自定义模式匹配,可以将属性归为DeepLink而无需触摸DeepLink类型!

  func〜=(模式:AppTab,值:DeepLink)-> Bool { 
返回value.path.hasPrefix(pattern.rawValue)
}切换deepLink {
案例.home:
homeViewController.handle(deepLink:deepLink)
案例.orderHistory:
historyViewController.handle(deepLink:deepLink)
案例.profile:
profileViewController.handle(deepLink:deepLink)
默认:
打破
}

这使您可以不必将更广泛的类型映射到更具体的类型,例如如何将Int映射到WeekDay枚举。 尤其是在这种情况下,您的后端将以IntString形式返回工作日,并且通过自定义模式匹配,您可以使用此更广泛的类型,同时仍将其视为映射到更具体的枚举类型的对象。 当您想尝试概念而又不承诺某些方法或类型时,这可能会很有用:

 枚举WeekDay:Int { 
案例周日
案例星期一
案例周二
案件星期三
案例周四
案件星期五
案件星期六
} func〜=(格式:WeekDay,值:Int)-> Bool {
返回pattern.rawValue ==值
} //服务器返回:// {nextHoliday:{weekDay:5}}如果是大小写。 = nextHoliday?.weekDay {
print(“ Woohoo!”)
}

创建自定义模式是无需花费太多精力即可编写简洁代码的一种简单方法,因为您可以利用cases直接跳到关键点,而不必在类型中添加其他属性-同时确保您的代码不会变得难以理解。

我很想知道您是否曾经超载~=做这样的事情。 如果您在Twitter上有任何建议,评论和反馈,请随时与我联系:@rockthebruno。

Apple Docs:模式


最初发布于 swiftrocks.com