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

134

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

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

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

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

Изображение 69

  • 3
    возможный дубликат обратного диапазона в Swift
  • 1
    Такое блестящее решение отказаться от петель в стиле c. Верни их
Показать ещё 1 комментарий
Теги:
swift-playground

13 ответов

162
Лучший ответ

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. Они являются глобальными функциями, которые возвращают либо структуру StrideTo, либо StrideThrough, которые определены иначе, чем структура Range.

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

  • 1
    У меня были сомнения, и я сам их очистил .. :)
  • 23
    В Swift 2.0 теперь это: 5.stride(to: 1, by: -1) или 5.stride(through: 1, by: -1)
Показать ещё 4 комментария
185

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

Для 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 s!

Тест:

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
}
  • 6
    Я считаю, что это легче читать, чем использовать функцию stride .
  • 2
    reversed может скопировать коллекцию, хотя. По крайней мере, так я понимаю документацию по Data .
78

Обновлено для 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 

Заметки

43

Swift 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
26

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

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

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

16

С Swift 4.2, в соответствии с вашими потребностями, вы можете выбрать один из четырех следующих примеров кода 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(_:) initializer

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
*/
  • 0
    sequence (first: 5, next: {($ 0> 0)? ($ 0 - 1): nil}) // проще, чем ($ 0 - 1> = 0)
  • 0
    @SirEnder Спасибо. Я обновил код.
7

В Swift 4 и более поздних

    let count = 50//For example
    for i in (1...count).reversed() {
        print(i)
    }
4

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)

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

2

как для 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
1

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

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

Метод reversed() переворачивает значения.

  • 0
    Да .. Это самый простой способ изменить значения
  • 0
    Почему вы добавляете ответ, идентичный уже существующему? (Например, @ Rohit's.)
1

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

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

for i in (10...100).reverse(){
    sum1 /= i
}
print(sum1)

Ещё вопросы

Сообщество Overcoder
Наверх
Меню