【发布时间】:2017-06-15 03:43:53
【问题描述】:
我有以下 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 的任何集合。
尝试通用实现
我尝试了很多方式来表达这一点,但我还没有找到让它编译的方法。
尝试 1
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 变得混乱。)
尝试 2
extension RandomAccessCollection where Indices: CountableRange {
// Implementation omitted.
}
给出错误:
对泛型“CountableRange”的引用需要 <...>
中的参数
尝试 3
extension RandomAccessCollection where Indices: CountableRange<Int> {
// Implementation omitted.
}
给出错误:
类型“Indices”被限制为非协议类型“CountableRange”
背景
这是上面省略的实现groupRanges(withEqualTest:) 的CountableRange 的扩展。该算法、它的作用以及它的大 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
}
}
【问题讨论】:
-
是否可以选择实现 RandomAccessCollection 而不是 CountableRange 的代码? – 然后您可以轻松地将调用从任意 RandomAccessCollection 转发到其索引。
-
@MartinR 谢谢——我已经在这个问题中添加了更多内容。