作者:Olivier Halligon,原文链接,原文日期:2015-04-24
译者:walkingway;校对:小锅;定稿:numbbbbb
在模式匹配系列文章的第一弹和第二弹中,我们已经看到关于 switch 搭配很多类型的用法,包括元组(tuples
),范围(Range
),字符串(String
),符号(Character
)和一些其他类型。但是假如我们使用自定义的类型和模式匹配,又能擦出怎样的火花呢?
Switch 和模式匹配操作符
如果你在 switch
实例中这样写 case 1900..<2000
,那么 Swift 如何比较 switch 入口的单值与下面的范围?
答案非常简单:Swift 使用了 ~=
操作符。当你在 case 中使用 Range<I>
时,switch 可以对 I
进行匹配,这是因为 Range<I>
与 I
二者之间定义了 ~=
操作符:
func ~=<I : ForwardIndexType where I : Comparable>(pattern: Range<I>,value: I) -> Bool
事实上,如果你写 switch someI
并加上 case aRangeOfI
语句时,Swift 会尝试调用 aRangeOfI ~= someI
来做匹配操作(该表达式会返回一个 Bool 来通知是否匹配成功)
这就意味着你可以为自己的类型定义相同的操作符 ~=
,这样就能保证这些自定义类型可以在 switch/case
语句中使用,我们也可以用相同的方式使用 Range
!
让你的自定义类型响应模式匹配
我们构造一个自定义的结构体:
struct Affine { var a: Int var b: Int } func ~= (lhs: Affine,rhs: Int) -> Bool { return rhs % lhs.a == lhs.b } switch 5 { case Affine(a: 2,b: 0): print("Even number") case Affine(a: 3,b: 1): print("3x+1") case Affine(a: 3,b: 2): print("3x+2") default: print("Other") }
最终打印的结果是 3x+2
!
值得注意的一点是:在使用自定义类型时,Swift 不知道 switch 是否穷尽了所有可能。例如,即使我们添加了 case Affine(a: 2,b: 1)
和 case Affine(a: 2,b: -1)
这两个子句,来覆盖到每一个正整数和负整数的情况,Swift 还是会强迫我们使用 default:
语句。
此外需要注意,不要搞混了参数的顺序:~=
的第一个参数(通常称为 lhs
,译指左手边)是你将要在 case
子句中使用的对象,第二个参数(通常称为 rhs
,译指右手边)是你使用 switch 传入的对象。
~= 的一些其他用途
~=
还有很多其他用途。
例如,我们可以在第二弹中登场的 Book
结构体中添加如下代码:
func ~= (lhs: Range<Int>,rhs: Book) -> Bool { return lhs ~= rhs.year }
现在测试一下:
let aBook = Book(title: "20,000 leagues under the sea",author: "Jules Vernes",year: 1870) switch aBook { case 1800..<1900: print("19th century book") case 1900..<2000: print("20th century book") default: print("Other century") }
当然我不鼓励这样使用,将 Book 直接与一段整数范围比较并不能很清晰地展现我们的意图:『其实是想与 book 的出版年份进行比较』。更好的方式是在 switch 中直接传入 aBook.year
。但我们举这个例子只是为了展示 ~=
操作符的强大(另一个原因是我暂时想不到更好的例子了