Bagaimana saya bisa memperpanjang Swift Array<T>
atau T[]
tipe dengan utilitas fungsional kustom?
Menjelajahi dokumen API Swift menunjukkan bahwa metode array adalah perpanjangan dari T[]
, misalnya:
extension T[] : ArrayType {
//...
init()
var count: Int { get }
var capacity: Int { get }
var isEmpty: Bool { get }
func copy() -> T[]
}
Saat menyalin dan menempel sumber yang sama dan mencoba variasi seperti:
extension T[] : ArrayType {
func foo(){}
}
extension T[] {
func foo(){}
}
Gagal membangun dengan kesalahan:
Jenis nominal
T[]
tidak dapat diperpanjang
Menggunakan definisi tipe lengkap gagal dengan Use of undefined type 'T'
, yaitu:
extension Array<T> {
func foo(){}
}
Dan itu juga gagal dengan Array<T : Any>
dan Array<String>
.
Curiously Swift memungkinkan saya memperluas array yang tidak diketik dengan:
extension Array {
func each(fn: (Any) -> ()) {
for i in self {
fn(i)
}
}
}
Yang memungkinkan saya menelepon dengan:
[1,2,3].each(println)
Tapi saya tidak bisa membuat ekstensi tipe generik yang tepat karena jenisnya sepertinya hilang ketika mengalir melalui metode, misalnya mencoba mengganti filter bawaan Swift dengan :
extension Array {
func find<T>(fn: (T) -> Bool) -> T[] {
var to = T[]()
for x in self {
let t = x as T
if fn(t) {
to += t
}
}
return to
}
}
Tetapi kompiler memperlakukannya sebagai tidak diketik di mana ia masih memungkinkan memanggil ekstensi dengan:
["A","B","C"].find { $0 > "A" }
Dan ketika melangkah-melalui debugger menunjukkan jenisnya Swift.String
tetapi itu adalah kesalahan build untuk mencoba mengaksesnya seperti sebuah String tanpa melemparkannya String
terlebih dahulu, yaitu:
["A","B","C"].find { ($0 as String).compare("A") > 0 }
Adakah yang tahu apa cara yang tepat untuk membuat metode ekstensi yang diketik yang bertindak seperti ekstensi bawaan?
extension T[]
bit yang sama ketika mengklik perintah pada jenis Array di XCode, tetapi tidak melihat cara untuk mengimplementasikannya tanpa mendapatkan kesalahan.<T>
dari metode tanda tangan.Jawaban:
Untuk memperluas array yang diketik dengan kelas , di bawah ini berfungsi untuk saya (Swift 2.2 ). Misalnya, mengurutkan array yang diketik:
Mencoba melakukan ini dengan struct atau typealias akan memberikan kesalahan:
Memperbarui :
Untuk memperluas array yang diketik dengan non-kelas gunakan pendekatan berikut:
Di Swift 3 beberapa jenis telah diganti namanya:
sumber
[Iterator.Element]
?Setelah beberapa saat mencoba hal yang berbeda, solusinya tampaknya menghapus
<T>
dari tanda tangan seperti:Yang sekarang berfungsi seperti yang dimaksudkan tanpa kesalahan build:
sumber
filter
fungsi yang ada :let x = ["A","B","C","X”].filter { $0.compare("A") > 0 }
filter
secara fungsional setara dengan Andafind
, yaitu hasil dari fungsinya sama. Jika penutupan filter Anda memiliki efek samping, Anda mungkin tidak suka hasilnya, pasti.filter
.filter
,map
danreduce
fungsi berasal, fungsi dieksekusi untuk nilai pengembaliannya. Sebaliknya,each
fungsi yang Anda tetapkan di atas adalah contoh dari fungsi yang dieksekusi untuk efek sampingnya, karena tidak menghasilkan apa-apa. Saya kira kita bisa sepakat bahwa implementasi Swift saat ini tidak ideal dan dokumentasi tidak menyatakan apa-apa tentang karakteristik runtime-nya.Perpanjang semua jenis:
Perpanjang beberapa jenis:
Perpanjang jenis tertentu :
sumber
Saya memiliki masalah yang sama - ingin memperpanjang Array umum dengan metode swap (), yang seharusnya mengambil argumen dengan tipe yang sama dengan array. Tetapi bagaimana Anda menentukan jenis generik? Saya menemukan dengan coba-coba bahwa di bawah ini berfungsi:
Kunci untuk itu adalah kata 'Elemen'. Perhatikan bahwa saya tidak mendefinisikan jenis ini di mana pun, sepertinya ada secara otomatis dalam konteks ekstensi array, dan merujuk pada apa pun jenis elemen array.
Saya tidak 100% yakin apa yang terjadi di sana, tapi saya pikir itu mungkin karena 'Elemen' adalah tipe terkait dari Array (lihat 'Jenis Terkait' di sini https://developer.apple.com/library/ios/documentation /Swift/Conceptual/Swift_Programming_Language/Generics.html#//apple_ref/doc/uid/TP40014097-CH26-ID189 )
Namun, saya tidak dapat melihat referensi ini dalam referensi struktur Array ( https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Array_Structure/index.html#//apple_ref/swift / struct / s: Sa ) ... jadi saya masih sedikit tidak yakin.
sumber
Array
adalah tipe generik:Array<Element>
(lihat swiftdoc.org/v2.1/type/Array ),Element
adalah pengganti untuk tipe yang terkandung. Misalnya:var myArray = [Foo]()
berarti yangmyArray
hanya akan berisi tipeFoo
.Foo
dalam hal ini "dipetakan" ke placeholder generikElement
. Jika Anda ingin mengubah perilaku umum Array (melalui ekstensi), Anda akan menggunakan placeholder generikElement
dan bukan jenis beton apa pun (seperti Foo).Menggunakan Swift 2.2 : Saya mengalami masalah serupa ketika mencoba menghapus duplikat dari berbagai string. Saya bisa menambahkan ekstensi pada kelas Array yang tidak hanya apa yang saya cari.
Menambahkan dua metode ini ke kelas Array memungkinkan saya untuk memanggil salah satu dari dua metode pada array dan berhasil menghapus duplikat. Perhatikan bahwa elemen dalam array harus sesuai dengan protokol Hashable. Sekarang saya bisa melakukan ini:
sumber
let deDuped = Set(dupes)
, yang bisa Anda kembalikan dengan metode non-destruktif yang disebuttoSet
asalkan Anda baik-baik saja dengan perubahan jenisJika Anda ingin mempelajari tentang memperluas Array dan jenis kode checkout kelas build in lainnya di repo github ini https://github.com/ankurp/Cent
Pada Xcode 6.1 sintaks untuk memperluas array adalah sebagai berikut
sumber
Saya telah melihat header perpustakaan standar Swift 2, dan di sini adalah prototipe untuk fungsi filter, yang membuatnya sangat jelas bagaimana cara menggulung sendiri.
Ini bukan ekstensi untuk Array, tetapi untuk CollectionType, jadi metode yang sama berlaku untuk jenis koleksi lainnya. @noescape berarti bahwa blok yang dilewati tidak akan meninggalkan ruang lingkup fungsi filter, yang memungkinkan beberapa optimisasi. Diri dengan modal S adalah kelas yang kita perluas. Self.Generator adalah iterator yang beriterasi melalui objek dalam koleksi dan Self.Generator.Element adalah jenis objek, misalnya untuk array [Int?] Self.Generator.Element akan menjadi Int ?.
Semua dalam semua metode filter ini dapat diterapkan ke sembarang CollectionType, perlu blok filter yang mengambil elemen koleksi dan mengembalikan Bool, dan mengembalikan array dari tipe aslinya. Jadi, menyatukan ini, inilah metode yang menurut saya berguna: Menggabungkan peta dan filter, dengan mengambil blok yang memetakan elemen koleksi ke nilai opsional, dan mengembalikan array nilai-nilai opsional yang tidak nol.
sumber
sumber
( Swift 2.x )
Anda juga dapat memperluas array agar sesuai dengan protokol yang mengandung blue-rpints untuk metode tipe generik, misalnya, protokol yang berisi utilitas fungsional kustom Anda untuk semua elemen array generik yang sesuai dengan beberapa batasan tipe, misalnya protokol
MyTypes
. Bonus menggunakan pendekatan ini adalah bahwa Anda dapat menulis fungsi yang mengambil argumen array umum, dengan batasan bahwa argumen array ini harus sesuai dengan protokol utilitas fungsi kustom Anda, misalnya protokolMyFunctionalUtils
.Anda bisa mendapatkan perilaku ini baik secara implisit, dengan mengetik membatasi elemen array
MyTypes
, atau --- seperti yang akan saya tunjukkan dalam metode yang saya jelaskan di bawah ---, cukup rapi, secara eksplisit, membiarkan tajuk fungsi array generik Anda secara langsung menunjukkan bahwa input array sesuai denganMyFunctionalUtils
.Kita mulai dengan Protokol
MyTypes
untuk digunakan sebagai batasan tipe; memperpanjang jenis yang ingin Anda muat dalam obat generik Anda dengan protokol ini (contoh di bawah ini memperluas jenis dasarInt
danDouble
juga jenis khususMyCustomType
)Protokol
MyFunctionalUtils
(memegang cetak biru utilitas fungsi array generik tambahan kami) dan setelah itu, perpanjangan Array olehMyFunctionalUtils
; implementasi metode cetak biru:Akhirnya, tes dan dua contoh menunjukkan fungsi mengambil array generik, dengan kasus-kasus berikut, masing-masing
Menampilkan pernyataan implisit bahwa parameter array sesuai dengan protokol 'MyFunctionalUtils', melalui tipe yang membatasi elemen array ke 'MyTypes' (fungsi
bar1
).Menunjukkan secara eksplisit bahwa parameter array sesuai dengan protokol 'MyFunctionalUtils' (fungsi
bar2
).Tes dan contoh berikut:
sumber
sumber
$0 as! Double
) berjuang melawan sistem tipe Swift dan juga mengalahkan tujuan pertanyaan OP, menurut saya. Dengan melakukan itu Anda kehilangan potensi untuk optimisasi kompiler untuk perhitungan yang sebenarnya ingin Anda lakukan, dan Anda juga mencemari namespace Array dengan fungsi yang tidak berarti (mengapa Anda ingin melihat .calculateMedian () dalam array UIViews, atau apa pun kecuali Double dalam hal ini?). Ada cara yang lebih baik.extension CollectionType where Generator.Element == Double {}