2016-01-24 7 views
0

Вот что у меня есть:Swift: Как сделать эту функцию родовое

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)

Это можно сделать?

+0

Вам нужен диапазон для всех целочисленных типов ? Было бы намного проще создать метод для каждого из них, который вам нужен. –

+0

Мне нужен только Int, но мотивация была просто в том, что нет оснований ограничивать это ints. –

+0

Вы пытаетесь вызвать конструктор на T, но T не является фактическим классом/struct/enum, а скорее просто «типом» «. – davecom

ответ

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 вы можете использовать successor и predecessor.

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

Это позволяет получить полную функцию перебора смещений и вызова successor и predecessor.

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 
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] 
} 
Смежные вопросы