¿Cómo iterar para el bucle en orden inverso en rápido?

187

Cuando uso el bucle for en Playground, todo funcionó bien, hasta que cambié el primer parámetro de for loop por el valor más alto. (iterado en orden descendente)

¿Es esto un error? ¿Alguien más lo tiene?

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

El contador que muestra el número de iteraciones que se ejecutarán sigue marcando ...

ingrese la descripción de la imagen aquí

Krishnan
fuente
3
posible duplicado del rango inverso en Swift
Matt Gibson
1
Una decisión tan brillante de abandonar los bucles de estilo c.
Tráelos de
Vea mi respuesta con Swift 5 que muestra hasta 4 formas diferentes de iterar en orden inverso.
Imanou Petit

Respuestas:

230

Xcode 6 beta 4 agregó dos funciones para iterar en rangos con un paso distinto de uno: stride(from: to: by:)que se usa con rangos exclusivos y stride(from: through: by:)que se usa con rangos inclusivos.

Para iterar en un rango en orden inverso, se pueden usar de la siguiente manera:

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

Tenga en cuenta que ninguno de esos es una Rangefunción miembro. Son funciones globales que devuelven StrideTouna StrideThroughestructura o una estructura, que se definen de manera diferente a la Rangeestructura.

Una versión anterior de esta respuesta utilizaba la by()función miembro de la Rangeestructura, que se eliminó en la versión beta 4. Si desea ver cómo funcionó, consulte el historial de edición.

Cezar
fuente
26
Con Swift 2.0 ahora es: 5.stride(to: 1, by: -1)o5.stride(through: 1, by: -1)
Binarian
66
Con Swift 3.0 volvemos a caminar como una función libre.
Cezar
@Shaun es una opción razonable y segura. No puedo decirte que a menudo los programadores R son disparados por su :operador implícitamente iterando hacia abajo, haciendo que sus bucles se ejecuten cuando pensaron que los omitiría. Es muy conveniente para el uso interactivo, pero terriblemente propenso a errores en los scripts, hasta el punto en que las guías de estilo recomiendan no usarlo .
Davor Cubranic
213

Aplique la función inversa al rango para iterar hacia atrás:

Para Swift 1.2 y versiones anteriores:

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

También funciona con rangos medio abiertos:

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

Nota: reverse(1...10)crea una matriz de tipos [Int], por lo que si bien esto podría estar bien para rangos pequeños, sería aconsejable usar lazycomo se muestra a continuación o considerar la striderespuesta aceptada si su rango es grande.


Para evitar crear una gran matriz, use lazyjunto con reverse(). ¡La siguiente prueba se ejecuta eficientemente en un área de juegos que muestra que no está creando una matriz con un billón de Ints!

Prueba:

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

Para Swift 2.0 en Xcode 7:

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

Tenga en cuenta que en Swift 2.0, (1...1_000_000_000_000).reverse()es de tipo ReverseRandomAccessCollection<(Range<Int>)>, por lo que funciona bien:

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

Para Swift 3.0 se reverse() ha cambiado el nombre a reversed():

for i in (1...10).reversed() {
    print(i) // prints 10 through 1
}
vacawama
fuente
3
reversedaunque puede copiar la colección. Al menos, así es como entiendo la documentación Data.
Raphael
96

Actualizado para Swift 3

La respuesta a continuación es un resumen de las opciones disponibles. Elija el que mejor se adapte a sus necesidades.

reversed: números en un rango

Adelante

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

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

Hacia atrás

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

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

reversed: elementos en SequenceType

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

Adelante

for animal in animals {
    print(animal)
}

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

Hacia atrás

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

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

reversed: elementos con un índice

A veces se necesita un índice cuando se itera por una colección. Para eso puedes usar enumerate(), que devuelve una tupla. El primer elemento de la tupla es el índice y el segundo elemento es el objeto.

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

Adelante

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

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

Hacia atrás

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

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

Tenga en cuenta que, como señaló Ben Lachman en su respuesta , es probable que desee hacerlo en .enumerated().reversed()lugar de .reversed().enumerated()(lo que aumentaría los números de índice).

zancada: números

Stride es una forma de iterar sin usar un rango. Hay dos formas Los comentarios al final del código muestran cuál sería la versión del rango (suponiendo que el tamaño del incremento sea 1).

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

Adelante

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

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

Hacia atrás

Cambiar el tamaño de incremento a le -1permite retroceder.

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

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

Tenga en cuenta la toy la throughdiferencia.

zancada: elementos de SequenceType

Adelante por incrementos de 2

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

Estoy usando 2en este ejemplo solo para mostrar otra posibilidad.

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

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

Hacia atrás

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

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

Notas

Suragch
fuente
52

Swift 4 en adelante

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
Irfan
fuente
28

Con Swift 5, de acuerdo con sus necesidades, puede elegir uno de los siguientes cuatro ejemplos de código de Playground para resolver su problema.


# 1 Utilizando el ClosedRange reversed()método

ClosedRangetiene un método llamado reversed(). reversed()El método tiene la siguiente declaración:

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

Devuelve una vista que presenta los elementos de la colección en orden inverso.

Uso:

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

for index in reversedCollection {
    print(index)
}

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

Como alternativa, puede usar el Range reversed()método:

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

for index in reversedCollection {
    print(index)
}

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

# 2 Usando la sequence(first:next:)función

Swift Standard Library proporciona una función llamada sequence(first:next:). sequence(first:next:)tiene la siguiente declaración:

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

Devuelve una secuencia formada a partir de firstaplicaciones perezosas repetidas de next.

Uso:

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 Usando la stride(from:through:by:)función

Swift Standard Library proporciona una función llamada stride(from:through:by:). stride(from:through:by:)tiene la siguiente declaración:

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

Devuelve una secuencia desde un valor inicial hacia, y posiblemente incluyendo, un valor final, avanzando por la cantidad especificada.

Uso:

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

for index in sequence {
    print(index)
}

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

Como alternativa, puede usar 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. Usando AnyIterator init(_:)inicializador

AnyIteratortiene un inicializador llamada init(_:). init(_:)tiene la siguiente declaración:

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

Crea un iterador que envuelve el cierre dado en su next()método.

Uso:

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
*/

Si es necesario, puede refactorizar el código anterior creando un método de extensión Inty envolviendo su iterador en él:

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
*/
Imanou Petit
fuente
secuencia (primero: 5, siguiente: {($ 0> 0)? ($ 0 - 1): nil}) // es más simple que ($ 0 - 1> = 0)
SirEnder
27

Para Swift 2.0 y superior, debe aplicar el reverso en una colección de rango

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

Se ha renombrado a .reversed () en Swift 3.0

Andrew Luca
fuente
9

En Swift 4 y posterior

    let count = 50//For example
    for i in (1...count).reversed() {
        print(i)
    }
Rohit Sisodia
fuente
5

Swift 4.0

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

Si desea incluir el tovalor:

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

Si uno quiere iterar a través de una matriz ( Arrayo más generalmente cualquiera SequenceType) a la inversa. Tienes algunas opciones adicionales.

Primero puede hacer reverse()la matriz y recorrerla de manera normal. Sin embargo, prefiero usar la mayor enumerate()parte del tiempo, ya que genera una tupla que contiene el objeto y su índice.

Lo único a tener en cuenta aquí es que es importante llamarlos en el orden correcto:

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

produce índices en orden descendente (que es lo que generalmente espero). mientras:

for (index, element) in array.reverse().enumerate()(que es una coincidencia más cercana a la de NSArray reverseEnumerator)

recorre la matriz hacia atrás pero genera índices ascendentes.

Ben Lachman
fuente
3

En cuanto a Swift 2.2, Xcode 7.3 (10 de junio de 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)")
}

Salida:

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
PeiweiChen
fuente
2

Puede considerar usar el whilebucle C-Style en su lugar. Esto funciona bien en Swift 3:

var i = 5 
while i > 0 { 
    print(i)
    i -= 1
}
Nitin Nain
fuente
2

Puede usar el método reverse () para invertir fácilmente los valores.

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

El método reverse () invierte los valores.

PU SIDHARTH
fuente
¿Por qué agregaría una respuesta idéntica a la ya existente? (Por ejemplo, @ 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)
Abo3atef
fuente