2014-11-22 4 views
3

Глядя на this question, я пытаюсь реализовать эквивалент CollectionType под названием CollectionOf, но у меня возникают проблемы с одним из ограничений:Как реализовать CollectionOf в Swift

struct CollectionOf<T, Index: ForwardIndexType>: CollectionType { 
    init<C: CollectionType where C.Generator.Element == T, C.Index == Index>(_ collection: C) { 
     // etc. 
    } 
    // etc. 
} 

Это не компилируется. Давайте проигнорируем тот факт, что я еще не реализовал членов CollectionType. Я доберусь до этого. Проблема, с которой я сталкиваюсь, заключается в том, что компилятор мне не нравится init. Он жалуется: «Индекс ограничения одного типа не соответствует требуемому протоколу ForwardIndexType». Ясно, что так оно и есть.

Возможно, у нас есть ошибка компилятора. Возможно, мой синтаксис неверен. Как бы то ни было, как я могу реализовать CollectionOf?

EDIT

Настоящая рабочая реализация. Из-за проблемы, отмеченной выше, он страдает серьезным недостатком, что Index typealias от CollectionType не ограничен таким же, как у обернутого CollectionType. Это означает, что я вынужден бросить, что теоретически может вызвать проблему во время выполнения, а не во время компиляции, что наносит ущерб цели статической типизации.

struct CollectionOf<T, I: ForwardIndexType>: CollectionType { 
    private let _generate:() -> GeneratorOf<T> 
    private let _startIndex:() -> I 
    private let _endIndex:() -> I 
    private let _subscript: (I) -> T 

    init<C: CollectionType where C.Generator.Element == T>(_ collection: C) { 
     _generate = { GeneratorOf(collection.generate()) } 
     _startIndex = { collection.startIndex as I } 
     _endIndex = { collection.endIndex as I } 
     _subscript = { i in collection[i as C.Index] } 
    } 

    var startIndex: I { 
     return _startIndex() 
    } 

    var endIndex: I { 
     return _endIndex() 
    } 

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

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

ответ

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) 
    } 
} 
+0

Не могли бы вы дать мне пример? Когда я пытаюсь это сделать, компилятор все еще жалуется на ограничение одного типа, то есть 'init ' все еще делает не компилировать. –

+0

Этот код работал для меня. Вы реализовали остальную часть CollectionType? Я обнаружил, что компилятор дает забавные сообщения об ошибках, когда протоколы не полностью соответствуют. –

+0

Нет кубиков. Если вы посмотрите на код, который я разместил выше в моем редактировании, и измените подпись 'init', как указано в моем комментарии выше, он не компилируется. Такая же ошибка. –

1

Update

Этот ответ еще технически правильно. У Нейта Кука обходной путь настолько умный, что я решил отметить его правильно.

Оригинал ответа

После долгих поисков, оказывается, что, как это письмо, я считаю, что это не возможно создать обобщенный типизированного CollectionOf. Проблема может быть сводилась таким образом:

protocol AwesomeType { 
    typealias Crazy: ForwardIndexType 
} 

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

Что мы пытаемся сказать компилятору в нашем инициализаторе является то, что соответствующий тип Crazy (который должен соответствовать протоколу ForwardIndexType) точно соответствует нашему классу уровня общего тип C, который был объявлен соответствующим ForwardIndexType по мере необходимости.

К сожалению, это просто не работает. Внутри обобщенного ограничения на уровне метода компилятор обрабатывает общие параметры на уровне класса, как если бы они были без ограничений, что приводило к ошибке.

Я провел много испытаний и не нашел ничего, что противоречило бы этому предположению. Если кто-нибудь знает об этом, я бы с удовольствием узнал.

Это означает, что единственный способ сделать типизированный (но не обобщенное) CollectionOf, к сожалению утечки информации о типе, а именно:

class CollectionOf<C: CollectionType>: CollectionType { 
    init(_ collection: C) { 
    } 
} 

Проблема с этим состоит в том, что она не является достаточно общей. Он просачивает тип базовой коллекции, например, вместо CollectionOf<Int, Int> мы должны сказать CollectionOf<Array<Int>>. Это настоящий позор, и я надеюсь, что это исправлено в будущей версии Swift.

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 и _SignedIntegerType имеет toIntMax() и init(_: IntMax)

+0

Я вижу, 'IntMax' является псевдонимом для' Int64'. На данный момент все 'ForwardIndexType' в стандартной библиотеке являются целыми числами того или иного типа, но возможно ли иметь индекс, который * не * является целым числом? (В конечном счете, любой такой индекс должен был бы отображаться на целое число, я предполагаю.) –

+0

Возможно, вы сбиваете с толку «Индекс» и «Индекс.Distance». «index» - это * not * always integer (например, 'String.Index'), но« расстояние от индекса »гарантируется как« целое число со знаком », по крайней мере, в Swift 1.1. – rintaro

+0

То, что я говорю, это связанный с индексом тип 'CollectionType', который ограничен соответствием протоколу ForwardIndexType. Ничто не говорит, что это должно быть целое, насколько я знаю, хотя трудно представить, что еще будет. –

Смежные вопросы