Reverse Range di Swift

105

Apakah ada cara untuk bekerja dengan rentang terbalik di Swift?

Sebagai contoh:

for i in 5...1 {
  // do something
}

adalah lingkaran tak terbatas.

Di versi Swift yang lebih baru yang dikompilasi kode, tetapi pada waktu proses memberikan kesalahan:

Kesalahan fatal: Tidak dapat membentuk Range dengan upperBound <lowerBound

Saya tahu saya dapat menggunakan 1..5, menghitung, j = 6 - idan menggunakan jsebagai indeks saya. Saya hanya ingin tahu apakah ada yang lebih terbaca?

Eduardo
sumber
Lihat jawaban saya untuk pertanyaan serupa yang memberikan hingga 8 cara untuk menyelesaikan masalah Anda dengan Swift 3.
Imanou Petit

Jawaban:

184

Perbarui Untuk Swift 3 terbaru (masih berfungsi di Swift 4)

Anda dapat menggunakan reversed()metode ini pada jarak tertentu

for i in (1...5).reversed() { print(i) } // 5 4 3 2 1

Atau stride(from:through:by:)metode

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

stide(from:to:by:) serupa tetapi tidak termasuk nilai terakhir

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

Perbarui Untuk Swift terbaru 2

Pertama-tama, ekstensi protokol mengubah cara reversedigunakan:

for i in (1...5).reverse() { print(i) } // 5 4 3 2 1

Langkah telah dikerjakan ulang di Xcode 7 Beta 6. Penggunaan baru adalah:

for i in 0.stride(to: -8, by: -2) { print(i) } // 0 -2 -4 -6
for i in 0.stride(through: -8, by: -2) { print(i) } // 0 -2 -4 -6 -8

Ini juga berfungsi untuk Doubles:

for i in 0.5.stride(to:-0.1, by: -0.1) { print(i) }

Berhati-hatilah dengan perbandingan floating point di sini untuk batas-batasnya.

Pengeditan sebelumnya untuk Swift 1.2 : Sejak Xcode 6 Beta 4, oleh dan ReverseRange tidak ada lagi: [

Jika Anda hanya ingin membalikkan rentang, Anda hanya memerlukan fungsi sebaliknya :

for i in reverse(1...5) { println(i) } // prints 5,4,3,2,1

Seperti yang diposting oleh 0x7fffffff, ada konstruksi langkah baru yang dapat digunakan untuk mengulangi dan menambah dengan bilangan bulat arbitrer. Apple juga menyatakan bahwa dukungan floating point akan datang.

Bersumber dari jawabannya:

for x in stride(from: 0, through: -8, by: -2) {
    println(x) // 0, -2, -4, -6, -8
}

for x in stride(from: 6, to: -2, by: -4) {
    println(x) // 6, 2
}
Mendongkrak
sumber
FYI Stride telah berubah sejak beta 6
DogCoffee
1
seharusnya (1...5).reverse()(sebagai panggilan fungsi), perbarui jawaban Anda. (Karena hanya dua karakter, saya tidak bisa mengedit posting Anda.)
Behdad
Di Swift 3.0-PREVIEW-4 (dan mungkin sebelumnya), seharusnya (1...5).reversed(). Juga, contoh untuk .stride()tidak berfungsi, dan membutuhkan fungsi gratis stride(from:to:by:)sebagai gantinya.
davidA
2
Sepertinya stridekode untuk swift 3 sedikit salah. untuk memasukkan nomor 1, Anda perlu menggunakan throughlawan toseperti ini:for i in stride(from:5,through:1,by:-1) { print(i) }
262Hz
4
Perlu ditunjukkan bahwa reversedmemiliki kompleksitas O (1) karena ini hanya membungkus koleksi asli dan tidak memerlukan iterasi untuk membuatnya.
devios1
21

Ada sesuatu yang meresahkan tentang asimetri ini:

for i in (1..<5).reverse()

... sebagai lawan dari ini:

for i in 1..<5 {

Artinya setiap saya ingin melakukan reverse range, saya harus ingat untuk meletakkan tanda kurung, ditambah lagi saya harus menuliskannya .reverse()di bagian akhir, menonjol seperti jempol yang sakit. Ini benar-benar jelek dibandingkan dengan gaya C untuk loop, yang simetris menghitung ke atas dan menghitung mundur. Jadi saya cenderung menggunakan C-style untuk loop. Namun di Swift 2.2, loop C-style akan ditiadakan! Jadi saya harus buru-buru mengganti semua gaya C saya yang menurun untuk loop dengan .reverse()konstruksi jelek ini - bertanya-tanya selama ini, mengapa tidak ada operator jarak mundur?

Tapi tunggu! Ini Swift - kami diizinkan untuk menentukan operator kami sendiri !! Kita mulai:

infix operator >>> {
    associativity none
    precedence 135
}

func >>> <Pos : ForwardIndexType where Pos : Comparable>(end:Pos, start:Pos)
    -> ReverseRandomAccessCollection<(Range<Pos>)> {
        return (start..<end).reverse()
}

Jadi sekarang saya diizinkan untuk mengatakan:

for i in 5>>>1 {print(i)} // 4, 3, 2, 1

Ini hanya mencakup kasus paling umum yang terjadi dalam kode saya, tetapi ini merupakan kasus yang paling umum, jadi hanya itu yang saya butuhkan saat ini.

Saya mengalami semacam krisis internal yang datang dengan operator. Saya ingin menggunakan >.., sebagai kebalikan dari ..<, tetapi itu tidak legal: Anda tidak dapat menggunakan titik setelah non-titik, itu muncul. Saya mempertimbangkan ..>tetapi memutuskan itu terlalu sulit untuk dibedakan ..<. Hal yang menyenangkan tentang hal itu >>>adalah ia berteriak pada Anda: " turun ke!" (Tentu saja Anda bebas untuk mencari operator lain. Tetapi saran saya adalah: untuk simetri super, tentukan <<<untuk melakukan apa ..<, dan sekarang Anda sudah punya <<<dan >>>mana yang simetris dan mudah diketik.)


Versi Swift 3 (Xcode 8 seed 6):

infix operator >>> : RangeFormationPrecedence
func >>><Bound>(maximum: Bound, minimum: Bound) ->
    ReversedRandomAccessCollection<CountableRange<Bound>> 
    where Bound : Comparable, Bound.Stride : Integer {
        return (minimum..<maximum).reversed()
}

Versi Swift 4 (Xcode 9 beta 3):

infix operator >>> : RangeFormationPrecedence
func >>><Bound>(maximum: Bound, minimum: Bound)
    -> ReversedRandomAccessCollection<CountableRange<Bound>>
    where Bound : Comparable & Strideable { 
        return (minimum..<maximum).reversed()
}

Versi Swift 4.2 (Xcode 10 beta 1):

infix operator >>> : RangeFormationPrecedence
func >>><Bound>(maximum: Bound, minimum: Bound)
    -> ReversedRandomAccessCollection<Range<Bound>>
    where Bound : Strideable { 
        return (minimum..<maximum).reversed()
}

Versi Swift 5 (Xcode 10.2.1):

infix operator >>> : RangeFormationPrecedence
func >>><Bound>(maximum: Bound, minimum: Bound)
    -> ReversedCollection<Range<Bound>>
    where Bound : Strideable {
        return (minimum..<maximum).reversed()
}
Matt
sumber
1
Wah, >>>operatornya keren banget! Saya berharap desainer Swift memperhatikan hal-hal seperti itu, karena menempel reverse()di akhir ekspresi dalam tanda kurung memang terasa aneh. Tidak ada alasan mereka tidak dapat menangani 5>..0rentang secara otomatis, karena ForwardIndexTypeprotokol menyediakan distanceTo()operator yang sangat masuk akal yang dapat digunakan untuk mengetahui apakah langkahnya harus positif atau negatif.
dasblinkenlight
1
Terima kasih @dasblinkenlight - Ini muncul untuk saya karena di salah satu aplikasi saya, saya memiliki banyak C untuk loop (di Objective-C) yang bermigrasi ke gaya C Swift untuk loop dan sekarang harus dikonversi karena gaya C untuk loop akan pergi. Ini sangat menakutkan, karena loop ini mewakili inti logika aplikasi saya, dan menulis ulang semuanya berisiko menimbulkan kerusakan. Jadi saya menginginkan sebuah notasi yang akan membuat korespondensi dengan notasi C-style (diberi komentar) sejelas mungkin.
matt
rapi! Saya suka obsesi Anda dengan kode yang terlihat bersih!
Yohst
10

Tampaknya jawaban atas pertanyaan ini telah sedikit berubah seiring dengan kemajuan kami melalui beta. Mulai beta 4, baik by()fungsi dan ReversedRangejenisnya telah dihapus dari bahasa. Jika Anda ingin membuat rentang terbalik, opsi Anda sekarang adalah sebagai berikut:

1: Buat rentang maju, lalu gunakan reverse()fungsi untuk membalikkannya.

for x in reverse(0 ... 4) {
    println(x) // 4, 3, 2, 1, 0
}

for x in reverse(0 ..< 4) {
    println(x) // 3, 2, 1, 0
}

2: Gunakan stride()fungsi baru yang ditambahkan dalam beta 4, yang mencakup fungsi untuk menentukan indeks awal dan akhir, serta jumlah yang akan diiterasi.

for x in stride(from: 0, through: -8, by: -2) {
    println(x) // 0, -2, -4, -6, -8
}

for x in stride(from: 6, to: -2, by: -4) {
    println(x) // 6, 2
}

Perhatikan bahwa saya juga menyertakan operator jangkauan eksklusif baru di posting ini juga. ..diganti dengan ..<.

Sunting: Dari catatan rilis Xcode 6 beta 5 , Apple menambahkan saran berikut untuk menangani ini:

ReverseRange telah dihapus; gunakan malas (x ..

Berikut contohnya.

for i in lazy(0...5).reverse() {
    // 0, 1, 2, 3, 4, 5
}
Mick MacCallum
sumber
+1 Saya mencari referensi ke lazy(0....5).reverse()dan saya tidak melihat catatan rilis beta 5. Apakah Anda mengetahui diskusi lain tentang topik ini? Juga, FYI, angka-angka di dalam forloop itu mundur dalam contoh Anda.
Rob
1
@Rob Hmm Saya seharusnya mengira bahwa catatan rilis untuk beta pada akhirnya akan dihapus. Ketika saya menulis ini, itu adalah satu-satunya tempat saya telah melihat referensi ke lazy (), tetapi saya dengan senang hati akan melihat-lihat lagi dan memperbarui / memperbaiki ini ketika saya pulang nanti. Terima kasih telah menunjukkan hal ini.
Mick MacCallum
1
@ 0x7fffffff Dalam contoh terakhir Anda, komentar mengatakan // 0, 1, 2, 3, 4, 5tetapi sebenarnya harus dibalik. Komentar itu menyesatkan.
Pang
5

Xcode 7, beta 2:

for i in (1...5).reverse() {
  // do something
}
leanne
sumber
3

Swift 3, 4+ : Anda dapat melakukannya seperti ini:

for i in sequence(first: 10, next: {$0 - 1}) {

    guard i >= 0 else {
        break
    }
    print(i)
}

hasil : 10, 9, 8 ... 0

Anda dapat menyesuaikannya sesuka Anda. Untuk info lebih lanjut baca func sequence<T>referensi

nCod3d
sumber
1

Ini bisa menjadi cara lain untuk melakukan ini.

(1...5).reversed().forEach { print($0) }
David H.
sumber
-2

Fungsi Reverse () digunakan untuk bilangan terbalik.

Var n: Int // Masukkan nomor

Untuk i in 1 ... n.reverse () {Print (i)}

laxrajpurohit
sumber