Swift'de Ters Mesafe


105

Swift'de ters aralıklarla çalışmanın bir yolu var mı?

Örneğin:

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

sonsuz bir döngüdür.

Swift'in daha yeni sürümlerinde bu kod derlenir, ancak çalışma zamanında şu hatayı verir:

Önemli hata: UpperBound <lowerBound ile Aralık oluşturulamıyor

Bunun 1..5yerine kullanabileceğimi , hesaplayabileceğimi j = 6 - ive indeksim jolarak kullanabileceğimi biliyorum . Daha okunaklı bir şey olup olmadığını merak ediyordum.


Swift 3 ile probleminizi çözmeniz için 8 yolu sunan benzer bir soru için cevabımı görün .
Imanou Petit

Yanıtlar:


184

En son Swift 3 için Güncelleme (Swift 4'te hala çalışıyor)

Sen kullanabilirsiniz reversed()bir dizi yöntem

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

Veya stride(from:through:by:)yöntem

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

stide(from:to:by:) benzerdir ancak son değeri hariç tutar

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

En son Swift 2 için Güncelleme

Her şeyden önce, protokol uzantıları nasıl reversekullanıldığını değiştirir :

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

Stride, Xcode 7 Beta 6'da yeniden çalışıldı. Yeni kullanım:

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

Şunlar için de çalışır Doubles:

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

Burada sınırlar için kayan nokta karşılaştırmalarına karşı dikkatli olun.

Swift 1.2 için önceki düzenleme : Xcode 6 Beta 4 itibariyle, by ve ReverseRange artık mevcut değil: [

Sadece bir aralığı ters çevirmek istiyorsanız, ihtiyacınız olan tek şey ters işlevdir:

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

0x7fffffff tarafından yayınlandığı gibi , gelişigüzel tamsayılarla yineleme ve artış için kullanılabilecek yeni bir adım yapısı vardır. Apple ayrıca kayan nokta desteğinin geldiğini belirtti.

Cevabından kaynaklandı:

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
}

FYI Stride beta 6'dan beri değişti
DogCoffee

1
olmalıdır (1...5).reverse()(bir işlev çağrısı olarak), lütfen cevabınızı güncelleyin. (Yalnızca iki karakter olduğu için gönderinizi düzenleyemedim.)
Behdad

Swift 3.0-ÖNİZLEME-4'te (ve muhtemelen daha önce), öyle olmalıdır (1...5).reversed(). Ayrıca, örnek .stride()çalışmıyor ve stride(from:to:by:)bunun yerine ücretsiz işlevi gerektiriyor .
Davida

2
Görünüşe göre strideswift 3'ün kodu biraz yanlış. 1 rakamını eklemek için, bunun throughaksine kullanmanız gerekir to:for i in stride(from:5,through:1,by:-1) { print(i) }
262Hz

4
reversedO (1) karmaşıklığına sahip olduğuna işaret etmeye değer çünkü yalnızca orijinal koleksiyonu sarıyor ve onu oluşturmak için bir yineleme gerektirmiyor.
devios1

21

Bunun asimetrisiyle ilgili rahatsız edici bir şey var:

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

... bunun aksine:

for i in 1..<5 {

Bu, ne zaman bir ters aralık yapmak istediğimde, parantezleri koymam gerektiğini ve ayrıca bunu .reverse()baş parmağım gibi çıkarak sonuna yazmam gerektiği anlamına gelir. Bu, simetrik sayma ve geri sayma olan döngüler için C-stiline kıyasla gerçekten çirkin. Bu yüzden bunun yerine döngüler için C stili kullanma eğilimindeydim. Ancak Swift 2.2'de, döngüler için C stili ortadan kalkıyor! Bu yüzden, döngüler için tüm azalan C-stilimi bu çirkin .reverse()yapıyla değiştirmek için acele etmek zorunda kaldım - her zaman merak ediyorum, neden dünyada bir ters menzil operatörü yok?

Fakat bekle! Bu Swift - kendi operatörlerimizi tanımlama iznimiz var !! İşte başlıyoruz:

infix operator >>> {
    associativity none
    precedence 135
}

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

Şimdi şunu söylememe izin var:

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

Bu kapaklar benim kodunda meydana, ama sadece en sık karşılaşılan durum olduğunu o anda tüm ı gerek bu yüzden, farkla en sık karşılaşılan durum.

Operatörle bir tür iç kriz yaşadım. Bunun >..tersi olarak kullanmak isterdim ..<, ancak bu yasal değil: nokta olmayan bir noktadan sonra nokta kullanamazsınız, öyle görünüyor. Düşündüm ..>ama ayırt etmenin çok zor olduğuna karar verdim ..<. Güzel olan yanı >>>size haykırması: " aşağıya !" (Elbette başka bir operatör bulmakta özgürsünüz. Ama benim tavsiyem şu: süper simetri için, <<<ne işe yarayacağını tanımlayın ..<ve şimdi sahip olduğunuz <<<ve >>>simetrik ve yazması kolay olan.)


Swift 3 sürümü (Xcode 8 tohum 6):

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

Swift 4 sürümü (Xcode 9 beta 3):

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

Swift 4.2 sürümü (Xcode 10 beta 1):

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

Swift 5 sürümü (Xcode 10.2.1):

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

1
Vay canına, >>>operatör gerçekten harika! Umarım Swift tasarımcıları böyle şeylere dikkat ederler çünkü reverse()parantez içindeki ifadelerin sonuna yapıştırmak tuhaf geliyor. 5>..0Aralıklarla otomatik olarak başa çıkamamalarının hiçbir nedeni yoktur , çünkü ForwardIndexTypeprotokol distanceTo(), adımın pozitif mi yoksa negatif mi olması gerektiğini anlamak için kullanılabilecek tamamen makul bir operatör sağlar .
dasblinkenlight

1
Teşekkürler @dasblinkenlight - Bu benim için geldi çünkü uygulamalarımdan birinde döngüler için Swift C stiline taşınan ve şimdi döngüler için C stili nedeniyle dönüştürülmek zorunda kalan çok sayıda döngü için (Objective-C'de) C vardı uzağa gidiyor. Bu sadece korkunçtu, çünkü bu döngüler uygulamamın mantığının özünü temsil ediyordu ve kırılma riski taşıyan her şeyi yeniden yazmak. Bu nedenle, (yorumlanmış) C tarzı gösterimle yazışmayı olabildiğince açık hale getirecek bir gösterim istedim.
matt

temiz! Temiz görünen kod saplantınızı beğendim!
Yohst

10

Betalarda ilerledikçe bu sorunun yanıtları biraz değişmiş görünüyor. Beta 4 itibariyle, hem by()işlev hem de ReversedRangetür dilden kaldırılmıştır. Ters bir aralık yapmak istiyorsanız, seçenekleriniz şimdi aşağıdaki gibidir:

1: İleri bir aralık oluşturun ve ardından reverse()bunu tersine çevirmek için işlevi kullanın.

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: stride()Başlangıç ​​ve bitiş dizinlerini ve yineleme miktarını belirleme işlevlerini içeren beta 4'e eklenen yeni işlevleri kullanın .

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
}

Bu gönderiye yeni özel aralık operatörünü de dahil ettiğimi unutmayın. ..ile değiştirildi ..<.

Düzenleme: Apple , Xcode 6 beta 5 sürüm notlarından , bunun üstesinden gelmek için aşağıdaki öneriyi ekledi:

ReverseRange kaldırıldı; tembel kullan (x ..

İşte bir örnek.

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

+1 Bir referans arıyorum lazy(0....5).reverse()ve beta 5 sürüm notlarını göremiyorum. Bu konuyla ilgili başka tartışmalar biliyor musunuz? Ayrıca, forbilginize, bu döngüdeki sayılar örneğinizde geriye doğru.
Rob

1
@Rob Hmm Bir beta sürüm notlarının sonunda kaldırılacağını tahmin etmeliydim. Bunu yazdığımda, lazy () için bir referans gördüğüm tek yer orasıydı, ancak memnuniyetle etrafa biraz daha bakacağım ve daha sonra eve geldiğimde bunu güncelleyeceğim / düzelteceğim. Bunu işaret ettiğiniz için teşekkürler.
Mick MacCallum

1
@ 0x7fffffff Son örneğinizde yorum yazıyor // 0, 1, 2, 3, 4, 5ama aslında tersine çevrilmesi gerekiyor. Yorum yanıltıcıdır.
Pang

5

Xcode 7, beta 2:

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

3

Swift 3, 4+ : Bunu şu şekilde yapabilirsiniz:

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

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

sonuç : 10, 9, 8 ... 0

İstediğiniz gibi özelleştirebilirsiniz. Daha fazla bilgi için referansı okuyunfunc sequence<T>


1

Bu, bunu yapmanın başka bir yolu olabilir.

(1...5).reversed().forEach { print($0) }

-2

Ters sayı için Reverse () işlevi kullanılır.

Var n: Int // Numarayı girin

İ in 1 için ... n.reverse () {Print (i)}

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.