Какой тип следует использовать, если вы хотите использовать index.advanceBy?

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

// Error: Cannot invoke 'advanceBy' with an argument list of type '(Int)'

Тип Int явно неверен, но метод indexOf принимает аргумент Self.Distance, и я не уверен, как использовать его в качестве типа параметра.

extension CollectionType where Generator.Element : Equatable {
    func objectNear(object: Self.Generator.Element, indexModifier: Int) -> Self.Generator.Element? {
        if let index = self.indexOf(object) {
            let newIndex = index.advancedBy(indexModifier) // this doesn't work
            //let newIndex = index.advancedBy(1) // but this this works
            if self.indices.contains(newIndex) {
                return self[newIndex]
            }
        }
        return nil
    }
}

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


person zekel    schedule 16.01.2016    source источник


Ответы (1)


CollectionType есть метод

public func indexOf(element: Self.Generator.Element) -> Self.Index?

и соответствует

public protocol Indexable {
    typealias Index : ForwardIndexType
    // ...
}

Наконец, ForwardIndexType имеет метод

public func advancedBy(n: Self.Distance) -> Self

Поэтому правильный тип Index.Distance:

func objectNear(object: Self.Generator.Element, indexModifier: Index.Distance) -> Self.Generator.Element? { ... }

Но обратите внимание, что продвижение индекса за пределы endIndex может привести к сбою, например. для коллекций персонажей:

let c = "abc".characters
print(c.objectNear("b", indexModifier: 1)) // Optional("c")
print(c.objectNear("b", indexModifier: 2)) // nil
print(c.objectNear("b", indexModifier: 3)) // fatal error: can not increment endIndex

Безопасный вариант:

func objectNear(object: Generator.Element, indexModifier: Index.Distance) -> Generator.Element? {
    if let index = indexOf(object) {
        if indexModifier > 0 && index.distanceTo(endIndex) <= indexModifier {
            return nil
        }
        if indexModifier < 0 && startIndex.distanceTo(index) < -indexModifier {
            return nil
        }
        return self[index.advancedBy(indexModifier)]
    }
    return nil
}

В качестве альтернативы, если вам нужен метод только для коллекций, которые индексируются Int (например, Array), вы можете определить

extension CollectionType where Generator.Element : Equatable, Index == Int {

    func objectNear(object: Generator.Element, indexModifier: Int) -> Generator.Element? {
        if let index = self.indexOf(object) {
            let newIndex = index + indexModifier
            if indices.contains(newIndex) {
                return self[newIndex]
            }
        }
        return nil
    }
}
person Martin R    schedule 16.01.2016
comment
Я просто печатал это =] Мартин прав. Недавно я столкнулся с подобной проблемой с шагом. - person Chris Slowik; 16.01.2016
comment
Спасибо за подробный ответ. Поскольку я большой поклонник отрицательные индексы, как я могу заставить индексы за пределами границ возвращать nil вместо первого элемента? (Ограничение на startIndex заставляет -1 возвращать первый элемент.) - person zekel; 17.01.2016
comment
@zekel: я обновил ответ версией, которая должна охватывать все комбинации. Может быть, есть более элегантный, но это то, что у меня есть до сих пор. - person Martin R; 17.01.2016