Swift 3 для цикла с приращением

Как мне написать следующее в Swift3?

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

Это моя собственная попытка

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

person user462990    schedule 11.05.2016    source источник


Ответы (5)


Swift 2.2 -> 3.0: Strideable:s stride(...) заменены глобальными stride(...) функциями

В Swift 2.2 мы можем (как вы пытались в своей собственной попытке) использовать стандартные (и реализованные по умолчанию) функции stride(through:by:) и stride(to:by:) из протокола 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')

Принимая во внимание, что в Swift 3.0 эти две функции были удалены из Strideable в пользу глобальных функций stride(from:through:by:) и stride(from:to:by:) ; следовательно, эквивалентная версия Swift 3.0 вышеизложенного следует как

/* 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) { }

В вашем примере вы хотите использовать альтернативу шага с закрытым интервалом stride(from:through:by:), поскольку инвариант в вашем цикле for использует сравнение с меньшим или равным (<=). т.е.

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

Где, естественно, мы используем ваш цикл for только как пример перехода от цикла for к циклу stride, так как вы можете, естественно, для вашего конкретного примера просто вычислить n без необходимости цикла (n=1+(last-first)/interval).

Swift 3.0: альтернатива stride для более сложной логики итерации приращения.

С реализацией предложения по развитию SE-0094, Swift 3.0 представил глобальные sequence функции:

что может быть подходящей альтернативой stride для случаев с более сложным отношением приращения итерации (что не так в этом примере).

Декларации

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>

Мы кратко рассмотрим первую из этих двух функций. Аргументы next принимают замыкание, которое применяет некоторую логику для ленивого построения следующего элемента последовательности с учетом текущего (начиная с first). Последовательность завершается, когда next возвращает nil, или бесконечно, если next никогда не возвращает nil.

Применительно к приведенному выше простому примеру с постоянным шагом метод sequence немного многословен и излишен по сравнению с другими. подходящее для этой цели 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

Однако функции sequence становятся очень полезными для случаев с непостоянным шагом, например. как в примере, описанном в следующих вопросах и ответах:

Просто позаботьтесь о завершении последовательности с возможным возвратом nil (если нет: «бесконечная» генерация элементов), или, когда выйдет Swift 3.1, используйте его ленивую генерацию в сочетании с методом prefix(while:) для последовательностей, как описано в предложении по эволюции. SE-0045. Последнее, примененное к текущему примеру этого ответа, делает подход sequence менее подробным, явно включая критерии завершения генерации элемента.

/* 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
person dfrib    schedule 16.05.2016
comment
разве мы не отошли от глобальных функций в Swift 2 и теперь возвращаемся к ним в Swift 3? <<вздох>> - person Dan Rosenstark; 22.03.2017

В Swift 5 вы можете выбрать один из 5 следующих примеров, чтобы решить свою проблему.


№1. Использование функции stride(from:to:by:)

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. Использование функции sequence(first:next:)

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. Использование инициализатора AnySequence init(_:)

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. Использование метода CountableRange filter(_:)

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. Использование метода CountableRange flatMap(_:)

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
*/
person Imanou Petit    schedule 29.06.2017

Просто рабочий код для Swift 3.0:

let (first, last, interval) = (0, 100, 1)
var n = 0
for _ in stride(from: first, to: last, by: interval) {
    n += 1
}
person Dan Rosenstark    schedule 15.03.2017
comment
Если вы просматриваете массив и хотите получить индекс, вы можете использовать enumerated(), как показано здесь: stackoverflow.com/a/24028458 /8047 - person Dan Rosenstark; 22.03.2017

Мы также можем использовать цикл while как альтернативный способ

while first <= last {
    first += interval
}
person Thein    schedule 04.04.2020

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

person user462990    schedule 11.05.2016
comment
Вы не можете сказать 0.stride в Swift 3. - person matt; 16.05.2016