Jika saya memiliki array di Swift, dan mencoba mengakses indeks yang di luar batas, ada kesalahan runtime yang tidak mengejutkan:
var str = ["Apple", "Banana", "Coconut"]
str[0] // "Apple"
str[3] // EXC_BAD_INSTRUCTION
Namun, saya akan berpikir dengan semua perangkaian opsional dan keamanan yang dibawa Swift, akan sepele untuk melakukan sesuatu seperti:
let theIndex = 3
if let nonexistent = str[theIndex] { // Bounds check + Lookup
print(nonexistent)
...do other things with nonexistent...
}
Dari pada:
let theIndex = 3
if (theIndex < str.count) { // Bounds check
let nonexistent = str[theIndex] // Lookup
print(nonexistent)
...do other things with nonexistent...
}
Tapi ini bukan masalahnya - saya harus menggunakan pernyataan ol ' if
untuk memeriksa dan memastikan indeks kurang dari str.count
.
Saya mencoba menambahkan subscript()
implementasi saya sendiri , tetapi saya tidak yakin bagaimana meneruskan panggilan ke implementasi asli, atau mengakses item (berbasis indeks) tanpa menggunakan notasi subskrip:
extension Array {
subscript(var index: Int) -> AnyObject? {
if index >= self.count {
NSLog("Womp!")
return nil
}
return ... // What?
}
}
Jawaban:
Jawaban Alex memiliki saran dan solusi yang bagus untuk pertanyaan itu, namun, saya kebetulan menemukan cara yang lebih baik dalam mengimplementasikan fungsi ini:
Swift 3.2 dan yang lebih baru
Swift 3.0 dan 3.1
Penghargaan untuk Hamish karena menghasilkan solusi untuk Swift 3 .
Cepat 2
Contoh
sumber
safe:
nama parameter yang disertakan untuk memastikan perbedaannya.return self.indices ~= index ? self[index] : nil;
Collection
jenis di manaIndices
ada tidak berdekatan. Misalnya untukSet
contoh, jika kita mengakses elemen set dengan indeks (SetIndex<Element>
), kita dapat mengalami pengecualian runtime untuk indeks yang ada>= startIndex
dan< endIndex
, dalam hal ini subskrip aman gagal (lihat misalnya contoh yang dibuat ini ).contains
Metode akan iterate melalui semua indeks sehingga membuat O ini (n). Cara yang lebih baik adalah dengan menggunakan indeks dan menghitung untuk memeriksa batas.return index >= startIndex && index < endIndex ? self[index] : nil
Collection
tipe milikistartIndex
,endIndex
yang manaComparable
. Tentu saja, ini tidak akan berfungsi untuk beberapa koleksi aneh yang, misalnya, tidak memiliki indeks di tengah, solusi denganindices
lebih umum.Jika Anda benar-benar menginginkan perilaku ini, baunya seperti Anda menginginkan Kamus alih-alih Array. Kamus kembali
nil
ketika mengakses kunci yang hilang, yang masuk akal karena jauh lebih sulit untuk mengetahui apakah kunci hadir dalam kamus karena kunci itu bisa apa saja, di mana dalam array kunci harus dalam kisaran:0
untukcount
. Dan itu sangat umum untuk beralih pada rentang ini, di mana Anda dapat benar - benar yakin memiliki nilai nyata pada setiap iterasi dari sebuah loop.Saya pikir alasannya tidak bekerja seperti ini adalah pilihan desain yang dibuat oleh pengembang Swift. Ambil contoh Anda:
Jika Anda sudah tahu indeks ada, seperti yang Anda lakukan dalam kebanyakan kasus di mana Anda menggunakan array, kode ini hebat. Namun, jika mengakses subscript mungkin bisa kembali
nil
maka Anda telah mengubah jenis kembali dariArray
'ssubscript
metode untuk menjadi opsional. Ini mengubah kode Anda menjadi:Yang berarti Anda harus membuka bungkusan opsional setiap kali Anda mengulangi melalui array, atau melakukan hal lain dengan indeks yang diketahui, hanya karena jarang Anda dapat mengakses indeks di luar batas. Perancang Swift memilih untuk lebih sedikit membuka bungkus opsional, dengan mengorbankan pengecualian runtime saat mengakses indeks di luar batas. Dan kerusakan lebih baik daripada kesalahan logika yang disebabkan oleh
nil
Anda tidak mengharapkan data Anda di suatu tempat.Dan saya setuju dengan mereka. Jadi Anda tidak akan mengubah
Array
implementasi default karena Anda akan merusak semua kode yang mengharapkan nilai-nilai non-opsional dari array.Sebagai gantinya, Anda bisa mensubklasifikasikan
Array
, dan menggantisubscript
untuk mengembalikan opsional. Atau, lebih praktisnya, Anda bisa memperluasArray
dengan metode non-subskrip yang melakukan ini.Pembaruan Swift 3
func get(index: Int) ->
T?
perlu diganti olehfunc get(index: Int) ->
Element?
sumber
subscript()
menjadi opsional - ini adalah hambatan utama yang dihadapi dalam mengesampingkan perilaku default. (Saya benar-benar tidak bisa membuatnya bekerja sama sekali. ) Saya menghindari menulisget()
metode ekstensi, yang merupakan pilihan yang jelas dalam skenario lain (Obj-C kategori, siapa pun?) Tetapiget(
tidak jauh lebih besar dari[
, dan membuatnya jelas bahwa perilakunya mungkin berbeda dari apa yang pengembang lain harapkan dari operator subskrip Swift. Terima kasih!T
telah diubah namanya menjadiElement
. Hanya pengingat ramah :)nil
bukannya menyebabkan pengecualian dari indeks di luar batas akan menjadi ambigu. Karena egArray<String?>
dapat juga mengembalikan nil sebagai anggota koleksi yang valid, Anda tidak akan dapat membedakan antara kedua kasus tersebut. Jika Anda memiliki jenis koleksi Anda sendiri yang Anda tahu tidak pernah dapat mengembalikannil
nilai, alias itu kontekstual ke aplikasi, maka Anda dapat memperpanjang Swift untuk memeriksa batas aman seperti dijawab dalam posting ini.Untuk membangun berdasarkan jawaban Nikita Kukushkin, kadang-kadang Anda perlu menetapkan dengan aman ke indeks array serta membaca dari mereka, yaitu
Jadi di sini adalah pembaruan untuk jawaban Nikita (Swift 3.2) yang juga memungkinkan penulisan dengan aman ke indeks array yang dapat diubah, dengan menambahkan safe: nama parameter.
sumber
Berlaku dalam Swift 2
Meskipun ini sudah dijawab berkali-kali, saya ingin memberikan jawaban yang lebih sesuai di mana cara pemrograman Swift, yang dalam kata-kata Crusty adalah: "Pikirkan
protocol
dulu"• Apa yang ingin kita lakukan?
- Dapatkan Elemen dari
Array
Indeks yang diberikan hanya ketika aman, dannil
jika tidak• Apa yang menjadi dasar penerapan fungsi ini?
-
Array
subscript
ing• Dari mana ia mendapatkan fitur ini?
- Definisi
struct Array
dalamSwift
modul memilikinya• Tidak ada yang lebih generik / abstrak?
- Mengadopsi
protocol CollectionType
yang memastikan juga• Tidak ada yang lebih generik / abstrak?
- Ini juga mengadopsi
protocol Indexable
...• Ya, sepertinya yang terbaik yang bisa kita lakukan. Bisakah kita mengembangkannya untuk memiliki fitur yang kita inginkan?
- Tapi kami memiliki tipe yang sangat terbatas (tidak
Int
) dan properti (tidakcount
) untuk bekerja dengan sekarang!• Itu sudah cukup. Stdlib Swift dilakukan dengan cukup baik;)
¹: tidak benar, tetapi memberikan ide
sumber
Collection
mungkin :)Berikut ini beberapa tes yang saya lakukan untuk Anda:
sumber
sumber
SafeIndex
kelas dan bukan struct?Cepat 4
Ekstensi untuk mereka yang lebih memilih sintaksis yang lebih tradisional:
sumber
Saya menemukan array aman dapatkan, atur, masukkan, hapus sangat berguna. Saya lebih suka masuk dan mengabaikan kesalahan karena semua yang lain segera menjadi sulit untuk dikelola. Kode lengkap di bawah
Tes
sumber
Menggunakan Above disebutkan ekstensi kembali nil jika indeks kapan saja keluar dari batas.
hasil - nihil
sumber
Saya menyadari ini adalah pertanyaan lama. Saya menggunakan Swift5.1 pada titik ini, OP untuk Swift 1 atau 2?
Saya membutuhkan sesuatu seperti ini hari ini, tetapi saya tidak ingin menambahkan ekstensi skala penuh hanya untuk satu tempat dan ingin sesuatu yang lebih fungsional (lebih aman?). Saya juga tidak perlu melindungi terhadap indeks negatif, hanya indeks yang mungkin melewati akhir array:
Bagi mereka yang berdebat tentang Urutan dengan nol, apa yang Anda lakukan tentang
first
danlast
properti yang mengembalikan nol untuk koleksi kosong?Saya suka ini karena saya bisa mengambil barang yang ada dan menggunakannya untuk mendapatkan hasil yang saya inginkan. Saya juga tahu bahwa dropFirst (n) bukan salinan seluruh koleksi, hanya sepotong. Dan kemudian perilaku pertama yang sudah ada mengambil alih untuk saya.
sumber
Saya pikir ini bukan ide yang baik. Tampaknya lebih baik untuk membangun kode padat yang tidak menghasilkan mencoba menerapkan indeks out-of-bounds.
Harap pertimbangkan bahwa kesalahan seperti itu gagal diam-diam (seperti yang disarankan oleh kode Anda di atas) dengan kembali
nil
cenderung menghasilkan kesalahan yang lebih rumit dan lebih sulit diselesaikan.Anda bisa melakukan override dengan cara yang sama seperti yang Anda gunakan dan hanya menulis subskrip dengan cara Anda sendiri. Hanya kekurangannya adalah kode yang ada tidak akan kompatibel. Saya pikir untuk menemukan hook untuk mengganti generik x [i] (juga tanpa preprocessor teks seperti pada C) akan sulit.
Yang paling dekat yang bisa saya pikirkan adalah
EDIT : Ini benar-benar berfungsi. Satu-baris !!
sumber
if let
dalam hal ini tidak membuat program lebih kompleks, atau kesalahan lebih sulit dipecahkan. Ini hanya mengembunif
cek batas dua pernyataan tradisional dan pencarian aktual menjadi pernyataan tunggal, kental. Ada kasus-kasus (terutama yang bekerja di UI) di mana indeks normal berada di luar batas, seperti memintaNSTableView
untukselectedRow
tanpa pilihan.countElements
atau seperti yang OP lakukancount
, hanya saja tidak dengan cara bahasa mendefinisikan penulisan larik subscript.Saya telah mengisi array dengan
nil
s dalam kasus penggunaan saya:Juga periksa ekstensi subskrip dengan
safe:
label oleh Erica Sadun / Mike Ash: http://ericasadun.com/2015/06/01/swift-safe-array-indexing-my-favorite-thing-of-the-new-week/sumber
"Common Rejected Changes" untuk Swift list berisi penyebutan tentang mengubah akses subscript Array untuk mengembalikan opsional daripada mogok:
Jadi akses subskrip dasar tidak akan berubah untuk mengembalikan opsional.
Namun, tim / komunitas Swift tampaknya terbuka untuk menambahkan pola akses opsional-kembali baru ke Array, baik melalui fungsi atau subskrip.
Ini telah diusulkan dan dibahas di forum Swift Evolution di sini:
https://forums.swift.org/t/add-accessor-with-bounds-check-to-array/16871
Khususnya, Chris Lattner memberi ide "+1":
Jadi ini dimungkinkan di luar kotak di beberapa versi Swift yang akan datang. Saya akan mendorong siapa saja yang ingin berkontribusi pada utas Swift Evolution itu.
sumber
Untuk menyebarkan mengapa operasi gagal, kesalahan lebih baik daripada opsional. Subskrip tidak dapat membuang kesalahan, jadi itu harus menjadi metode.
sumber
Tidak yakin mengapa tidak ada seorang pun, telah memasang ekstensi yang juga memiliki setter untuk menumbuhkan array secara otomatis
Penggunaannya mudah dan bekerja mulai dari Swift 5.1
Catatan: Anda harus memahami biaya kinerja (baik dalam waktu / ruang) ketika menumbuhkan array di swift - tetapi untuk masalah kecil kadang-kadang Anda hanya perlu membuat Swift untuk berhenti Swifting sendiri di kaki
sumber
Saya telah membuat ekstensi sederhana untuk array
ini bekerja dengan sempurna seperti yang dirancang
Contoh
sumber
Swift 5 Penggunaan
berakhir dengan tetapi benar - benar ingin melakukan umumnya suka
tapi karena koleksinya kontekstual, kurasa itu pantas.
sumber
Ketika Anda hanya perlu mendapatkan nilai dari array dan Anda tidak keberatan dengan penalti kinerja kecil (yaitu jika koleksi Anda tidak besar), ada alternatif berbasis Kamus yang tidak melibatkan (terlalu umum, untuk rasa) ekstensi koleksi:
sumber