Swift switch deyiminden küçük veya büyük


145

switchSwift'teki ifadelere aşinayım , ancak bu kod parçasının nasıl değiştirileceğini merak ediyorum switch:

if someVar < 0 {
    // do something
} else if someVar == 0 {
    // do something else
} else if someVar > 0 {
    // etc
}

Bu ilginç bir soru olsa da, anahtarı kullanarak kod if ifadelerinden çok daha az okunabilir olduğunu düşünüyorum. Sadece yapabileceğiniz için yapmanız gerektiği anlamına gelmez.
Rog

Yanıtlar:


241

İşte bir yaklaşım. someVarBir Intveya başka olduğunu varsayarsak , Comparableisteğe bağlı olarak işleneni yeni bir değişkene atayabilirsiniz. Bu, whereanahtar kelimeyi kullanarak istediğiniz şekilde kapsamlamanızı sağlar :

var someVar = 3

switch someVar {
case let x where x < 0:
    print("x is \(x)")
case let x where x == 0:
    print("x is \(x)")
case let x where x > 0:
    print("x is \(x)")
default:
    print("this is impossible")
}

Bu biraz basitleştirilebilir:

switch someVar {
case _ where someVar < 0:
    print("someVar is \(someVar)")
case 0:
    print("someVar is 0")
case _ where someVar > 0:
    print("someVar is \(someVar)")
default:
    print("this is impossible")
}

whereAnahtar kelime aralığını aralık eşleme ile de tamamen önleyebilirsiniz :

switch someVar {
case Int.min..<0:
    print("someVar is \(someVar)")
case 0:
    print("someVar is 0")
default:
    print("someVar is \(someVar)")
}

9
default: fatalError()Olası mantık hatalarını erken tespit etmenizi öneririm .
Martin R

1
Teşekkürler! Bu örnekler çok yardımcı oluyor ve sorunumu çözüyorlar! (diğer örnekler de iyiydi, ama seninki bana çok yardımcı oldu)
Pieter

1
@MartinR assertionFailure, özellikle bir ekipte çalışırken daha güvenli bir seçenek gibi görünüyor.
Michael Voline

120

Swift 5 ile if ifadenizi değiştirmek için aşağıdaki anahtarlardan birini seçebilirsiniz.


# 1 PartialRangeFromve ile anahtarın kullanılmasıPartialRangeUpTo

let value = 1

switch value {
case 1...:
    print("greater than zero")
case 0:
    print("zero")
case ..<0:
    print("less than zero")
default:
    fatalError()
}

# 2 ClosedRangeve ile anahtarın kullanılmasıRange

let value = 1

switch value {
case 1 ... Int.max:
    print("greater than zero")
case Int.min ..< 0:
    print("less than zero")
case 0:
    print("zero")
default:
    fatalError()
}

# 3 Nerede fıkra ile anahtar kullanma

let value = 1

switch value {
case let val where val > 0:
    print("\(val) is greater than zero")
case let val where val == 0:
    print("\(val) is zero")
case let val where val < 0:
    print("\(val) is less than zero")
default:
    fatalError()
}

# 4 Nerede cümlecik ve atama ile anahtarı kullanma _

let value = 1

switch value {
case _ where value > 0:
    print("greater than zero")
case _ where value == 0:
    print("zero")
case _ where value < 0:
    print("less than zero")
default:
    fatalError()
}

# 5 RangeExpressionProtokol ~=(_:_:)operatörüyle anahtar kullanma

let value = 1

switch true {
case 1... ~= value:
    print("greater than zero")
case ..<0 ~= value:
    print("less than zero")
default:
    print("zero")
}

# 6 EquatableProtokol ~=(_:_:)operatörüyle anahtar kullanma

let value = 1

switch true {
case value > 0:
    print("greater than zero")
case value < 0:
    print("less than zero")
case 0 ~= value:
    print("zero")
default:
    fatalError()
}

7. ile anahtarı kullanarak PartialRangeFrom, PartialRangeUpTove RangeExpressions' contains(_:)yöntemi

let value = 1

switch true {
case (1...).contains(value):
    print("greater than zero")
case (..<0).contains(value):
    print("less than zero")
default:
    print("zero")
}

1
# 2'de varsayılan dava neden gereklidir? rannge Int.min'den Int.max'a ise ne kaldı?
μολὼν.λαβέ

Vay be, güzel seçenekler listesi. Bunu yapmanın birkaç yolu olduğunu bilmek güzel.
Christopher Pickslay

2
İyi bir genel görünüm ancak kusurludur, çünkü 0 ile 1 arasındaki sayılar hesaba katılmaz. yalnızca 1'deki sayıları kapsadığından 0.1ölümcül bir hata atar 1.... Bu nedenle, bu çözüm yalnızca bir ise çalışır value, Intancak bu tehlikelidir çünkü değişken türü değişirse işlevsellik herhangi bir derleyici hatası olmadan kesilir.
Manuel

1
Çözümünüz Double tipi için düzgün çalışmıyor. case 1 ...: print ("sıfırdan büyük") 0'dan büyük DEĞİLDİR 1'den büyük veya 1'e eşit
Vlad

20

switchAçıklamada, kaputun altında kullanır ~=operatörü. Yani bu:

let x = 2

switch x {
case 1: print(1)
case 2: print(2)
case 3..<5: print(3..<5)
default: break
}

Desugars:

if 1          ~= x { print(1) }
else if 2     ~= x { print(2) }
else if 3..<5 ~= x { print(3..<5) }
else {  }

Standart kütüphane referansına bakarsanız, tam olarak ne ~=yapılması gerektiğini söyleyebilir : dahil edilen aralık eşleme ve eşitlikli şeyler için eşitleme. (Std lib'deki bir işlev yerine bir dil özelliği olan enum-case eşleşmesi dahil değildir)

Sol tarafta düz bir boole ile eşleşmediğini göreceksiniz. Bu tür karşılaştırmalar için bir where ifadesi eklemeniz gerekir.

Tabii ... ~=operatörü kendiniz aşırı yüklemezseniz. (Bu genellikle edilir değil Bir olasılık böyle bir şey olacağını önerilir):

func ~= <T> (lhs: T -> Bool, rhs: T) -> Bool {
  return lhs(rhs)
}

Bu, soldaki bir boolean'ı sağdaki parametresine döndüren bir işlevle eşleşir. İşte bunun için kullanabileceğiniz bir şey:

func isEven(n: Int) -> Bool { return n % 2 == 0 }

switch 2 {
case isEven: print("Even!")
default:     print("Odd!")
}

Durumunuz için aşağıdaki gibi bir ifadeniz olabilir:

switch someVar {
case isNegative: ...
case 0: ...
case isPositive: ...
}

Ama şimdi yeni isNegativeveisPositive işlevleri . Daha fazla operatör yüklemediğiniz sürece ...

Kıvrımlı önek veya düzeltme sonrası operatörleri olarak normal infix operatörlerini aşırı yükleyebilirsiniz. İşte bir örnek:

postfix operator < {}

postfix func < <T : Comparable>(lhs: T)(_ rhs: T) -> Bool {
  return lhs < rhs
}

Bu şöyle çalışır:

let isGreaterThanFive = 5<

isGreaterThanFive(6) // true
isGreaterThanFive(5) // false

Bunu önceki işlevle birleştirdiğinizde, switch ifadeniz şöyle görünebilir:

switch someVar {
case 0< : print("Bigger than 0")
case 0  : print("0")
default : print("Less than 0")
}

Şimdi, muhtemelen bu tür şeyleri pratikte kullanmamalısınız: biraz tehlikeli. (Muhtemelen) whereifadeye sadık kalsan iyi olur . Bununla birlikte,

switch x {
case negative:
case 0:
case positive:
}

veya

switch x {
case lessThan(someNumber):
case someNumber:
case greaterThan(someNumber):
}

Dikkate değer olması için yeterince yaygın görünüyor.


1
soruya cevabın nerede? Onu bulamıyorum.
Bal

1
case 3 .. <5: print (3 .. <5) - Kelimenin tam anlamıyla ilk paragrafta. Bu cevabın hafife alınması. Beni çok kod kurtarıyor.
Karim

14

Yapabilirsin:

switch true {
case someVar < 0:
    print("less than zero")
case someVar == 0:
    print("eq 0")
default:
    print("otherwise")
}

6

Birisi zaten gönderdi yana case let x where x < 0:burada yer için bir alternatiftir someVarbir olduğunu Int.

switch someVar{
case Int.min...0: // do something
case 0: // do something
default: // do something
}

Ve işte someVara nerede olduğu için bir alternatif Double:

case -(Double.infinity)...0: // do something
// etc

6

Aralıklarla böyle görünüyor

switch average {
case 0..<40: //greater or equal than 0 and less than 40
    return "T"
case 40..<55: //greater or equal than 40 and less than 55
    return "D"
case 55..<70: //greater or equal than 55 and less than 70
    return "P"
case 70..<80: //greater or equal than 70 and less than 80
    return "A"
case 80..<90: //greater or equal than 80 and less than 90
    return "E"
case 90...100: //greater or equal than 90 and less or equal than 100
    return "O"
default:
    return "Z"
}

3

<0İfadesi (artık?) İş değil bu ile sona erdi böylece:

Swift 3.0:

switch someVar {
    case 0:
        // it's zero
    case 0 ..< .greatestFiniteMagnitude:
        // it's greater than zero
    default:
        // it's less than zero
    }

1
Swift 3.0'da, X_MAXalmıştır .greatestFiniteMagnitude, yani Double.greatestFiniteMagnitude, CGFloat.greatestFiniteMagnitudevb Yani genellikle, sadece yapabileceği case 0..< .greatestFiniteMagnitudetürüne beri someVarzaten bilinmektedir
Guig

@Dorian Roy Operatör var timeLeft = 100 switch timeLeft {case 0...<=7200: print("ok") default:print("nothing") }neden <=tanınmıyor? Eğer eşit olmadan yazarsam işe yarar. Teşekkürler
bibscy

@bibscy Kapalı aralık operatörünü kullanmak istiyorsunuz: case 0...7200:Operatör <=bir karşılaştırma operatörüdür. Bir anahtarda yalnızca menzil operatörlerini kullanabilirsiniz (bkz. Dokümanlar)
Dorian Roy

Bu harikaydı. 'Range <Double>' türündeki bu hata ifadesi desenini 'Int' türündeki değerlerle eşleştiremiyordum çünkü benim olduğum bir someVarşeydi Intve bazı yapmak zorunda Double(kaldımVar) `` çalışması için ...
Honey

2

Swift 4'ün bu sorunu çözdüğüne sevindim:

3'te bir çözüm olarak:

switch translation.x  {
case  0..<200:
    print(translation.x, slideLimit)
case  -200..<0:
    print(translation.x, slideLimit)
default:
    break
}

Çalışıyor ama ideal değil

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.