在Swift里的自定义模式匹配(译)
原文地址
模式匹配在swift里是随处可见的,虽然switch case
是匹配模式最常见的用法,但是Swift有多种类型的模式,这些模式可以混合甚至在switches外部使用,从而写出真正酷而短的代码。我特别感兴趣的事模式匹配可以用作于各种各样的事情。
乍一看下面的代码,很容易把模式匹配看做是简单的等值检查
switch 80 {
case 100:
//
case 80:
//匹配, 因为 80 == 80
default:
break
}
鉴于下面的代码这种情况,你可能会认为像 case "eighty"
这样的将不会被编译,毕竟"eighty"和80不是相同的类型,如果你现在尝试一下的确会编译不通过
if case "eighty" = 80 {
//error: expression pattern of type 'String' cannot match values of type 'Int'
}
但事实并非如此,在项目开发中,你可能已经注意到某些类型之间存在着特殊的交互,例如Ranges
它们及其相关类型
switch 80 {
case 0...20:
break
case 21...50:
break
case 51...100:
//匹配, 因为 80 在 51...100 之间
default:
break
}
究其原因是~=
这个匹配模式运算符,这个操作符看起来在常规项目里没什么用(你之前可能已经看到过这个确切的数字范围内的例子),但是他在swift内部使用很多,而且他是用于确认case语句
很多情况下,这个运算符是一个简单的等值检查的包装器(例如Int),但是Range有一个特殊的实现,当针对其自己的关联类型使用时,允许他在模式匹配时具有自定义的行为
extension RangeExpression {
@inlinable
public static func ~= (pattern: Self, value: Bound) -> Bool {
return pattern.contains(value)
}
}
并且由于~=
的作用域是全局的,为了编写自己的模式匹配的逻辑,你可以重载它。
例如,要让"eighty"匹配80,你需要做的是,重载一个~=
,使它将String模式和Int值匹配
func ~= (pattern: String, value: Int) -> Bool {
if pattern == "eighty" {
return value == 80
} else if pattern == "not eighty" {
return value != 80
} else {
return false
}
}
switch 80 {
case "eighty":
//编译通过并且匹配
case "not eighty":
//
default:
break
}
现在假设我的APP收到了字符串形式的深层链接,我需要知道这个深层链接属于哪一个控制器
enum AppTab: String {
case home
case orderHistory
case profile
}
let deepLink = DeepLink(path: "home", parameters: [:])
这里有很多种方法来做这到这个功能,包括向DeepLink添加属性,如correspondingTab或者对DeepLink进行子类化,也可以使用自定义模式匹配,只需要一行代码而且无需修改DeepLink。
func ~= (pattern: AppTab, value: DeepLink) -> Bool {
return value.path.hasPrefix(pattern.rawValue)
}
switch deepLink {
case .home:
homeViewController.handle(deepLink: deepLink)
case .orderHistory:
historyViewController.handle(deepLink: deepLink)
case .profile:
profileViewController.handle(deepLink: deepLink)
default:
break
}
这允许你可以绕过将更广泛的类型映射到更具体的类型,例如如何将Int映射到工作日枚举。在这种情况下,您的后端将返回一周作为一个Int或一个String,并使用自定义模式匹配,您可以使用这种更广泛的类型,同时仍然将其视为映射到更特定的枚举类型。当您想尝试使用而不想引用某些方法或类型时,这可能是有用的:
enum WeekDay: Int {
case sunday
case monday
case tuesday
case wednesday
case thursday
case friday
case saturday
}
func ~= (pattern: WeekDay, value: Int) -> Bool {
return pattern.rawValue == value
}
// Server returns:
// { nextHoliday: { weekDay: 5 } }
if case .friday? = nextHoliday?.weekDay {
print("Woohoo!")
}
创建自定义模式是编写更干净代码的一种简单方法,不需要太多的努力,因为您可以通过cases直接跳到这一点而无需为类型添加其他属性 - 同时确保您的代码不会变得难以理解。
我很想知道你是否做过类似重载过~=
这样的事情,如果你有任何建议,意见和反馈,请随时与我联系,@rockthebruno