仅使用下标将自定义类型转换为RandomAccessCollection

3

我通常会实现行为类似于数组的类型,比如这个:

struct Dataset: RandomAccessCollection {
    let ids: [Int]
    // Other properties and methods...

    // Boilerplate
    var startIndex: Int { ids.startIndex }
    var endIndex: Int { ids.endIndex }
    func formIndex(after i: inout Int) { i += 1 }
    func formIndex(before i: inout Int) { i -= 1 }

    subscript(index: Int) -> Int {
        // Dummy example, could be more complex and return a different type
        return ids[index]
    }
}

问题在于每次我需要编写很多样板代码以符合RandomAccessCollection的要求。我想要一个协议或机制来将这些样板代码减少到一两个要求:

  • 一个基础的RandomAccessCollection(就像我示例中的ids属性)用于推断协议要求(startIndexendIndexformIndex
  • 一个下标操作符,可以完成其余要求

这种机制类似于目前在Pytorch中进行数据集继承的方式:只有一个__len____getitem__要求。

我提出了一个类似于以下草案:

protocol ArrayProtocol: RandomAccessCollection where Index == BaseCollection.Index {
    associatedtype BaseCollection: RandomAccessCollection
    
    var baseCollection: BaseCollection { get set }
    subscript(index: Index) -> Element { get set }
}

// Provide the default implementation of the RandomAccessCollection protocol
extension ArrayProtocol {
    var startIndex: Index { baseCollection.startIndex }
    var endIndex: Index { baseCollection.endIndex }
    func formIndex(after i: inout Index) { baseCollection.index(after: i) }
    func formIndex(before i: inout Index) { baseCollection.index(before: i) }
}

此协议将会如下使用:

struct Dataset: ArrayProtocol {
    let ids: [Int]
    // Other properties and methods...

    // No more boilerplate
    var baseCollection: [Int] { ids }

    subscript(index: Int) -> Int {
        // Dummy example, could be more complex and return a different type
        return ids[index]
    }
}

但是我找不到使其工作的方法,而且我感觉相关联的类型并不是一个很好的设计模式。

有什么解决办法吗?

编辑: where Index == BaseCollection.Index 子句不是必需的,下标可以具有与底层集合不同的Index类型。


这似乎正常工作,尽管不需要使用typealias BaseCollection = [Int]。谢谢! - Louis Lac
2个回答

2

关联类型一般用于泛型类型(通常是集合的元素)。将关联类型添加到它中typealias BaseCollection = [Int]并删除对set的下标要求。

protocol ArrayProtocol: RandomAccessCollection where Index == BaseCollection.Index {
    associatedtype BaseCollection: RandomAccessCollection
    var baseCollection: BaseCollection { get set }
    subscript(index: Index) -> Element { get }
}

extension ArrayProtocol {
    var startIndex: Index { baseCollection.startIndex }
    var endIndex: Index { baseCollection.endIndex }
    func formIndex(after i: inout Index) { baseCollection.index(after: i) }
    func formIndex(before i: inout Index) { baseCollection.index(before: i) }
}

struct Dataset: ArrayProtocol {
    typealias BaseCollection = [Int]
    var baseCollection: BaseCollection = [ ]
    subscript(index: Int) -> Int { baseCollection[index] }
}

请注意,如果您想保留设置需求subscript(index: Index) -> Element { get set },那么您还需要确保BaseCollection也符合MutableCollection
subscript(index: Int) -> BaseCollection.Element {
    get { baseCollection[index] }
    set { baseCollection[index] = newValue } 
}

0
上述答案中有错误。
func formIndex(after i: inout Index) { baseCollection.index(after: i) }
func formIndex(before i: inout Index) { baseCollection.index(before: i) }

应该是

func formIndex(after i: inout Index) { baseCollection.formIndex(after: &i) }
func formIndex(before i: inout Index) { baseCollection.formIndex(before: &i) }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接