Як швидко перебрати цикл у зворотному порядку?


186

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

Це помилка? Хтось ще мав його?

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

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

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


3
можливий дублікат Reverse Range у Свіфті
Метт Гібсон

1
Таке геніальне рішення скинути петлі стилю c. Поверніть їх назад
Джоель Теплі

Дивіться мою відповідь із Swift 5, який показує до 4 різних способів ітерації у зворотному порядку.
Imanou Petit

Відповіді:


229

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або StrideThroughструктуру, які визначаються інакше від Rangestru.

Попередня версія цієї відповіді використовувала функцію- by()член Rangeструктури, яку було видалено в бета-версії 4. Якщо ви хочете подивитися, як це працювало, перевірте історію редагування.


26
З Swift 2.0 це зараз: 5.stride(to: 1, by: -1)або5.stride(through: 1, by: -1)
Binarian

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()(що призвело б до збільшення чисел індексу).

крок: числа

Страйд - це спосіб ітерації без використання дальності. Існує дві форми. У коментарях в кінці коду видно, якою була б версія діапазону (якщо припустити, що приріст дорівнює 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
*/

послідовність (перший: 5, наступний: {($ 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's 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)
Давор Кубраніч

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.