Kurang dari atau lebih besar dari pada pernyataan sakelar Swift

145

Saya kenal dengan switchpernyataan di Swift, tetapi bertanya-tanya bagaimana cara mengganti bagian kode ini dengan switch:

if someVar < 0 {
    // do something
} else if someVar == 0 {
    // do something else
} else if someVar > 0 {
    // etc
}
Pieter
sumber
Meskipun ini adalah pertanyaan yang menarik, saya pikir kode menggunakan saklar jauh lebih mudah dibaca daripada pernyataan if. Hanya karena Anda bisa, bukan berarti Anda harus melakukannya.
Rog

Jawaban:

241

Inilah satu pendekatan. Dengan asumsi someVaradalah Intatau yang lain Comparable, Anda dapat secara opsional menetapkan operan ke variabel baru. Ini memungkinkan Anda menentukan ruang lingkupnya namun Anda ingin menggunakan wherekata kunci:

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")
}

Ini dapat disederhanakan sedikit:

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")
}

Anda juga dapat menghindari wherekata kunci sepenuhnya dengan pencocokan rentang:

switch someVar {
case Int.min..<0:
    print("someVar is \(someVar)")
case 0:
    print("someVar is 0")
default:
    print("someVar is \(someVar)")
}
Aaron Brager
sumber
9
Saya merekomendasikan default: fatalError()untuk mendeteksi kemungkinan kesalahan logika lebih awal.
Martin R
1
Terima kasih! Contoh-contoh ini sangat membantu dan mereka memecahkan masalah saya! (contoh lain juga bagus, tetapi milik Anda sangat membantu saya)
Pieter
1
@MartinR assertionFailuretampaknya menjadi pilihan yang lebih aman, terutama saat bekerja dalam tim.
Michael Voline
119

Dengan Swift 5, Anda dapat memilih salah satu sakelar berikut untuk mengganti pernyataan if Anda.


# 1 Menggunakan sakelar dengan PartialRangeFromdanPartialRangeUpTo

let value = 1

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

# 2 Menggunakan sakelar dengan ClosedRangedanRange

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 Menggunakan sakelar dengan klausa mana

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 Menggunakan switch dengan tempat klausa dan penugasan _

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 Menggunakan sakelar dengan operator RangeExpressionprotokol~=(_:_:)

let value = 1

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

# 6 Menggunakan sakelar dengan operator Equatableprotokol~=(_:_:)

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 Menggunakan switch dengan PartialRangeFrom, PartialRangeUpTodan RangeExpression's contains(_:)metode

let value = 1

switch true {
case (1...).contains(value):
    print("greater than zero")
case (..<0).contains(value):
    print("less than zero")
default:
    print("zero")
}
Imanou Petit
sumber
1
mengapa case default dibutuhkan di # 2? Tampaknya flakey bahwa jika rannge dari Int.min ke Int.max apa yang tersisa?
μολὼν.λαβέ
Wow, daftar opsi yang bagus. Senang mengetahui ada beberapa cara untuk melakukan ini.
Christopher Pickslay
2
Ikhtisar bagus tetapi cacat karena angka antara 0 dan 1 tidak terhitung. 0.1melempar kesalahan fatal karena 1...hanya mencakup angka dari 1. Jadi solusi ini hanya berfungsi jika valuemerupakan Inttetapi itu berbahaya karena jika tipe variabel berubah, fungsionalitas terputus tanpa kesalahan kompiler.
Manuel
1
Solusi Anda tidak berfungsi dengan benar untuk tipe Double. case 1 ...: print ("lebih besar dari nol") TIDAK lebih besar dari 0, lebih besar atau sama dengan 1.
Vlad
20

The switchpernyataan, di bawah tenda, menggunakan ~=operator. Jadi ini:

let x = 2

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

Desugars untuk ini:

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

Jika Anda melihat referensi pustaka standar, ini dapat memberi tahu Anda dengan tepat apa yang ~=dilakukan kelebihan beban : disertakan adalah pencocokan rentang, dan menyamakan untuk hal-hal yang setara. (Tidak termasuk adalah pencocokan enum, yang merupakan fitur bahasa, bukan fungsi di std lib)

Anda akan melihat bahwa itu tidak cocok dengan boolean lurus di sisi kiri. Untuk perbandingan semacam itu, Anda perlu menambahkan pernyataan where.

Kecuali ... Anda membebani ~=operator sendiri. (Ini umumnya tidak disarankan) Satu kemungkinan akan menjadi seperti ini:

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

Sehingga cocok dengan fungsi yang mengembalikan boolean di sebelah kiri ke parameternya di sebelah kanan. Inilah jenis hal yang dapat Anda gunakan untuk:

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

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

Untuk kasus Anda, Anda mungkin memiliki pernyataan yang terlihat seperti ini:

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

Tetapi sekarang Anda harus mendefinisikan baru isNegativedanisPositive fungsi . Kecuali jika Anda membebani beberapa operator lagi ...

Anda dapat membebani operator infiks normal menjadi operator awalan atau postfix. Ini sebuah contoh:

postfix operator < {}

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

Ini akan bekerja seperti ini:

let isGreaterThanFive = 5<

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

Gabungkan itu dengan fungsi sebelumnya, dan pernyataan sakelar Anda dapat terlihat seperti ini:

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

Sekarang, Anda mungkin tidak boleh menggunakan hal semacam ini dalam praktik: itu agak cerdik. Anda (mungkin) lebih baik bertahan dengan wherepernyataan itu. Yang mengatakan, pola pernyataan beralih

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

atau

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

Tampaknya cukup umum untuk itu layak dipertimbangkan.

oisdk
sumber
1
di mana jawaban Anda untuk pertanyaan itu? Saya tidak dapat menemukannya.
Sayang
1
case 3 .. <5: print (3 .. <5) - Secara harfiah dalam paragraf pertama. Jawaban ini diremehkan. Menghemat banyak kode untuk saya.
Karim
14

Kamu bisa:

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

Karena seseorang telah memposting di case let x where x < 0:sini adalah alternatif untuk di mana someVaradalah Int.

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

Dan di sini ada alternatif untuk di mana someVaradalah Double:

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

Beginilah tampilannya dengan rentang

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"
}
GOrozco58
sumber
3

The <0ekspresi tidak bekerja (lagi?) Jadi aku akhirnya dengan ini:

Swift 3.0:

switch someVar {
    case 0:
        // it's zero
    case 0 ..< .greatestFiniteMagnitude:
        // it's greater than zero
    default:
        // it's less than zero
    }
Dorian Roy
sumber
1
Di swift 3.0, X_MAXtelah digantikan oleh .greatestFiniteMagnitude, mis . Double.greatestFiniteMagnitude, CGFloat.greatestFiniteMagnitudeDll. Jadi biasanya, Anda bisa melakukannya case 0..< .greatestFiniteMagnitudekarena jenisnya someVarsudah diketahui
Guig
@Dorian Roy var timeLeft = 100 switch timeLeft {case 0...<=7200: print("ok") default:print("nothing") }Mengapa <=operator tidak dikenali? Jika saya menulisnya tanpa sama itu berfungsi. Terima kasih
bibscy
@bibscy Anda ingin menggunakan operator rentang tertutup: case 0...7200:Operator <=adalah operator perbandingan. Dalam sakelar, Anda hanya dapat menggunakan operator jangkauan (lihat dokumen)
Dorian Roy
Ini bagus. Saya mendapatkan pola ekspresi kesalahan ini dari tipe 'Range <Double>' tidak dapat mencocokkan nilai-nilai dari tipe 'Int' karena saya someVaradalah Intdan saya harus melakukan Double(beberapaVar) `untuk membuatnya bekerja ...
Honey
2

Senang bahwa Swift 4 mengatasi masalah:

Sebagai solusi di 3 saya lakukan:

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

Bekerja tetapi tidak ideal

Jeremy Andrews
sumber