Halo, Habr!
Nama saya Alexander Zimin, saya adalah pengembang iOS di Badoo. Ini adalah terjemahan dari artikel oleh kolega saya, Schwib, di mana dia menggambarkan seperti apa fungsi flatMap di Swift dan mengapa salah satu kelebihannya dinamai compactMap. Artikel ini bermanfaat baik untuk memahami proses yang terjadi di
repositori Swift dan
evolusinya , serta untuk pengembangan umum.

Dalam pemrograman fungsional, ada definisi yang jelas tentang apa fungsi
flatMap
. Metode
flatMap
mengambil daftar dan fungsi transformasi (yang untuk setiap transformasi mengharapkan untuk mendapatkan nilai nol atau lebih), menerapkannya ke setiap elemen daftar dan membuat daftar tunggal (diratakan). Perilaku ini berbeda dari fungsi
map
sederhana, yang menerapkan transformasi untuk setiap nilai dan berharap untuk mendapatkan hanya satu nilai untuk setiap transformasi.

Untuk beberapa versi, Swift memiliki
map
dan
flatMap
. Namun, di
Swift 4.1 Anda tidak bisa lagi menerapkan
flatMap
ke urutan nilai dan masih melewati penutupan yang mengembalikan nilai opsional. Sekarang ada metode
compactMap
untuk
compactMap
.
Pada awalnya mungkin tidak begitu mudah untuk memahami esensi dari inovasi. Jika
flatMap
bekerja dengan baik, mengapa memperkenalkan metode terpisah? Mari kita cari tahu.
Perpustakaan standar Swift sebelum versi 4.1 memberikan tiga implementasi kelebihan beban untuk
flatMap
:
1. Sequence.flatMap<S>(_: (Element) -> S) -> [S.Element], S : Sequence 2. Optional.flatMap<U>(_: (Wrapped) -> U?) -> U? 3. Sequence.flatMap<U>(_: (Element) -> U?) -> [U]
Mari kita pergi melalui ketiga opsi dan melihat apa yang mereka lakukan.
Sequence.flatMap <S> (_: (Elemen) -> S) -> [S.Element], di mana S: Sequence
Kelebihan pertama adalah untuk urutan di mana penutupan mengambil elemen dari urutan itu dan mengubah ke urutan lain.
flatMap
-
flatMap
semua urutan yang diubah ini ke dalam urutan akhir yang dikembalikan sebagai hasilnya. Sebagai contoh:
let array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] let flattened = array.flatMap { $0 }
Ini adalah contoh yang bagus tentang bagaimana metode
flatMap
seharusnya bekerja. Kami akan mengubah (memetakan) setiap elemen dari daftar sumber dan membuat urutan baru. Berkat
flatMap
hasil akhirnya adalah struktur rata dari sekuens yang ditransformasikan.
Opsional.flatMap <U> (_: (Dibungkus) -> U?) -> U?
Kelebihan kedua adalah untuk tipe opsional. Jika jenis opsional yang Anda panggil memiliki nilai, maka penutupan akan dipanggil dengan nilai tanpa pembungkus opsional (nilai yang tidak dibuka), dan Anda dapat mengembalikan nilai opsional yang dikonversi.
let a: Int? = 2 let transformedA = a.flatMap { $0 * 2 }
Sequence.flatMap <U> (_: (Elemen) -> U?) -> [U]
Kelebihan ketiga akan membantu Anda memahami apa
compactMap
. Versi ini terlihat sama dengan yang pertama, tetapi ada perbedaan penting. Dalam hal ini, penutupan mengembalikan opsional.
flatMap
memprosesnya, melewatkan nilai-nilai nil yang dikembalikan, dan memasukkan semua sisanya - dalam hasilnya sebagai nilai tanpa pembungkus.
let array = [1, 2, 3, 4, nil, 5, 6, nil, 7] let arrayWithoutNils = array.flatMap { $0 }
Namun dalam kasus ini, pemesanan tidak dilakukan. Oleh karena itu, versi
flatMap
lebih dekat dengan
map
daripada definisi fungsional
flatMap
. Dan masalah dengan kelebihan ini adalah bahwa Anda mungkin tidak menggunakannya dengan benar di mana
map
akan bekerja dengan sempurna.
let array = [1, 2, 3, 4, 5, 6] let transformed = array.flatMap { $0 }
Penggunaan
flatMap
berhubungan dengan kelebihan ketiga, secara implisit membungkus nilai yang dikonversi dalam opsional, dan kemudian menghapus pembungkus untuk menambah hasilnya. Situasi menjadi sangat menarik jika konversi string tidak digunakan dengan benar.
struct Person { let name: String } let people = [Person(name: βFooβ), Person(name: βBarβ)] let names = array.flatMap { $0.name }
Di Swift sebelum versi 4.0, kita akan mendapatkan konversi ke
[βFooβ, βBarβ]
. Tetapi dimulai dengan versi 4.0, nilai string mengimplementasikan protokol Collection. Oleh karena itu, penggunaan
flatMap
dalam kasus ini, alih-alih beban ketiga, akan sesuai dengan yang pertama, dan kami akan mendapatkan hasil "yang diratakan" dari nilai yang dikonversi:
[βFβ, βoβ, βoβ, βBβ, βaβ, βrβ]
Saat memanggil
flatMap
Anda tidak akan mendapatkan kesalahan, karena ini diperbolehkan digunakan. Tetapi logikanya akan rusak, karena hasilnya adalah tipe
Array<Character>.Type
, bukan
Array<String>.Type
diharapkan
Array<String>.Type
.
Kesimpulan
Untuk menghindari penyalahgunaan
flatMap
, versi kelebihan beban ketiga telah dihapus dari versi Swift baru. Dan untuk mengatasi masalah yang sama (menghapus nil-nilai) sekarang Anda perlu menggunakan metode terpisah -
compactMap
.