实现`Array`& `ArraySlice`扩展名,而不是协议

时间:2017-06-06 14:42:22

标签: swift generics

我有以下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.
  }
}

不是用相同的代码扩展ArrayArraySlice,而是我可以扩展一个可以达到相同结果的协议吗?

基本上,我想扩展关联类型IndicesCountableRange的任何集合。

尝试通用实现

我试图以多种方式表达这一点,但我还没有找到一种方法来编译它。

尝试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.
}

给出错误:

  

键入'指标'约束为非协议类型'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
  }
}

1 个答案:

答案 0 :(得分:1)

要拨打indices.groupSplitIndices(),您需要约束
集合扩展上的Indices == CountableRange<Index>, 这需要IndexStrideable

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}}

那就是它!