Как индекс Swift Dictionary устраняет неоднозначность ключей и индексов Int?

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

В документации и источнике Swift (насколько я могу найти) словари имеют два индекса. Одним из них является наиболее часто используемый индекс по ключу (источник Github):

extension Dictionary {
  ...
  public subscript(key: Key) -> Value? {
    @inline(__always)
    get {
      return _variantBuffer.maybeGet(key)
    }
    set(newValue) {
      if let x = newValue {
        // FIXME(performance): this loads and discards the old value.
        _variantBuffer.updateValue(x, forKey: key)
      }
      else {
        // FIXME(performance): this loads and discards the old value.
        removeValue(forKey: key)
      }
    }
  }
  ...
}

А второй — это индекс по положению/индексу (Github) источник) как часть его соответствия протоколу Collection:

extension Dictionary: Collection {
  ...
  public subscript(position: Index) -> Element {
    return _variantBuffer.assertingGet(position)
  }
  ...
}

При использовании их со словарем с ключом, отличным от Int, они ведут себя точно так, как ожидалось, потому что индексы различаются разными типами параметров, например: String против Int.

let stringKeys = ["One": 1, "Two": 2, "Three": 3]
stringKeys["One"]   // 1
stringKeys[1]       // ("Two", 2)

Когда Int используются в качестве ключей, нижний индекс ключа используется по желанию.

let intKeys = [1: "One, 2: "Two, 3: "Three"]
intKeys[1]   // "One"

Как тип Dictionary выполняет это? Мне кажется, что параметры Index и Key индексов оба равны Int и что компилятор не должен знать, что имелось в виду. Действительно, когда я реализую те же индексы для своего пользовательского словаря, компилятор выдает точно такую ​​же ошибку — «Неоднозначное использование индекса» — когда я тестирую его с ключами Int.

Сначала я задавался вопросом, было ли это значение по умолчанию, предоставленное в расширении протокола и переопределенное более конкретной реализацией, но из того, что я могу сказать выше, это не так. Единственная другая теория, которая у меня есть, заключается в том, что Index имеет какой-то другой тип, чем «Int», поэтому он остается однозначным, но я не могу найти ничего, что подтверждало бы это. Может кто-нибудь пролить некоторый свет на это? Помимо моих непосредственных потребностей, это довольно умное поведение в Swift, которое я хотел бы понять.

Всем спасибо за прочтение и за помощь!


person Matthew M.    schedule 09.12.2017    source источник


Ответы (1)


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

Это именно так. Dictionary имеет два метода индекса:

public subscript(key: Dictionary.Key) -> Dictionary.Value?
public subscript(position: Dictionary<Key, Value>.Index) -> Dictionary.Element { get }

Первый принимает ключ и возвращает (необязательное) значение, второй принимает Dictionary.Index (исходный код) и возвращает (необязательно) Dictionary.Element, то есть ключ- пара значений. Пример:

let d : Dictionary = [1 : "one"]
if let idx = d.index(forKey: 1) {
    print(String(reflecting: type(of: idx))) // Swift.Dictionary<Swift.Int, Swift.String>.Index
    let kv = d[idx]
    print(String(reflecting: type(of: kv)))  // (key: Swift.Int, value: Swift.String)
}

Dictionary.Index также используется в других методах, таких как

public var startIndex: Dictionary<Key, Value>.Index { get }
public var endIndex: Dictionary<Key, Value>.Index { get }
public func index(where predicate: ((key: Key, value: Value)) throws -> Bool) rethrows -> Dictionary<Key, Value>.Index?

которые являются частью протокола Collection.

Как правило, связанный тип Index для Collection не обязательно является Int, другим примером является String, который имеет свой собственный тип String.Index.

person Martin R    schedule 09.12.2017