如何在Swift中实现CollectionOf

时间:2014-11-22 20:24:34

标签: generics swift

查看this question,我试图为名为CollectionType的{​​{1}}实施等效内容,但我遇到了其中一个限制问题:

CollectionOf

这不编译。让我们忽略这样一个事实:我还没有实现struct CollectionOf<T, Index: ForwardIndexType>: CollectionType { init<C: CollectionType where C.Generator.Element == T, C.Index == Index>(_ collection: C) { // etc. } // etc. } 的成员。我会接受这个。我面临的问题是编译器不喜欢我的CollectionType。它抱怨,&#34;相同类型约束索引不符合所需协议ForwardIndexType。&#34;显然它确实如此。

也许我们有编译错误。也许我的语法错了。无论如何,我如何实施init

修改

这是一个有效的实施方案。由于上面提到的问题,它存在严重的缺陷,CollectionOf Index typealias不限于与包裹的CollectionType相同。这意味着我被迫进行强制转换,这在理论上可能会在运行时而不是在编译时引起问题,这会破坏静态类型的目的。

CollectionType

3 个答案:

答案 0 :(得分:3)

无论出于何种原因,看起来编译器没有在CollectionType内查找其索引已经是ForwardIndex。如果将C.Index: ForwardIndexType添加到泛型约束的where子句中,编译器就会停止窃听你。


这是一种实现CollectionOf<T>的方法,其索引始终为Int。这里的一个缺点是,一些操作,即查找endIndex和下标,可能是 O N )而不是 O ( 1)。如果传入的集合具有类型Int的索引,则所有操作都将是 O (1)。

struct CollectionOf<T>: CollectionType {
    typealias Index = Int

    private let _generate: () -> GeneratorOf<T>
    private let _subscript: (Int) -> T
    private let _endIndex: () -> Int

    init<C: CollectionType where C.Generator.Element == T>(_ collection: C) {
        _generate = { GeneratorOf(collection.generate()) }
        _subscript = {
            (i: Int) in
            if C.Index.self is Int.Type {
                return collection[((collection.startIndex as Int) + i) as C.Index]
            } else {
                let index = reduce(0..<i, collection.startIndex) { $0.0.successor() }
                return collection[index]
            }
        }
        _endIndex = {
            if C.Index.Distance.self is Int.Type {
                return distance(collection.startIndex, collection.endIndex) as Int
            } else {
                return reduce(collection.startIndex..<collection.endIndex, 0) { $0.0 + 1 }
            }
        }
    }

    var startIndex: Int {
        return 0
    }

    var endIndex: Int {
        return _endIndex()
    }

    func generate() -> GeneratorOf<T> {
        return _generate()
    }

    subscript (i: Int) -> T {
        return _subscript(i)
    }
}

答案 1 :(得分:2)

CollectionType.Index.Distance用作CollectionOf<T>.Index可以简化事情:

struct CollectionOf<T>:CollectionType {
    private let _generate: () -> GeneratorOf<T>
    private let _endIndex: () -> Int
    private let _subscript: (Int) -> T

    init<C: CollectionType where C.Generator.Element == T, C.Index.Distance == Int>(_ base: C) {
        _generate = { GeneratorOf(base.generate()) }
        _endIndex = { distance(base.startIndex, base.endIndex) }
        _subscript = { base[advance(base.startIndex, $0)] }
    }

    var startIndex: Int { return 0 }
    var endIndex: Int { return _endIndex() }
    subscript (i: Int) -> T { return _subscript(i) }
    func generate() -> GeneratorOf<T> { return _generate() }
}

此约束C.Index.Distance == Int,但据我所知,内置Index.Distance的所有CollectionType均为Int。所以这里没有实际缺陷。

如果您确实要接受所有CollectionType,请使用IntMax

struct CollectionOf<T>:CollectionType {
    private let _generate: () -> GeneratorOf<T>
    private let _endIndex: () -> IntMax
    private let _subscript: (IntMax) -> T

    init<C: CollectionType where C.Generator.Element == T>(_ base: C) {
        _generate = { GeneratorOf(base.generate()) }
        _endIndex = { distance(base.startIndex, base.endIndex).toIntMax() }
        _subscript = { base[advance(base.startIndex, C.Index.Distance($0))] }
    }

    var startIndex: IntMax { return 0 }
    var endIndex: IntMax { return _endIndex() }
    subscript (i: IntMax) -> T { return _subscript(i) }
    func generate() -> GeneratorOf<T> { return _generate() }
}

这是有效的,因为CollectionType.Index.Distance_SignedIntegerType_SignedIntegerTypetoIntMax()init(_: IntMax)

答案 2 :(得分:1)

更新

这个答案在技术上仍然是正确的。 Nate Cook有一个聪明的解决方法,我决定将其标记为正确。

原始答案

经过大量搜索后发现,截至本文撰写时,我认为无法创建通用类型安全CollectionOf。问题可以归结为:

protocol AwesomeType {
    typealias Crazy: ForwardIndexType
}

struct AwesomeOf<C: ForwardIndexType> {
    init<A: AwesomeType where A.Crazy == C>(_ awesome: A) {
    }
}

我们试图在初始化程序中告诉编译器,关联类型Crazy(必须符合ForwardIndexType协议)与我们的类级泛型类型参数{{ 1}},已根据需要声明符合C

不幸的是,这不起作用。在方法级泛型约束中,编译器将类级泛型参数视为不受约束,从而产生错误。

我做了很多测试,发现没有什么能与这个假设相矛盾。如果有人知道如何解决这个问题,我很想知道。

这意味着不幸的是,传递类型安全(但不是通用的)ForwardIndexType的唯一方法是泄漏类型信息,如下所示:

CollectionOf

问题在于它不够通用。它泄漏了底层集合的类型,例如,我们必须说class CollectionOf<C: CollectionType>: CollectionType { init(_ collection: C) { } } 而不是CollectionOf<Int, Int>。这是一个真正的耻辱,我希望它在未来的Swift版本中得到修复。