Получение "String.Index" при перечислении быстрой строки

В настоящее время мы повторяем строку, как показано ниже:

let greeting = "Hello"
for (intIndex, char) in greeting.enumerated() {
    let currentIndex = greeting.index(greeting.startIndex, offsetBy: intIndex)
    let indexAfterCurrentIndex = greeting.index(after: currentIndex)
    print(greeting[indexAfterCurrentIndex...])
}

Я чувствую, что написание кода ниже является излишним.

let currentIndex = greeting.index(greeting.startIndex, offsetBy: intIndex)

Есть ли другой способ получить напрямую "String.Index" во время итерации?

Что-то вроде этого

let greeting = "Hello"
for (stringIndex, char) in greeting.enumeratedXXX() {
    let indexAfterCurrentIndex = greeting.index(after: stringIndex)
    print(greeting[indexAfterCurrentIndex...])
}

3 ответа

Решение

Для этого нет встроенной функциональности. Вы можете обернуть это в пользовательский итератор, но тогда вы будете инкапсулировать только те же вычисления в другом месте, так что это не ответ:)

Сложность кода

Тем не менее, вы можете улучшить производительность вашего текущего кода:

greeting.index(greeting.startIndex, offsetBy: intIndex)
  • Это рассчитает индекс из startIndex в результирующий индекс для каждой итерации цикла.
  • Расчет индекса с index(_:offsetBy:) на самом деле просто еще один цикл, где он +1каждый индекс. Нет никаких O(1) способ "вычислить" индекс; это обнаруживается петлей в O(n)

Таким образом, ваш собственный внешний цикл является линейным с O(n) за n итерации, по одной на каждого персонажа.

Затем вычисление индекса с помощью внутреннего цикла означает, что есть 1+2+3+4+5+6+...n = (n^2 + n)/2 итерации, где n это intIndex в этом случае.

Это означает, что алгоритм имеет сложность * обхода * обходной O(n + n^2), Квадратичная часть проблематична!

Лучший подход

Вы можете уменьшить сложность до 2 операций за итерацию, или O(2n), Просто сохраните ранее вычисленный индекс в памяти и +1 себе, избегая повторного вычисления с нуля.

Вот код:

let greeting = "Hello"
var index = greeting.startIndex
for char in greeting {
    let indexAfterCurrentIndex = greeting.index(after: index)
    print(greeting[indexAfterCurrentIndex...])
    index = indexAfterCurrentIndex
}

Все еще не простое и встроенное решение, но вы можете просто свернуть этот более эффективный алгоритм и все готово!

extension String {
    func forEachCharacterWithIndex(iterator: (String.Index, Character) -> Void) {
        var currIndex = self.startIndex
        for char in self {
            iterator(currIndex, char)
            currIndex = self.index(after: currIndex)
        }
    }
}

let greeting = "Hello"
greeting.forEachCharacterWithIndex { (index, char) in
    let indexAfterCurrentIndex = greeting.index(after: index)
    print(greeting[indexAfterCurrentIndex...])
}

Если вам нужны строковые индексы, вы можете перечислить greeting.indices:

let greeting = "Hello"
for index in greeting.indices {
    // ...
}

Если вам нужен каждый символ вместе с его индексом, вы можете перечислить строку и индексы параллельно:

let greeting = "Hello"
for (char, currentIndex) in zip(greeting, greeting.indices) {
    let indexAfterCurrentIndex = greeting.index(after: currentIndex)
    print(char, "-", greeting[indexAfterCurrentIndex...])
}

Выход:

Привет
е - лло
л - вот
л - о
о -

Более простой вариант будет

let greeting = "Hello"
for (char, nextIndex) in zip(greeting, greeting.indices.dropFirst()) {
    print(char, "-", greeting[nextIndex...])
}

который дает почти тот же результат, только без последней пары символ / индекс:

Привет
е - лло
л - вот
л - о

Почему бы не увеличить currentIndex на 1?

let greeting = "Hello"
for (stringIndex, char) in greeting.enumerated() {
    let currentIndex = stringIndex
    let nextIndex = currentIndex + 1
    print(nextIndex)
}
Другие вопросы по тегам