Swift:如何使这个函数通用

时间:2016-01-24 06:37:10

标签: swift generics

这就是我所拥有的:

class func truncateTailsOfRange(range: Range<Int>, portion: Double) -> Range<Int> {
    let start = range.startIndex + Int(portion * Double(range.count))
    let end = range.endIndex - Int(portion * Double(range.count))
    return Range(start: start, end: end)
}

我想为IntegerType制作这个通用:

class func truncateTailsOfRange<T: IntegerType>(range: Range<T>, portion: Double) -> Range<T> {
    let start = range.startIndex + T(portion * Double(range.count))
    let end = range.endIndex - T(portion * Double(range.count))
    return Range(start: start, end: end)
}

但我得到的错误是:

  

无法使用类型(Double)的参数列表调用类型T.Distance的初始值设定项

这可能吗?

2 个答案:

答案 0 :(得分:0)

首先,您需要CustomDoubleConvertible协议。这反映了CustomStringConvertible。您可以将要转换的所有类型扩展为Double。与description类似,返回类型的String表示。

protocol CustomDoubleConvertible {

    var doubleValue : Double { get }
}

extension Int : CustomDoubleConvertible {

    var doubleValue : Double { return Double(self) }

}

extension Int16 : CustomDoubleConvertible {

    var doubleValue : Double { return Double(self) }

}

如果你将该函数作为Range本身的扩展,你可以利用它的通用性,它是typealiases

extension Range where Element.Distance : CustomDoubleConvertible {

现在您可以计算索引的偏移量,如下所示:

let startOffset = Int(portion * self.count.doubleValue)
let endOffset = Int(portion * self.count.doubleValue)

如果您进一步约束Range以使其Element必须为BidirectionalIndexType,则可以使用successorpredecessor

extension Range where Element.Distance : CustomDoubleConvertible, Element : BidirectionalIndexType {

这使您可以通过迭代偏移量并调用successorpredecessor来获得完整功能。

extension Range where Element.Distance : CustomDoubleConvertible, Element : BidirectionalIndexType {

    func truncateTailsOfRange(portion: Double) -> Range<Element> {

        let startOffset = Int(portion * self.count.doubleValue)
        let endOffset = Int(portion * self.count.doubleValue)

        var start = self.startIndex
        var end = self.endIndex

        for _ in 0..<startOffset { start = start.successor() }
        for _ in 0..<endOffset { end = end.predecessor() }

        return Range(start: start, end: end)
    }
}

一些测试:

let rangeA = 1...4 // 1..<5

let rangeB = "a"..."g"

rangeA.truncateTailsOfRange(0.3) // 2..<4

rangeB.truncateTailsOfRange(0.3) // has no member ....



let w : Int16 = 3
let z : Int16 = 9

let rangeC = w...z // 3..<10
rangeC.truncateTailsOfRange(0.4) // 5..<8

答案 1 :(得分:0)

这是一项有趣但学术性的练习。

这是一种效率不高但可以适用于所有范围类型的方法:

 func truncateTailsOfRange<T>(var range: Range<T>, portion: Double) -> Range<T> 
 {
     let elementCount    = Array(range).count
     let truncationCount = Int( portion * Double(elementCount) ) 
     let remainingCount  = max(0, elementCount - 2 * truncationCount)

     for _ in 0..<truncationCount 
     { range.startIndex = range.startIndex.successor() }

     range.endIndex = range.startIndex

     for _ in 0..<remainingCount
     { range.endIndex = range.endIndex.successor() }

     return range
 }

这是一个更快的一个:

 func truncateTailsOfRange2<T>(var range: Range<T>, portion: Double) -> Range<T> 
 {
     if range.isEmpty {return range}
     let elements = Array(range)
     let truncationCount = Int( portion * Double(elements.count) )
     let remainingCount  = max(0, elements.count - 2 * truncationCount)
     return elements[truncationCount]..<elements[truncationCount+remainingCount]
 }