Mengapa 'zip' mengabaikan ekor koleksi yang menggantung?

12

C # , Scala, Haskell, Lisp dan Python memiliki zipperilaku yang sama : jika satu koleksi lebih panjang, ekor diabaikan secara diam-diam.

Ini bisa menjadi pengecualian yang dilemparkan juga, tetapi saya tidak mendengar adanya bahasa yang menggunakan pendekatan ini.

Ini membuatku bingung. Adakah yang tahu alasan mengapa zipdirancang seperti itu? Saya kira untuk bahasa baru, ini dilakukan karena bahasa lain melakukannya dengan cara ini. Tapi apa alasan dasarnya?

Saya mengajukan pertanyaan faktual, berbasis sejarah di sini, bukan apakah seseorang menyukainya, atau apakah itu pendekatan yang baik atau buruk.

Pembaruan : Jika saya ditanya apa yang harus dilakukan, saya akan mengatakan - melemparkan pengecualian, cukup mirip dengan pengindeksan array (meskipun bahasa "lama" melakukan semua jenis sihir, bagaimana menangani indeks di luar batas, UB, memperluas array, dll).

Greenoldman
sumber
10
Jika tidak mengabaikan ekor yang dimiliki functor, menggunakan urutan yang tak terbatas akan lebih rumit. Apalagi jika mendapatkan panjang rentang non-tak terbatas itu mahal / berbelit-belit / tidak mungkin.
Deduplicator
2
Anda tampaknya berpikir bahwa ini tidak terduga dan aneh. Saya merasa jelas dan memang tidak terhindarkan. Apa yang Anda inginkan terjadi ketika Anda membuka koleksi dengan panjang yang tidak sama?
Kilian Foth
@KilianFoth, dapatkan pengecualian.
greenoldman
@Dupuplikator, bagus. Dengan drop tail diam-diam Anda dapat secara alami mengekspresikan zipWithIndexmenyediakan bilangan alami. Sekarang, bagian hanya hilang info - apa itu alasannya? :-) (btw. tolong posting ulang komentar Anda sebagai jawaban, terima kasih).
greenoldman
1
Python memiliki itertools.izip_longest, yang secara autopad berhasil menyelesaikan input dengan Nones. Saya memilihnya lebih sering daripada zip ketika saya benar-benar menggunakan zip; saya tidak dapat mengingat alasan di balik pilihan apa pun lagi. Python sudah menghitung () untuk kasus @ greenoldman, yang sering saya gunakan.
StarWeaver

Jawaban:

11

Ini hampir selalu seperti yang Anda inginkan, dan ketika tidak, Anda dapat mengisi sendiri.

Masalah utamanya adalah dengan semantik malas yang tidak Anda ketahui panjangnya saat pertama kali memulai zip, jadi Anda tidak bisa hanya melempar pengecualian di awal. Pertama-tama Anda harus mengembalikan semua elemen umum, lalu melemparkan pengecualian, yang tidak akan sangat berguna.

Ini juga masalah gaya. Programmer imperatif terbiasa memeriksa kondisi batas secara manual di semua tempat. Pemrogram fungsional lebih suka konstruksi yang tidak dapat gagal dengan desain. Pengecualian sangat jarang. Jika ada cara bagi fungsi untuk mengembalikan default yang wajar, programmer fungsional akan menerimanya. Kompabilitas adalah raja.

Karl Bielefeldt
sumber
Saya bertanya tentang alasan historis, bukan apa yang bisa saya lakukan. Paragraf kedua - Anda salah, lihat bagaimana zippenerapannya saat ini. Melempar pengecualian hanya mengubah "berhenti menghasilkan" menjadi "melempar". Paragraf ketiga - mengembalikan elemen kosong untuk mencapai batas tidak boleh gagal, tetapi saya ragu FP dev akan memilih itu adalah desain yang bagus.
greenoldman
3
Paragraf kedua saya tidak berlaku untuk semua implementasi, hanya yang benar-benar malas. Jika Anda zipberdua urutan yang tak terbatas bersama-sama, Anda tidak tahu ukuran di awal. Pada paragraf ketiga, saya mengatakan default yang masuk akal . Mengembalikan kosong dalam kasus ini tidak masuk akal, sedangkan menjatuhkan ekor jelas.
Karl Bielefeldt
Ah, akhirnya saya mengerti maksud Anda - dengan melemparkan pengecualian dalam bahasa malas itu bukan pengganti teknis, itu benar-benar perubahan perilaku, karena Anda perlu melempar pengecualian tepat di awal, sementara Anda dapat mengabaikan ekor kapan saja itu nyaman.
greenoldman
3
+1 ini juga merupakan jawaban yang bagus, "Programer fungsional lebih suka konstruksi yang tidak dapat gagal dengan desain" ini dengan demikian menyatakan apa yang menjadi motivator terbesar di balik mayoritas keputusan desain yang dibuat oleh programmer fungsional. Programmer imperatif memiliki aturan yang mereka suka yang mengatakan "Katakan, jangan tanya", FP mengambil ini ke tingkat ke-N dengan berfokus pada memungkinkan terus menerus memberi tahu instruksi tanpa memerlukan hasil pemeriksaan sampai saat terakhir yang absolut, jadi kami mencoba untuk memastikan langkah-langkah menengah tidak bisa gagal, karena Composability adalah raja. Sangat baik dikatakan.
Jimmy Hoffa
12

Karena tidak ada cara yang jelas untuk melengkapi ekornya. Pilihan apa pun tentang bagaimana melakukannya akan menghasilkan buntut yang tidak jelas.

Caranya adalah dengan secara eksplisit memperpanjang daftar terpendek Anda agar sesuai dengan panjang terpanjang dengan nilai yang Anda harapkan.

Jika zip melakukan itu untuk Anda, Anda tidak bisa tahu nilai apa yang diisi secara intuitif. Apakah itu menggilir daftar? Apakah itu mengulang nilai yang dipty? Apa nilai mempty untuk tipe Anda?

Tidak ada implikasi dalam hal apa zip yang bisa digunakan seseorang untuk intuisi bagaimana ekor akan diperpanjang, jadi satu-satunya hal yang masuk akal untuk dilakukan adalah bekerja dengan nilai-nilai yang tersedia daripada membuat beberapa yang mungkin tidak diharapkan oleh konsumen Anda.


Juga ingat Anda mengacu pada fungsi terkenal yang sangat spesifik dengan semantik terkenal tertentu. Tetapi itu tidak berarti Anda tidak dapat membuat fungsi yang serupa tetapi sedikit berbeda . Hanya karena ada fungsi umum yang berfungsi x, tidak berarti Anda tidak dapat memutuskan untuk tujuan tertentu yang ingin Anda lakukan xdan y.

Meskipun ingat alasan ini dan banyak fungsi gaya FP umum lainnya adalah umum, adalah karena mereka sederhana dan digeneralisasi sehingga Anda dapat mengubah kode Anda untuk menggunakannya dan mendapatkan perilaku yang Anda inginkan. Misalnya, dalam C # Anda bisa saja

IEnumerable<Tuple<T, U>> ZipDefaults(IEnumerable<T> first, IEnumerable<U> second)
{
    return first.Count() < second.Count()
        ? first.Concat(Enumerable.Repeat(default(T), second.Count() - first.Count())).Zip(second)
        : first.Zip(second.Concat(Enumerable.Repeat(default(U), first.Count() - second.count())))
}

Atau hal-hal sederhana lainnya. Pendekatan FP membuat modifikasi sangat mudah karena Anda dapat menggunakan kembali potongan-potongan serta memiliki implementasi menjadi sangat kecil seperti di atas yang membuat versi modifikasi Anda sendiri dari hal-hal yang sangat sederhana.

Jimmy Hoffa
sumber
Ok, tapi itu hanya ketika Anda memaksa koleksi untuk melakukan sesuatu yang cocok dengan yang lain - bandingkan dengan pengindeksan koleksi (array). Anda dapat mulai berpikir haruskah saya memperluas dan menyusun jika saya memiliki indeks di luar batas? Atau mungkin diam-diam mengabaikan permintaan itu. Tetapi untuk beberapa waktu ada pendapat umum tentang melempar pengecualian. Sama di sini - jika Anda tidak memiliki koleksi yang cocok, berikan pengecualian. Mengapa pendekatan ini tidak dilakukan?
greenoldman
2
zipdapat mengisi nol, yang seringkali merupakan solusi intuitif. Pertimbangkan tipenya zip :: [a] -> [b] -> [(Maybe a, Maybe b)]. Memang, tipe hasilnya agak ^ H ^ H cukup tidak praktis, tetapi akan memungkinkan untuk dengan mudah menerapkan perilaku lain (jalan pintas, pengecualian) di atasnya.
amon
1
@amon: Itu sama sekali tidak intuitif, konyol. Itu hanya akan membutuhkan nol memeriksa setiap argumen.
DeadMG
4
@amon tidak setiap jenis memiliki null, itu yang saya maksud dengan mempty, objek memiliki null untuk mengisi ruang, tetapi Anda ingin itu harus datang dengan hal seperti itu untuk int dan tipe lainnya juga? Tentu, C # memiliki default(T)tetapi tidak semua bahasa melakukannya, dan bahkan untuk C # apakah itu perilaku yang sangat jelas ? Saya kira tidak
Jimmy Hoffa
1
@amon Mungkin akan lebih bermanfaat untuk mengembalikan bagian yang tidak digunakan dari daftar yang lebih panjang. Anda dapat menggunakannya untuk memeriksa apakah mereka memiliki panjang yang sama setelah-fakta-jika Anda perlu, dan masih dapat kembali zip atau melakukan sesuatu dengan ekor yang tidak dikonsumsi tanpa kembali menelusuri daftar.
Doval