จะวนซ้ำในลำดับย้อนกลับอย่างรวดเร็วได้อย่างไร


187

เมื่อฉันใช้ for for loop ในสนามเด็กเล่นทุกอย่างทำงานได้ดีจนกระทั่งฉันเปลี่ยนพารามิเตอร์แรกของ for loop ให้เป็นค่าสูงสุด (ซ้ำตามลำดับจากมากไปน้อย)

นี่เป็นข้อบกพร่องหรือไม่? มีคนอื่นไหม

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

ตัวนับที่แสดงจำนวนการวนซ้ำที่จะประมวลผลทำให้เกิดการฟ้องร้อง ...

ป้อนคำอธิบายรูปภาพที่นี่


3
เป็นไปได้ซ้ำของReverse Range in Swift
Matt Gibson

1
การตัดสินใจที่ยอดเยี่ยมในการวางลูปสไตล์ c นำพวกเขากลับมา
โจเอล Teply

ดูคำตอบของฉันกับ Swift 5ที่แสดงวิธีการวนซ้ำได้ถึง 4 วิธีในลำดับย้อนกลับ
Imanou Petit

คำตอบ:


230

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ฟังก์ชั่นสมาชิก พวกมันเป็นฟังก์ชั่นระดับโลกที่ส่งกลับทั้ง a StrideToหรือStrideThroughstruct ซึ่งถูกกำหนดแตกต่างจากRangestruct

รุ่นก่อนหน้าของคำตอบนี้ใช้by()ฟังก์ชันสมาชิกของRangestruct ซึ่งถูกลบออกในเบต้า 4 หากคุณต้องการดูวิธีการทำงานให้ตรวจสอบประวัติการแก้ไข


26
ด้วย Swift 2.0 ตอนนี้5.stride(to: 1, by: -1)หรือ:5.stride(through: 1, by: -1)
Binarian

6
ด้วย Swift 3.0 เรากลับไปก้าวเป็นฟังก์ชั่นฟรี
Cezar

@ Shaun มันเป็นตัวเลือกที่เหมาะสมและปลอดภัย ฉันไม่สามารถบอกคุณได้บ่อยนักโปรแกรมเมอร์ R นั้นถูกทาบทามโดย:โอเปอเรเตอร์ของมันโดยการวนซ้ำลงไปโดยปริยาย มันสะดวกมากสำหรับการใช้งานแบบโต้ตอบ แต่มีข้อผิดพลาดเกิดขึ้นบ่อยในสคริปต์จนถึงจุดที่แนะนำสไตล์ไม่แนะนำให้ใช้
Davor Cubranic

213

ใช้ฟังก์ชั่นย้อนกลับกับช่วงเพื่อทำซ้ำย้อนหลัง:

สำหรับ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คำตอบที่ยอมรับถ้าช่วงของคุณมีขนาดใหญ่


เพื่อหลีกเลี่ยงการสร้าง array ขนาดใหญ่ใช้พร้อมกับlazy reverse()การทดสอบต่อไปนี้ทำงานอย่างมีประสิทธิภาพในสนามเด็กเล่นที่แสดงว่ามันไม่ได้สร้างอาร์เรย์ด้วยหนึ่งล้านล้านInts!

ทดสอบ:

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
}

3
reversedอาจคัดลอกคอลเลกชันได้ Dataอย่างน้อยที่เป็นวิธีที่ผมเข้าใจเอกสารบน
Raphael

96

อัปเดตสำหรับ 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()ซึ่งผลตอบแทน tuple องค์ประกอบแรกของ tuple คือดัชนีและองค์ประกอบที่สองคือวัตถุ

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

โปรดทราบว่าตามที่ Ben Lachman บันทึกไว้ในคำตอบของเขาคุณอาจต้องการทำ.enumerated().reversed()มากกว่า.reversed().enumerated()(ซึ่งจะทำให้ตัวเลขดัชนีเพิ่มขึ้น)

กางเกง: ตัวเลข

สาวเท้าเป็นวิธีการย้ำโดยไม่ต้องใช้ช่วง มีสองรูปแบบ ความคิดเห็นที่ท้ายรหัสแสดงว่ารุ่นของช่วงจะเป็นอย่างไร (โดยสมมติว่าขนาดที่เพิ่มขึ้นคือ 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ความแตกต่าง

กางเกง: องค์ประกอบของ SequenceType

ส่งต่อโดยการเพิ่มทีละ 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 

หมายเหตุ


52

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

28

ด้วย Swift 5 ตามความต้องการของคุณคุณสามารถเลือกหนึ่งในสี่ตัวอย่างรหัสสนามเด็กเล่นต่อไปนี้เพื่อแก้ไขปัญหาของคุณ


# 1 ใช้ClosedRange reversed()วิธีการ

ClosedRangereversed()มีวิธีการที่เรียกว่า 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
*/

# 2 การใช้sequence(first:next:)ฟังก์ชั่น

sequence(first:next:)สวิฟท์มาตรฐานห้องสมุดให้ฟังก์ชั่นที่เรียกว่า sequence(first:next:)มีการประกาศดังต่อไปนี้:

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

คืนลำดับที่เกิดจากการซ้ำการใช้งานของคนขี้เกียจfirstnext

การใช้งาน:

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 การใช้stride(from:through:by:)ฟังก์ชั่น

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

# 4 ใช้AnyIterator init(_:)initializer

AnyIteratorinit(_:)มีการเริ่มต้นที่เรียกว่า 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
*/

ลำดับ (แรก: 5, ถัดไป: {($ 0> 0)) ($ 0 - 1): ไม่มี}} // ง่ายกว่า ($ 0 - 1> = 0)
SirEnder

27

สำหรับ Swift 2.0 ขึ้นไปคุณควรใช้การย้อนกลับของคอลเลกชันช่วง

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

มันได้รับการเปลี่ยนชื่อเป็น. reversed () ใน Swift 3.0




3

หากมีใครต้องการย้ำผ่านอาร์เรย์ ( Arrayหรือมากกว่าใด ๆ ทั่วไปSequenceType) ในสิ่งที่ตรงกันข้าม คุณมีตัวเลือกเพิ่มเติมเล็กน้อย

ก่อนอื่นคุณสามารถreverse()อาร์เรย์และวนรอบมันได้ตามปกติ อย่างไรก็ตามฉันชอบที่จะใช้enumerate()เวลาส่วนใหญ่เนื่องจากมันจะส่งออก tuple ที่มีวัตถุและเป็นดัชนี

สิ่งหนึ่งที่ควรทราบที่นี่คือการเรียกสิ่งเหล่านี้ตามลำดับที่ถูกต้อง:

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

ผลผลิตดัชนีตามลำดับจากมากไปน้อย (ซึ่งเป็นสิ่งที่ฉันคาดหวังโดยทั่วไป) ขณะที่:

for (index, element) in array.reverse().enumerate()(ซึ่งใกล้เคียงกับ NSArray มากขึ้นreverseEnumerator)

เดินไปข้างหลังอาร์เรย์ แต่ผลผลิตดัชนีจากน้อยไปมาก


3

สำหรับ 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

2

คุณสามารถลองใช้การwhileวนซ้ำC-Style แทน วิธีนี้ใช้ได้ผลดีใน Swift 3:

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

2

คุณสามารถใช้วิธี reversed () เพื่อกลับค่าได้อย่างง่ายดาย

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

วิธีการตรงกันข้าม () วิธีการย้อนกลับค่า


ทำไมคุณต้องเพิ่มคำตอบเหมือนกันกับคำตอบที่มีอยู่แล้ว? (เช่น @ Rohit's.)
Davor Cubranic

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.