Как перебрать цикл в обратном порядке в swift?


187

Когда я использую цикл for в Playground, все работало нормально, пока я не изменил первый параметр цикла for на самое высокое значение. (повторяется в порядке убывания)

Это ошибка? У кого-нибудь еще это было?

for index in 510..509
{
    var a = 10
}

Счетчик, который отображает количество итераций, которые будут выполняться, продолжает тикать ...

введите описание изображения здесь



1
Такое блестящее решение отказаться от петель в стиле c. Верни их назад
Джоэл Тепли

Смотрите мой ответ с Swift 5, который показывает до 4 различных способов итерации в обратном порядке.
Imanou Petit

Ответы:


230

В Xcode 6 beta 4 добавлены две функции для итерации по диапазонам с шагом, отличным от одного:, stride(from: to: by:)который используется с исключительными диапазонами и stride(from: through: by:)который используется с включающими диапазонами.

Для итерации диапазона в обратном порядке их можно использовать, как показано ниже:

for index in stride(from: 5, to: 1, by: -1) {
    print(index)
}
//prints 5, 4, 3, 2

for index in stride(from: 5, through: 1, by: -1) {
    print(index)
}
//prints 5, 4, 3, 2, 1

Обратите внимание, что ни один из них не является Rangeфункцией-членом. Это глобальные функции, которые возвращают a StrideToили StrideThroughstruct, которые определены не так, как Rangestruct.

Предыдущая версия этого ответа использовала by()функцию-член Rangeструктуры, которая была удалена в бета-версии 4. Если вы хотите увидеть, как это работает, проверьте историю редактирования.


26
С Swift 2.0 это сейчас: 5.stride(to: 1, by: -1)или5.stride(through: 1, by: -1)
Бинар

6
С Swift 3.0 мы вернулись к бесплатной функции.
Цезарь

@Shaun это разумный и безопасный выбор. Я не могу сказать вам часто, что программисты R отключаются из-за того, что его :оператор неявно выполняет итерацию вниз, заставляя их циклы выполняться, когда они думают, что они пропустят их. Это очень удобно для интерактивного использования, но ужасно подвержено ошибкам в сценариях до такой степени, что руководства по стилю рекомендуют не использовать его .
Давор Кубранич

213

Примените функцию реверса к диапазону для итерации в обратном направлении:

Для Swift 1.2 и более ранних версий:

// Print 10 through 1
for i in reverse(1...10) {
    println(i)
}

Он также работает с полуоткрытыми диапазонами:

// Print 9 through 1
for i in reverse(1..<10) {
    println(i)
}

Примечание: reverse(1...10)создает массив типа [Int], поэтому, хотя это может быть хорошо для небольших диапазонов, было бы целесообразно использовать, lazyкак показано ниже, или рассмотреть принятый strideответ, если ваш диапазон большой.


Чтобы избежать создания большого массива, используйте lazyвместе с reverse(). Следующий тест эффективно работает на игровой площадке, показывая, что он не создает массив с одним триллионом Intс!

Тест:

var count = 0
for i in lazy(1...1_000_000_000_000).reverse() {
    if ++count > 5 {
        break
    }
    println(i)
}

Для Swift 2.0 в Xcode 7:

for i in (1...10).reverse() {
    print(i)
}

Обратите внимание, что в Swift 2.0, (1...1_000_000_000_000).reverse()это тип ReverseRandomAccessCollection<(Range<Int>)>, так что это прекрасно работает:

var count = 0
for i in (1...1_000_000_000_000).reverse() {
    count += 1
    if count > 5 {
        break
    }
    print(i)
}

Для Swift 3.0 reverse() был переименован в reversed():

for i in (1...10).reversed() {
    print(i) // prints 10 through 1
}

3
reversedможет скопировать коллекцию, хотя. По крайней мере, так я понимаю документацию Data.
Рафаэль

96

Обновлено для Swift 3

Ответ ниже представляет собой сводку доступных опций. Выберите тот, который наилучшим образом соответствует вашим потребностям.

reversed: числа в диапазоне

Вперед

for index in 0..<5 {
    print(index)
}

// 0
// 1
// 2
// 3
// 4

назад

for index in (0..<5).reversed() {
    print(index)
}

// 4
// 3
// 2
// 1
// 0

reversed: элементы в SequenceType

let animals = ["horse", "cow", "camel", "sheep", "goat"]

Вперед

for animal in animals {
    print(animal)
}

// horse
// cow
// camel
// sheep
// goat

назад

for animal in animals.reversed() {
    print(animal)
}

// goat
// sheep
// camel
// cow
// horse

reversed: элементы с индексом

Иногда требуется индекс для итерации по коллекции. Для этого вы можете использовать enumerate(), который возвращает кортеж. Первый элемент кортежа - это индекс, а второй элемент - это объект.

let animals = ["horse", "cow", "camel", "sheep", "goat"]

Вперед

for (index, animal) in animals.enumerated() {
    print("\(index), \(animal)")
}

// 0, horse
// 1, cow
// 2, camel
// 3, sheep
// 4, goat

назад

for (index, animal) in animals.enumerated().reversed()  {
    print("\(index), \(animal)")
}

// 4, goat
// 3, sheep
// 2, camel
// 1, cow
// 0, horse

Обратите внимание, что, как отметил Бен Лахман в своем ответе , вы, скорее всего, захотите сделать это, .enumerated().reversed()а не .reversed().enumerated()(что приведет к увеличению числа индексов).

шагать: цифры

Stride - это способ итерации без использования диапазона. Есть две формы. Комментарии в конце кода показывают, какой будет версия диапазона (при условии, что размер приращения равен 1).

startIndex.stride(to: endIndex, by: incrementSize)      // startIndex..<endIndex
startIndex.stride(through: endIndex, by: incrementSize) // startIndex...endIndex

Вперед

for index in stride(from: 0, to: 5, by: 1) {
    print(index)
}

// 0
// 1
// 2
// 3
// 4

назад

Изменение размера приращения -1позволяет вам вернуться назад.

for index in stride(from: 4, through: 0, by: -1) {
    print(index)
}

// 4
// 3
// 2
// 1
// 0

Обратите внимание , что toи throughразличие.

шаг: элементы SequenceType

Вперед с шагом 2

let animals = ["horse", "cow", "camel", "sheep", "goat"]

Я использую 2в этом примере, чтобы показать еще одну возможность.

for index in stride(from: 0, to: 5, by: 2) {
    print("\(index), \(animals[index])")
}

// 0, horse
// 2, camel
// 4, goat

назад

for index in stride(from: 4, through: 0, by: -1) {
    print("\(index), \(animals[index])")
}

// 4, goat
// 3, sheep 
// 2, camel
// 1, cow  
// 0, horse 

Ноты


52

Свифт 4 года

for i in stride(from: 5, to: 0, by: -1) {
    print(i)
}
//prints 5, 4, 3, 2, 1

for i in stride(from: 5, through: 0, by: -1) {
    print(i)
}
//prints 5, 4, 3, 2, 1, 0

28

С Swift 5, в соответствии с вашими потребностями, вы можете выбрать один из четырех следующих примеров кода Playground , чтобы решить вашу проблему.


# 1. Используя ClosedRange reversed()метод

ClosedRangeимеет метод с именем reversed(). reversed()Метод имеет следующую декларацию:

func reversed() -> ReversedCollection<ClosedRange<Bound>>

Возвращает представление, представляющее элементы коллекции в обратном порядке.

Использование:

let reversedCollection = (0 ... 5).reversed()

for index in reversedCollection {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

В качестве альтернативы вы можете использовать Range reversed()метод:

let reversedCollection = (0 ..< 6).reversed()

for index in reversedCollection {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

# 2. Используя sequence(first:next:)функцию

Стандартная библиотека Swift предоставляет функцию с именем sequence(first:next:). sequence(first:next:)имеет следующую декларацию:

func sequence<T>(first: T, next: @escaping (T) -> T?) -> UnfoldFirstSequence<T>

Возвращает последовательность, сформированную из firstповторяющихся ленивых приложений next.

Использование:

let unfoldSequence = sequence(first: 5, next: {
    $0 > 0 ? $0 - 1 : nil
})

for index in unfoldSequence {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

# 3. Используя stride(from:through:by:)функцию

Стандартная библиотека Swift предоставляет функцию с именем stride(from:through:by:). stride(from:through:by:)имеет следующую декларацию:

func stride<T>(from start: T, through end: T, by stride: T.Stride) -> StrideThrough<T> where T : Strideable

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

Использование:

let sequence = stride(from: 5, through: 0, by: -1)

for index in sequence {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

В качестве альтернативы вы можете использовать stride(from:to:by:):

let sequence = stride(from: 5, to: -1, by: -1)

for index in sequence {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

# 4. Использование AnyIterator init(_:)инициализатора

AnyIteratorимеет инициализатор под названием init(_:). init(_:)имеет следующую декларацию:

init(_ body: @escaping () -> AnyIterator<Element>.Element?)

Создает итератор, который оборачивает данное замыкание в свой next()метод.

Использование:

var index = 5

guard index >= 0 else { fatalError("index must be positive or equal to zero") }

let iterator = AnyIterator({ () -> Int? in
    defer { index = index - 1 }
    return index >= 0 ? index : nil
})

for index in iterator {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

При необходимости вы можете изменить рефакторинг предыдущего кода, создав метод расширения Intи обернув в него свой итератор:

extension Int {

    func iterateDownTo(_ endIndex: Int) -> AnyIterator<Int> {
        var index = self
        guard index >= endIndex else { fatalError("self must be greater than or equal to endIndex") }

        let iterator = AnyIterator { () -> Int? in
            defer { index = index - 1 }
            return index >= endIndex ? index : nil
        }
        return iterator
    }

}

let iterator = 5.iterateDownTo(0)

for index in iterator {
    print(index)
}

/*
Prints:
5
4
3
2
1
0
*/

sequence (first: 5, next: {($ 0> 0)? ($ 0 - 1): nil}) // проще, чем ($ 0 - 1> = 0)
SirEnder

27

Для Swift 2.0 и выше вы должны применить реверс на коллекцию диапазонов

for i in (0 ..< 10).reverse() {
  // process
}

Он был переименован в .reversed () в Swift 3.0



5

Swift 4.0

for i in stride(from: 5, to: 0, by: -1) {
    print(i) // 5,4,3,2,1
}

Если вы хотите включить toзначение:

for i in stride(from: 5, through: 0, by: -1) {
    print(i) // 5,4,3,2,1,0
}

3

Если кто-то хочет перебрать массив ( Arrayили, вообще говоря, любой SequenceType) в обратном порядке. У вас есть несколько дополнительных опций.

Сначала вы можете reverse()массива и цикл через него, как обычно. Однако я предпочитаю использовать enumerate()большую часть времени, так как он выводит кортеж, содержащий объект и его индекс.

Здесь нужно отметить одну вещь: важно называть их в правильном порядке:

for (index, element) in array.enumerate().reverse()

дает индексы в порядке убывания (что я обычно ожидаю). в то время как:

for (index, element) in array.reverse().enumerate()(что ближе к NSArray reverseEnumerator)

идет назад по массиву, но выводит восходящие индексы.


3

как для Swift 2.2, Xcode 7.3 (10 июня 2016 г.):

for (index,number) in (0...10).enumerate() {
    print("index \(index) , number \(number)")
}

for (index,number) in (0...10).reverse().enumerate() {
    print("index \(index) , number \(number)")
}

Вывод :

index 0 , number 0
index 1 , number 1
index 2 , number 2
index 3 , number 3
index 4 , number 4
index 5 , number 5
index 6 , number 6
index 7 , number 7
index 8 , number 8
index 9 , number 9
index 10 , number 10


index 0 , number 10
index 1 , number 9
index 2 , number 8
index 3 , number 7
index 4 , number 6
index 5 , number 5
index 6 , number 4
index 7 , number 3
index 8 , number 2
index 9 , number 1
index 10 , number 0

2

whileВместо этого вы можете использовать цикл C-Style . Это прекрасно работает в Swift 3:

var i = 5 
while i > 0 { 
    print(i)
    i -= 1
}

2

Вы можете использовать метод reversed () для легкого реверсирования значений.

var i:Int
for i in 1..10.reversed() {
    print(i)
}

Метод reversed () меняет значения.


Почему вы добавляете ответ, идентичный уже существующему? (Например, @ Rohit's.)
Davor Cubranic

1
var sum1 = 0
for i in 0...100{
    sum1 += i
}
print (sum1)

for i in (10...100).reverse(){
    sum1 /= i
}
print(sum1)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.