我们这几天一直在探究集合在泛型化时所遇到的问题。在另外一个内容集合索引与泛型中,我们讨论的是在集合泛型化处理的时候如何处理集合索引indices.这一次我们来看一下如何处理subsequence的问题。我们首先看一个需求。
在一个 集合中定位另一个集合元素第一次出现的位置
我们熟悉indexOf方法,用来定位一个集合元素第一次出现的位置,我们现在的需求是给集合类型做一个扩展,增加一个方法用来定位一个subsequence第一次在集合中出现的位置,例如:"or"第一次在“Hello,world"中出现的位置。
思考一下,问题好像并不难解决,只要集合中的元素可以进行比较相等就可以了,也就是说准守Equatable协议就可以了,所以我们尝试着写出下面这个代码片段。
extension CollectionType where Generator.Element: Equatable {
func searchRange<Other: SequenceType where
Other.Generator.Element == Generator.Element>(pattern: Other) -> Index? {
for idx in self.indices {
if suffixFrom(idx).startsWith(pattern) {
return idx
}
}
return nil
}
}
这个时候我们会收到编译器提示的错误,抱怨我们的startsWith方法没有提供参数 isEquivalent ,我们经过翻看SequenceType的定义,我们会发现,实际上存在两个startsWith方法分别是:
- startsWith(other: OtherSequence)
- startsWith(other: OtherSequence,isEquivalent: {})
这也就是表明,我们的调用并没有什么不对,其实在这个时候就算我们迁就一下编译器,将方法调用改成
if suffixFrom(idx).startsWith(pattern,isEquivalent: {_,_ in true}) {
return idx
}
即使这样,编译器也不会放过你的,它会继续提示你 OtherSequence 的类型无法推断。
问题的真正原因
** 其实问题的根本原因是出在了,我们虽然在泛型方法中通过约束条件Other.Generator.Element == Generator.Element,约束了两个集合的元素类型相同,但suffixFrom(idx)方法返回的是一个subsequence,这个subsequence的类型和Self的类型可不一定相同,而我们现在是用subsequence与Other类型进行对比,因此,方法中的泛型约束已经失效了。**
要解决这个问题我们可以这样做,增加一个泛型约束,另subsequence == Self.....
extension CollectionType where Generator.Element: Equatable,
SubSequence == Self{
func searchRange<Other: SequenceType where
Other.Generator.Element == Generator.Element>(pattern: Other) -> Index? {
for idx in self.indices {
if suffixFrom(idx).startsWith(pattern) {
return idx
}
}
return nil
}
}
通过 SubSequence == Self ,我们保证了Other.Generator.Element == Generator.Element>约束的有效。我们现在可以这样使用searchRange方法了
"Hello,world".characters.searchRange("or".characters) // 7
进一步扩大适用范围
searchRange 虽然可以工作了,但是它只能用于像String这种,subsquence与self类型相等的情况,而对于数组之类的集合,subsquence与self一定是不相等的,那应该怎么办呢?sequence
其实我们并不是真的需要两个sequence类型相同,我们需要的只是集合内的元素类型相同就可以了。我们再调整一下代码。
extension CollectionType where Generator.Element: Equatable,
SubSequence.Generator.Element == Generator.Element{
func searchRange<Other: SequenceType where
Other.Generator.Element == Generator.Element>(pattern: Other) -> Index? {
for idx in self.indices {
if suffixFrom(idx).startsWith(pattern) {
return idx
}
}
return nil
}
}
通过 SubSequence.Generator.Element == Generator.Element 约束条件,我们把searchRange的方法扩大到了所有subsequence的元素与self中的元素类型相同的集合类型了。
总结
八条8tiao没有总结。