web-dev-qa-db-fra.com

Swift 3 pour la boucle avec incrémentation

Comment puis-je écrire ce qui suit dans Swift3?

for (f = first; f <= last; f += interval)          
{
    n += 1
}

C'est ma propre tentative

for _ in 0.stride(to: last, by: interval)
{
    n += 1
}
40
user462990

Swift 2.2 -> 3.0: Strideable: s stride(...) remplacée par une fonction globale stride(...)

Dans Swift 2.2, nous pouvons (comme vous l'avez essayé dans votre propre tentative) utiliser les fonctions blueprinted (et implémentées par défaut) stride(through:by:) et stride(to:by:)du protocole Strideable

/* Swift 2.2: stride example usage */
let from = 0
let to = 10
let through = 10
let by = 1
for _ in from.stride(through, by: by) { } // from ... through (steps: 'by')
for _ in from.stride(to, by: by) { }      // from ..< to      (steps: 'by')

Alors que dans Swift 3.0, ces deux fonctions ont été supprimées de Strideable au profit de les fonctions globales stride(from:through:by:) et stride(from:to:by:) ; d’où l’équivalent Swift 3.0 de ce qui suit est la suivante:

/* Swift 3.0: stride example usage */
let from = 0
let to = 10
let through = 10
let by = 1
for _ in stride(from: from, through: through, by: by) { }
for _ in stride(from: from, to: to, by: by) { }

Dans votre exemple, vous souhaitez utiliser l'alternative de foulée à intervalle fermé stride(from:through:by:), car l'invariant de votre boucle for utilise la comparaison avec less ou égal à (<=). C'est à dire.

/* example values of your parameters 'first', 'last' and 'interval' */
let first = 0
let last = 10
let interval = 2
var n = 0
for f in stride(from: first, through: last, by: interval) { 
    print(f)
    n += 1 
} // 0 2 4 6 8 10
print(n) // 6

Où naturellement nous utilisons votre boucle for uniquement comme exemple du passage de for loop à stride, comme vous pouvez le faire naturellement, par exemple, calculez n sans avoir besoin d'une boucle (n=1+(last-first)/interval).

Swift 3.0: une alternative à stride pour une logique d’incrémentation plus complexe

Avec mise en œuvre de la proposition d’évolution SE-0094 , Swift 3.0 a introduit les fonctions globales sequence:

ce qui peut être une alternative appropriée à stride pour les cas avec une relation d'incrémentation d'itération plus complexe (ce qui n'est pas le cas dans cet exemple).

Déclaration (s)

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

func sequence<T, State>(state: State, 
                        next: @escaping (inout State) -> T?) ->
           UnfoldSequence<T, State>

Nous allons brièvement examiner la première de ces deux fonctions. Les arguments next prennent une fermeture qui applique une certaine logique à la construction lente de l'élément suivant de la séquence en fonction de l'élément actuel (commençant par first). La séquence est terminée lorsque next renvoie nil, ou infini, si un next ne renvoie jamais nil.

Appliquée à l'exemple simple à pas constant ci-dessus, la méthode sequence est un peu verbeuse et déborde w.r.t. la solution adaptée à cet usage stride:

let first = 0
let last = 10
let interval = 2
var n = 0
for f in sequence(first: first,
                  next: { $0 + interval <= last ? $0 + interval : nil }) {
    print(f)
    n += 1
} // 0 2 4 6 8 10
print(n) // 6

Les fonctions sequence deviennent très utiles pour les cas à foulée non constante, cependant, par ex. comme dans l'exemple couvert dans le Q & A suivant:

Veillez simplement à mettre fin à la séquence avec un éventuel retour nil (sinon: une génération d'éléments "infinie") ou, lorsque Swift 3.1 arrive, utilisez sa génération paresseuse en combinaison avec la méthode prefix(while:) pour les séquences, comme décrit dans la proposition d'évolution SE-0045 . Cette dernière appliquée à l'exemple en cours de cette réponse rend l'approche sequence moins verbeux, incluant clairement les critères de terminaison de la génération d’éléments.

/* for Swift 3.1 */
// ... as above
for f in sequence(first: first, next: { $0 + interval })
    .prefix(while: { $0 <= last }) {
    print(f)
    n += 1
} // 0 2 4 6 8 10
print(n) // 6
63
dfri

Avec Swift 5, vous pouvez choisir l’un des 5 exemples suivants pour résoudre votre problème.


#1. Utiliser stride(from:to:by:) fonction

let first = 0
let last = 10
let interval = 2

let sequence = stride(from: first, to: last, by: interval)

for element in sequence {
    print(element)
}

/*
prints:
0
2
4
6
8
*/

# 2. Utiliser sequence(first:next:) fonction

let first = 0
let last = 10
let interval = 2

let unfoldSequence = sequence(first: first, next: {
    $0 + interval < last ? $0 + interval : nil
})

for element in unfoldSequence {
    print(element)
}

/*
prints:
0
2
4
6
8
*/

# 3. Utilisation de AnySequenceinit(_:) initialiseur

let anySequence = AnySequence<Int>({ () -> AnyIterator<Int> in
    let first = 0
    let last = 10
    let interval = 2

    var value = first
    return AnyIterator<Int> {
        defer { value += interval }
        return value < last ? value : nil
    }
})

for element in anySequence {
    print(element)
}

/*
prints:
0
2
4
6
8
*/

# 4. Utilisation de la méthode CountableRangefilter(_:)

let first = 0
let last = 10
let interval = 2

let range = first ..< last
let lazyCollection = range.lazy.filter({ $0 % interval == 0 })

for element in lazyCollection {
    print(element)
}

/*
prints:
0
2
4
6
8
*/

# 5. Utilisation de la méthode CountableRangeflatMap(_:)

let first = 0
let last = 10
let interval = 2

let range = first ..< last
let lazyCollection = range.lazy.compactMap({ $0 % interval == 0 ? $0 : nil })

for element in lazyCollection {
    print(element)
}

/*
prints:
0
2
4
6
8
*/
26
Imanou Petit

Simplement, code de travail pour Swift 3.0:

let (first, last, interval) = (0, 100, 1)
var n = 0
for _ in stride(from: first, to: last, by: interval) {
    n += 1
}
12
Dan Rosenstark