Bisakah saya menggunakan operator jangkauan dengan pernyataan if di Swift?

196

Apakah mungkin untuk menggunakan operator jangkauan ...dan ..<dengan pernyataan if. Mungkin sesuatu seperti ini:

let statusCode = 204
if statusCode in 200 ..< 299 {
  NSLog("Success")
}
Jimmy
sumber

Jawaban:

425

Anda dapat menggunakan operator "pencocokan pola" ~=:

if 200 ... 299 ~= statusCode {
    print("success")
}

Atau pernyataan sakelar dengan pola ekspresi (yang menggunakan operator pencocokan pola secara internal):

switch statusCode {
case 200 ... 299:
    print("success")
default:
    print("failure")
}

Catatan yang ..<menunjukkan rentang yang menghilangkan nilai atas, jadi Anda mungkin ingin 200 ... 299atau 200 ..< 300.

Informasi tambahan: Ketika kode di atas dikompilasi dalam Xcode 6.3 dengan optimisasi diaktifkan, maka untuk pengujian

if 200 ... 299 ~= statusCode

sebenarnya tidak ada panggilan fungsi yang dihasilkan sama sekali, hanya tiga instruksi perakitan:

addq    $-200, %rdi
cmpq    $99, %rdi
ja  LBB0_1

ini persis kode perakitan yang sama dengan yang dihasilkan

if statusCode >= 200 && statusCode <= 299

Anda dapat memverifikasi itu dengan

xcrun -sdk macosx swiftc -O -emit-assembly main.swift

Pada Swift 2, ini dapat ditulis sebagai

if case 200 ... 299 = statusCode {
    print("success")
}

menggunakan pencocokan pola yang baru diperkenalkan untuk pernyataan if. Lihat juga Swift 2 - Pencocokan pola dalam "jika" .

Martin R
sumber
1
Keren, apakah ini O (1)? Juga, alangkah baiknya jika Swift memiliki tangan pendek untuk pergantian pernyataan, seperti Scala misalnya. Tetapi mengingat bahwa Anda selalu dipaksa untuk menangani semua kemungkinan pada waktu kompilasi di Swift, itu mungkin tidak benar-benar layak.
Langit
2
@ Sky: Dari kode assembly yang dihasilkan, seseorang dapat melihat bahwa fungsi perpustakaan func ~= (Range<A>, A) -> Booldipanggil. Saya akan berasumsi bahwa fungsi ini berfungsi dengan O (1).
Martin R
4
@Downvoter: Beberapa komentar yang menjelaskan akan menyenangkan, sehingga saya dapat meningkatkan atau memperbaiki jawabannya ...
Martin R
1
@ Martin bagaimana Anda bisa mengetahui fungsi mana yang disebut oleh bahasa assembly. Hopper? +1 untuk jawaban keren
codester
3
@codester: Saya mengkompilasi kode pada baris perintah dengan xcrun -sdk macosx swift -emit-assembly main.swiftdan memeriksa kode assembly. Kemudian saya biasa xcrun swift-demangle ...memotong-motong nama fungsi yang dipanggil. - Sayangnya, Xcode belum dapat membuat kode rakitan untuk file Swift, mungkin ini akan bekerja di versi yang lebih baru.
Martin R
95

Versi ini tampaknya lebih mudah dibaca daripada pencocokan pola:

if (200 ... 299).contains(statusCode) {
    print("Success")
}
Serhii Yakovenko
sumber
2
Persis apa yang saya cari
Nazim Kerimbekov
Saya mendapatkan kesalahan ini => Tidak dapat membentuk Rentang dengan upperBound <lowerBound
Alfi
9

Ini adalah utas lama, tetapi bagi saya sepertinya kita terlalu memikirkan ini. Menurut saya jawaban yang terbaik adalah adil

if statusCode >= 200 && statusCode <= 299

Tidak ada

if 200 > statusCode > 299

bentuk yang saya sadari, dan solusi lain yang disarankan adalah melakukan pemanggilan fungsi, yang lebih sulit dibaca, dan mungkin lebih lambat untuk dijalankan. Metode pencocokan pola adalah trik yang berguna untuk diketahui, tetapi tampaknya tidak cocok untuk masalah ini.

Edit:

Secara pribadi, saya menemukan operator pencocokan pola menjadi mengerikan, dan berharap kompiler akan mendukung if x in 1...100sintaks. Itu sooooo jauh lebih intuitif dan mudah dibaca daripadaif 1...100 ~= x

Duncan C
sumber
1
Anda benar bahwa versi ini lebih baik dibaca, saya hanya mencoba menjawab pertanyaan eksplisit "Apakah mungkin menggunakan operator jangkauan ...?" - Tapi Xcode 6.3 beta (dalam mode yang dioptimalkan) menghasilkan tepat tiga instruksi perakitan untuk if 200 ... 299 ~= statusCode, tidak ada panggilan fungsi :)
Martin R
13
Sebenarnya if 200 ... 299 ~= statusCodememberikan kode perakitan yang sama denganif statusCode >= 200 && statusCode <= 299
Martin R
6
Kecuali jika persyaratan ini berada di bagian kritis yang dikunjungi ribuan kali per detik, mengkhawatirkan overhead panggilan fungsi adalah pengoptimalan prematur. Bahkan kemudian, saya akan lebih khawatir tentang apa panggilan fungsi melakukan daripada biaya menyebutnya. Kerja bagus @MartinR untuk membuktikan tidak ada biaya apa pun.
Rickick
1
@rickster, cukup benar. Saya masih cenderung lebih suka konstruksi yang efisien daripada yang tidak efisien karena kebiasaan (dengan asumsi keterbacaan sama). Tidak sampai-sampai saya membuang terlalu banyak waktu SAYA untuk itu, tetapi masih membayar untuk mengetahui berapa biaya dari pendekatan yang berbeda.
Duncan C
1
Ini adalah nitpicking, tetapi saya tidak setuju dengan saran Anda bahwa pernyataan if Anda lebih mudah dibaca atau dimengerti daripada jawaban yang diposting oleh @SerhiiYakovenko. Cukup berdasarkan KERING - Anda nama "statusCode" dua kali. Dalam sesi debugging bermata larut malam setelah saya memutuskan bahwa variabel yang berbeda bernama "statusValue" harus digunakan di sini daripada "statusCode", saya mungkin saja membuat kesalahan dengan mengubah salah satu nama variabel dan bukan yang lain .
RenniePet
3

Saya ingin memeriksa kesalahan 4xx kecuali 401. Berikut adalah kodenya:

let i = 401

if 400..<500 ~= i, i != 401 {
    print("yes")
} else {
    print("NO")
}
abhimuralidharan
sumber
2

Saya lebih suka operator Range .contains (), sampai menemukan bahwa implementasinya tidak efisien - https://oleb.net/blog/2015/09/swift-ranges-and-intervals/

Kita dapat mewakili kondisi x <0 menggunakan rentang: (Int.min .. <0) .contains (x) persis sama. Ini jauh lebih lambat. Implementasi default berisi (_ :) melintasi seluruh koleksi, dan menjalankan loop sembilan quintillion kali dalam kasus terburuk tidak murah.

Entro
sumber