Como iterar o loop na ordem inversa no swift?

187

Quando eu uso o loop for no Playground, tudo funcionou bem, até que eu mudei o primeiro parâmetro do loop for para o valor mais alto. (iterado em ordem decrescente)

Isso é um inseto? Alguém mais o teve?

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

O contador que exibe o número de iterações que serão executadas continua correndo ...

insira a descrição da imagem aqui

Krishnan
fonte
3
possível duplicado da Gama inversa no Swift
Matt Gibson
1
Uma decisão tão brilhante de descartar loops de estilo c. Trazê-los de volta
Joel Teply
Veja minha resposta com o Swift 5, que mostra até 4 maneiras diferentes de iterar na ordem inversa.
Imanou Petit 30/05/19

Respostas:

230

O Xcode 6 beta 4 adicionou duas funções para iterar em intervalos com uma etapa diferente de um:, stride(from: to: by:)que é usado com intervalos exclusivos e stride(from: through: by:), que é usado com intervalos inclusivos.

Para iterar em um intervalo na ordem inversa, eles podem ser usados ​​como abaixo:

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

Observe que nenhum desses é uma Rangefunção de membro. São funções globais que retornam uma StrideToou uma StrideThroughestrutura, definidas de forma diferente da Rangeestrutura.

Uma versão anterior desta resposta usava a by()função membro da Rangeestrutura, que foi removida na versão beta 4. Se você quiser ver como isso funcionou, verifique o histórico de edição.

Cezar
fonte
26
Com o Swift 2.0, é agora: 5.stride(to: 1, by: -1)ou5.stride(through: 1, by: -1)
Binarian
6
Com o Swift 3.0, voltamos ao ritmo como uma função livre.
Cezar
@ Shaun é uma escolha razoável e segura. Não posso dizer com frequência que os programadores de R são acionados por seu :operador iterando implicitamente para baixo, fazendo com que seus loops sejam executados quando eles pensam que os ignorariam. É muito conveniente para uso interativo, mas é extremamente propenso a erros em scripts, a ponto de os guias de estilo não recomendarem o uso .
Davor Cubranic
213

Aplique a função reversa ao intervalo para iterar para trás:

Para o Swift 1.2 e versões anteriores:

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

Também funciona com intervalos semi-abertos:

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

Nota: reverse(1...10)cria uma matriz do tipo [Int], portanto, embora isso possa ser bom para pequenos intervalos, seria aconselhável usar lazycomo mostrado abaixo ou considerar a strideresposta aceita se o seu intervalo for grande.


Para evitar criar uma grande matriz, use lazyjunto com reverse(). O teste a seguir é executado com eficiência em um Playground, mostrando que ele não está criando uma matriz com um trilhão de Ints!

Teste:

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

Para o Swift 2.0 no Xcode 7:

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

Observe que no Swift 2.0, (1...1_000_000_000_000).reverse()é do tipo ReverseRandomAccessCollection<(Range<Int>)>, então isso funciona bem:

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

O Swift 3.0 reverse() foi renomeado para reversed():

for i in (1...10).reversed() {
    print(i) // prints 10 through 1
}
Vacawama
fonte
3
reversedpode copiar a coleção, no entanto. Pelo menos, é assim que entendo a documentação Data.
Raphael
96

Atualizado para o Swift 3

A resposta abaixo é um resumo das opções disponíveis. Escolha o que melhor se adapta às suas necessidades.

reversed: números em um intervalo

frente

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

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

Para trás

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

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

reversed: elementos em SequenceType

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

frente

for animal in animals {
    print(animal)
}

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

Para trás

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

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

reversed: elementos com um índice

Às vezes, é necessário um índice ao percorrer uma coleção. Para isso você pode usar enumerate(), o que retorna uma tupla. O primeiro elemento da tupla é o índice e o segundo elemento é o objeto.

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

frente

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

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

Para trás

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

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

Observe que, como Ben Lachman observou em sua resposta , você provavelmente deseja fazer .enumerated().reversed()mais do que .reversed().enumerated()(o que aumentaria o número de índices).

stride: numbers

Stride é uma maneira de iterar sem usar um intervalo. Existem duas formas. Os comentários no final do código mostram qual seria a versão do intervalo (assumindo que o tamanho do incremento seja 1).

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

frente

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

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

Para trás

Alterar o tamanho do incremento para -1permite retroceder.

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

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

Observe a diferença toe through.

stride: elementos de SequenceType

Avançar em incrementos de 2

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

Estou usando 2neste exemplo apenas para mostrar outra possibilidade.

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

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

Para trá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
fonte
52

Swift 4 em diante

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
fonte
28

Com o Swift 5, de acordo com suas necessidades, você pode escolher um dos quatro exemplos de código do Playground a seguir para resolver seu problema.


# 1 Usando o ClosedRange reversed()método

ClosedRangetem um método chamado reversed(). reversed()O método tem a seguinte declaração:

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

Retorna uma exibição que apresenta os elementos da coleção em ordem inversa.

Uso:

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

for index in reversedCollection {
    print(index)
}

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

Como alternativa, você pode usar o Range reversed()método:

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

for index in reversedCollection {
    print(index)
}

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

# 2 Usando a sequence(first:next:)função

A Biblioteca Padrão Swift fornece uma função chamada sequence(first:next:). sequence(first:next:)tem a seguinte declaração:

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

Retorna uma sequência formada firste repetidas aplicações preguiçosas 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 a stride(from:through:by:)função

A Biblioteca Padrão Swift fornece uma função chamada stride(from:through:by:). stride(from:through:by:)tem a seguinte declaração:

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

Retorna uma sequência de um valor inicial em direção a, e possivelmente incluindo, um valor final, avançando pela quantidade 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, você pode 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

AnyIteratortem um inicializador chamado init(_:). init(_:)tem a seguinte declaração:

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

Cria um iterador que envolve o fechamento especificado em seu 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
*/

Se necessário, você pode refatorar o código anterior criando um método de extensão Inte envolvendo seu iterador nele:

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
fonte
seqüência (primeiro: 5, em seguida: {($ 0> 0) ($ 0 - 1):? nil}) // é mais simples do que ($ 0 - 1> = 0)
SirEnder
27

Para Swift 2.0 e superior, você deve aplicar reverso em uma coleção de intervalos

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

Foi renomeado para .reversed () no Swift 3.0

Andrew Luca
fonte
9

No Swift 4 e no último

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

Swift 4.0

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

Se você deseja incluir o tovalor:

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

Se alguém quiser iterar através de uma matriz ( Arrayou mais geralmente alguma SequenceType) ao contrário. Você tem algumas opções adicionais.

Primeiro você pode reverse()a matriz e percorrê-la normalmente. No entanto, prefiro usar a enumerate()maior parte do tempo, pois gera uma tupla contendo o objeto e seu índice.

A única coisa a observar aqui é que é importante chamá-los na ordem correta:

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

gera índices em ordem decrescente (que é o que geralmente espero). enquanto que:

for (index, element) in array.reverse().enumerate()(que é mais parecido com o do NSArray reverseEnumerator)

retrocede a matriz, mas gera índices ascendentes.

Ben Lachman
fonte
3

quanto ao Swift 2.2, Xcode 7.3 (10 de junho 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)")
}

Resultado :

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
fonte
2

Você pode usar o whileloop C-Style . Isso funciona muito bem no Swift 3:

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

Você pode usar o método reversed () para reverter valores facilmente.

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

O método reversed () reverte os valores.

SIDHARTH PU
fonte
Por que você adicionaria uma resposta idêntica à já existente? (Por exemplo, @ 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
fonte