原标题:更多模式匹配的例子
本文由CocoaChina译者ztdj121(微博)翻译
作者:Ole Begemann
原文:More Pattern Matching Examples
译文地址
这个系列的其他文章:
1、自定义模式匹配
2、范围和间隔(Ranges and Intervals)
3、多模式匹配的例子(这篇)
Download this article as a playground for Xcode 7.
在模式匹配这个小系列的最后部分,我将给你们展示更多的例子,来说明用这个我们可以做些什么。
概述
在第一部分里,我们用一个变量重载了模式匹配的运载符 ~= , 这个变量用T->Bool类型函数作为它的第一个参数:
func~=(pattern:T->Bool,value:T)->Bool{ returnpattern(value) }
这个实现非常通用。我们可以用它来使任何类型T的值和任何接受T并返回Bool的方法相匹配(就像第一部分里的isEven例子一样)。
当我们尝试用String.hasPrefix方法的模式匹配形式时,我们会遇到一个问题,就是参数的顺序。记住实例方法是把实例作为第一个参数的柯里化方法(curried function)。所以String.prefix方法的类型是String -> String -> Bool,String的第一个参数是方法接收器,第二个参数是我们想匹配的前缀:
//This: "HelloWorld".hasPrefix("H")//true //isequivalenttothis: String.hasPrefix("HelloWorld")("H")//true
参数顺序正是后面我们使用有模式匹配操作符的部分应用版本的方法中所需要的。我们需要一个版本,让接收方法的变量在最后(除非你想使一个前缀匹配多个字符串,这种情况下,顺序是正确地,但是方法名会混淆)。第一部分里,我们写了一个翻转参数的小的辅助方法:
funchasPrefix(prefix:String)(_value:String)->Bool{ returnvalue.hasPrefix(prefix) } //Nowwecancallitwiththeargumentsflipped: hasPrefix("H")("HelloWorld")//true
这样,我们可以部分应用前缀并返回一个方法,我们可以在~=中把这个方法当作pattern参数。
一个通用的解决方法
有作用了,但是我们把想使用的每个方法都这样做得话,很快就会变得冗长乏味。所以让我们来写一个通用的方法,翻转,把柯里化方法(curried function)的第一个参数移到后面,最后返回值的前面。
///Movesthefirstargumenttotheback funcflip(method:A->B->C)->(B->A->C){ return{(b:B)in {(a:A)inmethod(a)(b)} } }
我喜欢这个方法的类型签名,因为这个类型非常清晰地显示它所做的:只有一个类型方法(A -> B -> C) -> (B -> A -> C)可能实现的函数类型。尽管两个嵌套的闭合表达式的函数体可能解析起来有点困难。写这个函数的另一个方法是利用Swift里柯里化函数(curried function)的特殊语法功能。这个变量使函数体非常容易写,但在我看来类型签名有点难以理解:
funcflip(method:A->B->C)(_b:B)(_a:A)->C{ returnmethod(a)(b) }
现在我们可以像这样使字符串和不同的前缀相匹配:
letstr="ABCDEF" switchstr{ caseflip(String.hasPrefix)("A"): print("A") caseflip(String.hasPrefix)("B"): print("B") default: "default" }
由于翻转(flip)是通用的,它适用于所有类型的所有方法–即使是那些有不同数量的参数。最后一点可能会令人惊讶,但是一个(非柯里化)需要多个参数的方法实际只需要一个参数:一个有相应数量的元素的元组有一样的类型作为参数。这并非偶然,元组的语法(a,b)和方法参数的语法是一样的,f(a,b)–他们是一样的东西。这就意味着任何匹配泛型类型A->B->C是我们翻转方法预料的,不管参数元组(泛型类型B代表)包含多少元素。
例子
匹配数组
翻转(flip)甚至适用于没有参数的方法(除了隐式接收器参数),因为空元组()也是一个有效的类型。不过,我们不能直接使用翻转(flip)属性,因为Swift目前不允许指定一个类型的属性作为一个方法。我们需要写一个独立的封装器方法,就像例子中collection的isEmpty属性:
extensionCollectionType{ funcisEmptyFunc()->Bool{ returnisEmpty } }
这有一个我们对数字数组进行模式匹配的完整的示例。除了contains 方法,这个方法包含在标准库里,我们使用一个带两个参数的自定义的contains的重载方法,检查队列中是否包含两者。
extensionSequenceTypewhereGenerator.Element:Equatable{ funccontains(a:Self.Generator.Element,andb:Self.Generator.Element)->Bool{ returncontains(a)&&contains(b) } } letnumbers=[1,2,3,4,5,6,7,8,9] switchnumbers{ caseflip(Array.isEmptyFunc)(): print("isempty") caseflip(Array.contains)(10): print("contains10") caseflip(Array.contains)(2,4): print("contains2and4") caseflip(Array.contains)(5): print("contains5") default: print("default") }
匹配CGRects
又如匹配两个CGRect怎么样?
importCoreGraphics letrect1=CGRect(x:20,y:20,width:50,height:50) letrect2=CGRect(x:40,y:40,width:100,height:100) switchrect1{ caseflip(CGRect.contains)(rect2): "contains" caseflip(CGRect.intersects)(rect2): "intersects" default: "default" }
匹配Sets
最后一个例子,比较两个集合。这里我们使用另一个辅助函数,而非否定一个模式。
funcnot(f:T->Bool)->T->Bool{ return{!f($0)} } letset1:Set=[1,2,3,4,5,6,7,8,9] letset2:Set=[3,4,5] switchset1{ caseflip(Set.contains)(10): "contains10" casenot(flip(Set.isSupersetOf)(set2)): "isnotasupersetof(set2)" caseflip(Set.isSupersetOf)(set2): "issupersetof(set2)" caseflip(Set.isDisjointWith)(set2): "isdisjointwith(set2)" default: "default" }
总结
再次声明我不是要你把代码写成这样。尽管我认为我们开发的模式极其简洁,语法很短。上面的许多例子在我看来很丑,比另一种(一堆if语句)更难读懂。
这个系列的目标是鼓励你们(和我)开始用方法来考虑编程问题。通过把方法当成可以传递的值,然后在另外的方法中返回,并通过把多个简单的方法组合成一个更复杂的方法,我们可以用一些简单的块构建非常通用的系统。
相关:
要记住,当一扇门在你面前关闭的时候,另一扇门就会打开。肯·汤普森(Ken Thompson) 和丹尼斯&midd
Nota 是一款非常轻便的应用,大小只有 285 KB,只有 Android 版本。 由于非常小,所以能做的事情也有限,区
“维多利亚的秘密时尚秀”将在北京时间 12 月 9 日上午 11:00全球同步直播,今年腾讯向CBS(哥伦比亚广播公
本周一,瑞士 Swatch 集团宣布和 Visa 支付系统达成合作,推出非接触式支付服务。 10 月时,我们就曾报道过
看完回答有种莫名的心酸!!【免责声明:本文自互联网收集而来,方便大家阅读,版权归原创者所有,如果侵犯
如果你必须从这9款发型中选出一款顶在头上,你会选择……..?
你会选择……..?【免责声明:本文自互联网收集而来,方便大家阅读,版权归原创者所有,如果侵犯了
一个女人向男票借5w……….【免责声明:本文自互联网收集而来,方便大家阅读,版权归原创者所有,如果
≈日【免责声明:本文自互联网收集而来,方便大家阅读,版权归原创者所有,如果侵犯了你的权益,请通知我们
请升级至最新版本阅读此文进入“设置–版本”中检查最新版本并升级【免责声明:本文自互联网收集而来,
我们通过K线走势变化来观察价格趋势的方向发展,用MACD指标柱状线红柱和绿柱的收缩来研判行情。在持续的