我有以下Swift代码:
extension Array {
typealias EqualTest = (Iterator.Element, Iterator.Element) -> Bool
func groupSplitIndices(withEqualTest equal: EqualTest) -> [Index] {
return indices.groupSplitIndices(withEqualTest: {equal(self[$0], self[$1])})
}
}
extension ArraySlice {
typealias EqualTest = (Iterator.Element, Iterator.Element) -> Bool
func groupSplitIndices(withEqualTest equal: EqualTest) -> [Index] {
return indices.groupSplitIndices(withEqualTest: {equal(self[$0], self[$1])})
}
}
extension CountableRange {
typealias EqualTest = (Element, Element) -> Bool
func groupSplitIndices(withEqualTest equal: EqualTest) -> [Element] {
// Implementation omitted here.
// For details see "Background" at the end of the question.
}
}
不是用相同的代码扩展Array
和ArraySlice
,而是我可以扩展一个可以达到相同结果的协议吗?
基本上,我想扩展关联类型Indices
为CountableRange
的任何集合。
我试图以多种方式表达这一点,但我还没有找到一种方法来编译它。
extension RandomAccessCollection {
typealias EqualTest = (Iterator.Element, Iterator.Element) -> Bool
func groupSplitIndices(withEqualTest equal: EqualTest) -> [Index] {
// Error on next line…
return indices.groupSplitIndices(withEqualTest: {equal(self[$0], self[$1])})
}
}
此尝试产生2个错误:
'Self.Indices'类型的值没有成员'groupSplitIndices'
关闭使用非转义参数'equal'可能允许它转义
(我认为第二个错误是Swift感到困惑。)
extension RandomAccessCollection where Indices: CountableRange {
// Implementation omitted.
}
给出错误:
对泛型类型'CountableRange'的引用需要< ...>
中的参数
extension RandomAccessCollection where Indices: CountableRange<Int> {
// Implementation omitted.
}
给出错误:
键入'指标'约束为非协议类型'CountableRange'
以上是CountableRange
实施groupRanges(withEqualTest:)
的扩展名,上面省略了该扩展名。讨论了算法,它做了什么,以及它的大O成本in this question。
我试图实现与RandomAccessCollection
的扩展类似的东西,但没有太多运气。
extension CountableRange {
typealias EqualTest = (Element, Element) -> Bool
func groupRanges(withEqualTest equal:EqualTest) -> [CountableRange] {
let groupIndices = groupSplitIndices(withEqualTest: equal)
return groupIndices.indices.dropLast().map {groupIndices[$0]..<groupIndices[$0+1]}
}
func groupSplitIndices(withEqualTest equal: EqualTest) -> [Element] {
var allIndexes = [lowerBound]
allIndexes.append(contentsOf: interiorGroupSplitIndices(withEqualTest: equal))
allIndexes.append(upperBound)
return allIndexes
}
func interiorGroupSplitIndices(withEqualTest equal: EqualTest) -> [Element] {
var result = Array<Element>()
var toDo = [self]
while toDo.count > 0 {
let range = toDo.removeLast()
guard
let firstElement = range.first,
let lastElement = range.last,
firstElement != lastElement,
!equal(firstElement, lastElement) else {
continue;
}
switch range.count {
case 2:
result.append(lastElement)
default:
let midIndex = index(firstElement, offsetBy: range.count/2)
toDo.append(range.suffix(from: midIndex))
toDo.append(range.prefix(through: midIndex))
}
}
return result
}
}
答案 0 :(得分:1)
要拨打indices.groupSplitIndices()
,您需要约束
集合扩展上的Indices == CountableRange<Index>
,
这需要Index
为Strideable
:
extension RandomAccessCollection where Index: Strideable, Indices == CountableRange<Index> {
typealias EqualTest = (Iterator.Element, Iterator.Element) -> Bool
func groupSplitIndices(withEqualTest equal: EqualTest) -> [Index] {
return indices.groupSplitIndices(withEqualTest: {
equal(self[$0], self[$1])
})
}
}
extension CountableRange {
typealias EqualTest = (Element, Element) -> Bool
func groupSplitIndices(withEqualTest equal: EqualTest) -> [Element] {
// Dummy implementation:
return []
}
}
这实际上是使用Swift 4(Xcode 9 beta或Xcode 8.3.3和Swift 4工具链)编译的。
但是有一个问题:Xcode 8.3.3中的Swift 3编译器在编译时会崩溃 上面的代码用&#34; Debug&#34;组态。这似乎是一个编译器 bug,因为它在&#34; Release&#34;中编译没有问题。组态, 以及Xcode 9或Xcode 8.3.2以及Swift 4工具链。
以下是我如何弄清楚上述解决方案的粗略描述。 让我们从您的&#34;尝试3&#34;:
开始extension RandomAccessCollection where Indices: CountableRange<Int>
// error: type 'Indices' constrained to non-protocol type 'CountableRange<Int>
Indices
不能是采用CountableRange<Int>
的子类或类型,这意味着我们需要相同类型的要求:
extension RandomAccessCollection where Indices == CountableRange<Int>
这导致
// error: cannot subscript a value of type 'Self' with an index of type 'Int'
在self[$0]
和self[$1]
。 subscript
Collection
方法Self.Index
采用extension RandomAccessCollection where Indices == CountableRange<Index>
// error: type 'Self.Index' does not conform to protocol '_Strideable'
参数,因此我们将其更改为
Index
所以Strideable
必须是extension RandomAccessCollection where Index: Strideable, Indices == CountableRange<Index>
:
{{1}}
那就是它!