Làm thế nào để lặp lại vòng lặp theo thứ tự ngược trong swift?


186

Khi tôi sử dụng vòng lặp for trong Playground, mọi thứ đều hoạt động tốt, cho đến khi tôi thay đổi tham số đầu tiên của vòng lặp for thành giá trị cao nhất. (lặp theo thứ tự giảm dần)

Đây có phải là một lỗi? Có ai khác có nó?

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

Bộ đếm hiển thị số lần lặp sẽ được thực hiện tiếp tục ...

nhập mô tả hình ảnh ở đây


3
bản sao có thể có của Reverse Range trong Swift
Matt Gibson

1
Như một quyết định tuyệt vời để thả các vòng kiểu c. Mang chúng trở lại
Joel Teply

Xem câu trả lời của tôi với Swift 5 hiển thị tối đa 4 cách khác nhau để lặp theo thứ tự ngược lại.
Imanou Petit

Câu trả lời:


229

Xcode 6 beta 4 đã thêm hai hàm để lặp lại trên các phạm vi với một bước khác với một : stride(from: to: by:), được sử dụng với các phạm vi độc quyền và stride(from: through: by:), được sử dụng với các phạm vi bao gồm.

Để lặp lại trên một phạm vi theo thứ tự ngược lại, chúng có thể được sử dụng như dưới đây:

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

Lưu ý rằng cả hai không phải là một Rangechức năng thành viên. Chúng là các hàm toàn cục trả về một StrideTohoặc một StrideThroughcấu trúc, được định nghĩa khác với Rangecấu trúc.

Một phiên bản trước của câu trả lời này đã sử dụng by()chức năng thành viên của Rangestruct, đã bị xóa trong phiên bản beta 4. Nếu bạn muốn xem nó hoạt động như thế nào, hãy kiểm tra lịch sử chỉnh sửa.


26
Với Swift 2.0, giờ là: 5.stride(to: 1, by: -1)hoặc5.stride(through: 1, by: -1)
Binary

6
Với Swift 3.0, chúng tôi trở lại sải bước như một chức năng miễn phí.
Cezar

@Shaun nó là một lựa chọn hợp lý và an toàn. Tôi không thể nói với bạn thường là các lập trình viên R bị vấp bởi :toán tử của nó, lặp đi lặp lại xuống dưới, khiến các vòng lặp của họ thực thi khi họ nghĩ rằng nó sẽ bỏ qua chúng. Nó rất thuận tiện cho việc sử dụng tương tác, nhưng rất dễ xảy ra lỗi trong các tập lệnh, đến mức mà các hướng dẫn về phong cách khuyên bạn không nên sử dụng nó .
Davor Cuba

212

Áp dụng hàm đảo ngược cho phạm vi để lặp lại:

Dành cho Swift 1.2 trở về trước:

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

Nó cũng hoạt động với phạm vi nửa mở:

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

Lưu ý: reverse(1...10)tạo một mảng kiểu [Int], vì vậy trong khi điều này có thể tốt cho các phạm vi nhỏ, thì nên sử dụng lazynhư hiển thị bên dưới hoặc xem xét stridecâu trả lời được chấp nhận nếu phạm vi của bạn lớn.


Để tránh tạo ra một mảng lớn, sử dụng lazycùng với reverse(). Thử nghiệm sau đây chạy hiệu quả trong Sân chơi cho thấy nó không tạo ra một mảng với một nghìn tỷ Intgiây!

Kiểm tra:

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

Đối với Swift 2.0 trong Xcode 7:

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

Lưu ý rằng trong Swift 2.0, (1...1_000_000_000_000).reverse()thuộc loại ReverseRandomAccessCollection<(Range<Int>)>, vì vậy điều này hoạt động tốt:

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

Đối với Swift 3.0 reverse() đã được đổi tên thành reversed():

for i in (1...10).reversed() {
    print(i) // prints 10 through 1
}

3
reversedcó thể sao chép bộ sưu tập, mặc dù. Ít nhất, đó là cách tôi hiểu tài liệu trên Data.
Raphael

96

Đã cập nhật cho Swift 3

Câu trả lời dưới đây là một bản tóm tắt của các tùy chọn có sẵn. Chọn một trong những phù hợp nhất với nhu cầu của bạn.

reversed: số trong một phạm vi

Ở đằng trước

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

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

Phía sau

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

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

reversed: các yếu tố trong SequenceType

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

Ở đằng trước

for animal in animals {
    print(animal)
}

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

Phía sau

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

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

reversed: các phần tử có chỉ mục

Đôi khi một chỉ số là cần thiết khi lặp qua một bộ sưu tập. Cho rằng bạn có thể sử dụng enumerate(), trả về một tuple. Phần tử đầu tiên của bộ dữ liệu là chỉ mục và phần tử thứ hai là đối tượng.

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

Ở đằng trước

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

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

Phía sau

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

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

Lưu ý rằng như Ben Lachman đã lưu ý trong câu trả lời của mình , có lẽ bạn muốn làm .enumerated().reversed()hơn là .reversed().enumerated()(điều này sẽ làm cho số chỉ số tăng lên).

sải chân: số

Stride là cách để lặp mà không cần sử dụng một phạm vi. Có hai hình thức. Các nhận xét ở cuối mã cho biết phiên bản phạm vi sẽ là gì (giả sử kích thước tăng là 1).

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

Ở đằng trước

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

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

Phía sau

Thay đổi kích thước gia tăng để -1cho phép bạn đi lùi.

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

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

Lưu ý tothroughsự khác biệt.

bước tiến: các yếu tố của SequenceType

Chuyển tiếp theo gia số 2

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

Tôi đang sử dụng 2trong ví dụ này chỉ để thể hiện một khả năng khác.

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

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

Phía sau

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

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

Ghi chú


52

Swift 4 trở đi

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

28

Với Swift 5, theo nhu cầu của bạn, bạn có thể chọn một trong bốn ví dụ mã Playground sau để giải quyết vấn đề của mình.


# 1. Sử dụng ClosedRange reversed()phương pháp

ClosedRangecó một phương thức gọi là reversed(). reversed()phương thức có khai báo sau:

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

Trả về một khung nhìn trình bày các yếu tố của bộ sưu tập theo thứ tự ngược lại.

Sử dụng:

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

for index in reversedCollection {
    print(index)
}

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

Để thay thế, bạn có thể sử dụng Range reversed()phương pháp:

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

for index in reversedCollection {
    print(index)
}

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

# 2. Sử dụng sequence(first:next:)chức năng

Thư viện tiêu chuẩn Swift cung cấp một chức năng được gọi là sequence(first:next:). sequence(first:next:)có tuyên bố sau:

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

Trả về một chuỗi được hình thành từ firstvà lặp đi lặp lại các ứng dụng lười biếng của next.

Sử dụng:

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. Sử dụng stride(from:through:by:)chức năng

Thư viện tiêu chuẩn Swift cung cấp một chức năng được gọi là stride(from:through:by:). stride(from:through:by:)có tuyên bố sau:

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

Trả về một chuỗi từ một giá trị bắt đầu về phía, và có thể bao gồm, một giá trị cuối, từng bước theo số lượng được chỉ định.

Sử dụng:

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

for index in sequence {
    print(index)
}

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

Thay thế, bạn có thể sử dụng 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. Sử dụng bộ AnyIterator init(_:)khởi tạo

AnyIteratorcó một trình khởi tạo được gọi là init(_:). init(_:)có tuyên bố sau:

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

Tạo một trình vòng lặp bao bọc các bao đóng đã cho trong next()phương thức của nó .

Sử dụng:

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

Nếu cần, bạn có thể cấu trúc lại mã trước đó bằng cách tạo một phương thức mở rộng cho Intvà gói trình lặp của bạn trong đó:

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

chuỗi (đầu tiên: 5, tiếp theo: {($ 0> 0)? ($ 0 - 1): nil}) // đơn giản hơn ($ 0 - 1> = 0)
SirEnder

27

Đối với Swift 2.0 trở lên, bạn nên áp dụng ngược lại trên bộ sưu tập phạm vi

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

Nó đã được đổi tên thành .reversed () trong Swift 3.0


9

Trong Swift 4 và sau này

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

5

Swift 4.0

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

Nếu bạn muốn bao gồm togiá trị:

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

3

Nếu ai đó muốn lặp lại qua một mảng ( Arrayhay nói chung là bất kỳSequenceType ) ngược lại. Bạn có một vài lựa chọn bổ sung.

Đầu tiên bạn có thể reverse()mảng và lặp qua nó như bình thường. Tuy nhiên tôi thích sử dụngenumerate() phần lớn thời gian vì nó tạo ra một tuple chứa đối tượng và chỉ mục của nó.

Một điều cần lưu ý ở đây là điều quan trọng là gọi những thứ này theo đúng thứ tự:

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

mang lại các chỉ số theo thứ tự giảm dần (đó là những gì tôi thường mong đợi). trong khi:

for (index, element) in array.reverse().enumerate() (phù hợp gần hơn với NSArray's reverseEnumerator )

đi ngược lại mảng nhưng xuất ra các chỉ số tăng dần.


3

như đối với Swift 2.2, Xcode 7.3 (10, tháng 6 năm 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)")
}

Đầu ra:

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

2

Bạn có thể xem xét sử dụng whilevòng lặp C-Style thay thế. Điều này chỉ hoạt động tốt trong Swift 3:

var i = 5 
while i > 0 { 
    print(i)
    i -= 1
}

2

Bạn có thể sử dụng phương thức Reverse () để dễ dàng đảo ngược các giá trị.

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

Phương thức Reverse () đảo ngược các giá trị.


Tại sao bạn lại thêm một câu trả lời giống hệt với câu trả lời đã có? (Ví dụ: @ 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.