Когда я использую цикл for в Playground, все работает нормально, пока я не изменил первый параметр цикла for на максимальное значение. (итерируется в порядке убывания)
Это ошибка? У кого-нибудь еще есть?
for index in 510..509
{
var a = 10
}
Счетчик, отображающий количество итераций, которые будут выполняться, продолжает тикать...
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. Если вы хотите посмотреть, как это работает, проверьте историю изменений.
5.stride(to: 1, by: -1)
или 5.stride(through: 1, by: -1)
Примените обратную функцию к диапазону для итерации назад:
Для 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
}
stride
.
reversed
может скопировать коллекцию, хотя. По крайней мере, так я понимаю документацию по Data
.
Обновлено для 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
разницу.
Вперед с шагом 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
У @matt есть интересное решение, где он определяет свой собственный обратный оператор и называет его >>>
. Для определения не требуется много кода, и он используется так:
for index in 5>>>0 {
print(index)
}
// 4
// 3
// 2
// 1
// 0
Проверьте на C-Style для петель, удаленных из Swift 3
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
Для Swift 2.0 и выше вы должны применить обратное к коллекции диапазона
for i in (0 ..< 10).reverse() {
// process
}
Он был переименован в .reversed() в Swift 3.0
С Swift 4.2, в соответствии с вашими потребностями, вы можете выбрать один из четырех следующих примеров кода Playground, чтобы решить вашу проблему.
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
*/
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
*/
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
*/
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
*/
В Swift 4 и более поздних
let count = 50//For example
for i in (1...count).reversed() {
print(i)
}
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
}
Если требуется выполнить итерацию по массиву (Array
или, как правило, любой SequenceType
) в обратном порядке. У вас есть несколько дополнительных опций.
Сначала вы можете reverse()
массива и пропустить его как обычно. Однако я предпочитаю использовать enumerate()
большую часть времени, так как он выводит кортеж, содержащий объект и индекс.
Здесь следует отметить, что важно называть их в правильном порядке:
for (index, element) in array.enumerate().reverse()
дает индексы в порядке убывания (чего я обычно ожидаю). в то время как:
for (index, element) in array.reverse().enumerate()
(что ближе к NSArray reverseEnumerator
)
перемещает массив назад, но выводит восходящие индексы.
как для 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
Вы можете использовать метод reversed() для легкого реверсирования значений.
var i:Int
for i in 1..10.reversed() {
print(i)
}
Метод reversed() переворачивает значения.
Вместо этого вы можете использовать цикл C-Style while
. Это очень хорошо работает в Swift 3:
var i = 5
while i > 0 {
print(i)
i -= 1
}
var sum1 = 0
for i in 0...100{
sum1 += i
}
print (sum1)
for i in (10...100).reverse(){
sum1 /= i
}
print(sum1)