Namun, mana yang lebih clojure idiomatik? Apakah itu membuat banyak perbedaan di satu sisi atau yang lain? Dari pengujian kinerja saya (terbatas), sepertinya reducesedikit lebih cepat.
reducedan applytentu saja hanya setara (dalam hal hasil akhir dikembalikan) untuk fungsi asosiatif yang perlu melihat semua argumen mereka dalam kasus variabel-arity. Ketika mereka setara hasil-bijaksana, saya akan mengatakan itu applyselalu sangat idiomatis, sementara reduceitu setara - dan mungkin memangkas sebagian dari sekejap mata - dalam banyak kasus umum. Berikut ini adalah alasan saya untuk percaya ini.
+itu sendiri diimplementasikan dalam hal reducekasus variabel-arity (lebih dari 2 argumen). Memang, ini sepertinya cara "default" yang sangat masuk akal untuk dilakukan untuk variabel-arity, fungsi asosiatif: reducememiliki potensi untuk melakukan beberapa optimisasi untuk mempercepat segalanya - mungkin melalui sesuatu seperti internal-reduce, 1,2 kebaruan yang baru saja dinonaktifkan di master, tetapi mudah-mudahan akan diperkenalkan kembali di masa depan - yang konyol untuk mereplikasi di setiap fungsi yang mungkin mendapat manfaat dari mereka dalam kasus vararg. Dalam kasus umum seperti itu, applyhanya akan menambah sedikit overhead. (Perhatikan bahwa tidak ada yang perlu dikhawatirkan.)
Di sisi lain, fungsi yang kompleks mungkin mengambil keuntungan dari beberapa peluang optimisasi yang tidak cukup umum untuk dibangun reduce; maka applyakan membiarkan Anda mengambil keuntungan dari mereka sementara reducemungkin sebenarnya memperlambat Anda. Contoh yang baik dari skenario terakhir yang terjadi dalam praktik disediakan oleh str: ia menggunakan StringBuilderinternal dan akan mendapat manfaat secara signifikan dari penggunaan applydaripada reduce.
Jadi, saya akan mengatakan gunakan applysaat ragu; dan jika Anda tahu bahwa itu tidak membuat Anda kesal reduce(dan bahwa ini tidak mungkin berubah segera), jangan ragu reduceuntuk mencukur habisnya biaya tambahan yang tidak perlu jika Anda menginginkannya.
Jawaban yang bagus Di samping catatan, mengapa tidak menyertakan sumfungsi bawaan seperti di haskell? Sepertinya operasi yang sangat umum.
dbyrne
17
Terima kasih, senang mendengarnya! Re: sumSaya akan mengatakan bahwa Clojure memiliki fungsi ini, namanya +dan Anda dapat menggunakannya apply. :-) Secara serius, saya pikir dalam Lisp, secara umum, jika fungsi variadik disediakan, biasanya tidak disertai oleh pembungkus yang beroperasi pada koleksi - itulah yang Anda gunakan applyuntuk (atau reduce, jika Anda tahu itu lebih masuk akal).
Michał Marczyk
6
Lucu, saran saya adalah kebalikannya: reduceketika ragu, applyketika Anda tahu pasti ada optimasi. reduceKontrak lebih tepat dan dengan demikian lebih rentan terhadap optimasi umum. applylebih kabur dan dengan demikian hanya dapat dioptimalkan berdasarkan kasus per kasus. strdan concatmerupakan dua pengecualian umum.
cgrand
1
@ grand Sebuah pengulangan ulang dari alasan saya mungkin kira-kira untuk fungsi di mana reducedan applysetara dalam hal hasil, saya berharap penulis fungsi tersebut tahu bagaimana cara terbaik untuk mengoptimalkan kelebihan variadic mereka dan hanya menerapkannya dalam hal reducejika memang itulah yang paling masuk akal (opsi untuk melakukannya tentu selalu tersedia dan menjadikannya default yang masuk akal). Saya melihat dari mana Anda berasal, reducejelas merupakan pusat kisah kinerja Clojure (dan semakin meningkat), sangat dioptimalkan dan sangat jelas.
Michał Marczyk
51
Untuk pemula yang melihat jawaban ini,
berhati-hatilah, mereka tidak sama:
Pendapat bervariasi- Di dunia Lisp yang lebih besar, reducepasti dianggap lebih idiomatis. Pertama, ada masalah variadik yang sudah dibahas. Juga, beberapa kompiler Lisp Umum sebenarnya akan gagal ketika applyditerapkan terhadap daftar yang sangat panjang karena cara mereka menangani daftar argumen.
Di antara Clojurists di lingkaran saya, menggunakan applydalam kasus ini tampaknya lebih umum. Saya merasa lebih mudah untuk grok dan lebih suka juga.
Itu tidak membuat perbedaan dalam kasus ini, karena + adalah kasus khusus yang dapat diterapkan pada sejumlah argumen. Reduce adalah cara untuk menerapkan fungsi yang mengharapkan sejumlah argumen (2) ke daftar argumen yang panjang dan sewenang-wenang.
Saya biasanya menemukan diri saya lebih suka mengurangi ketika bertindak pada segala jenis koleksi - berkinerja baik, dan merupakan fungsi yang cukup berguna secara umum.
Alasan utama yang akan saya gunakan berlaku adalah jika parameter berarti hal yang berbeda di posisi yang berbeda, atau jika Anda memiliki beberapa parameter awal tetapi ingin mendapatkan sisanya dari koleksi, mis.
Dalam kasus khusus ini saya lebih suka reducekarena lebih mudah dibaca : ketika saya membaca
(reduce + some-numbers)
Saya segera tahu bahwa Anda mengubah urutan menjadi nilai.
Dengan applysaya harus mempertimbangkan fungsi mana yang sedang diterapkan: "ah, itu +fungsinya, jadi saya mendapatkan ... satu nomor". Sedikit kurang lugas.
Saat menggunakan fungsi sederhana seperti +, tidak masalah yang mana yang Anda gunakan.
Secara umum, idenya adalah reduceoperasi yang terakumulasi. Anda menyajikan nilai akumulasi saat ini dan satu nilai baru ke fungsi akumulasi Anda. Hasil dari fungsi tersebut adalah nilai kumulatif untuk iterasi berikutnya. Jadi, iterasi Anda terlihat seperti:
cum-val[i+1] = F( cum-val[i], input-val[i]); please forgive the java-like syntax!
Untuk diterapkan, idenya adalah bahwa Anda mencoba memanggil fungsi yang mengharapkan sejumlah argumen skalar, tetapi mereka saat ini dalam koleksi dan perlu ditarik keluar. Jadi, alih-alih mengatakan:
Agak terlambat pada topik tetapi saya melakukan percobaan sederhana setelah membaca contoh ini. Ini adalah hasil dari balasan saya, saya tidak bisa menyimpulkan apa pun dari respons, tetapi sepertinya ada semacam tendangan caching di antara pengurangan dan penerapan.
Melihat kode sumber clojure mengurangi rekursi yang cukup bersih dengan internal-reduction, tidak menemukan apa pun pada penerapan yang berlaku sekalipun. Implementasi Clojure dari + untuk menerapkan pengurangan invoke secara internal, yang di-cache dengan repl, yang tampaknya menjelaskan panggilan ke-4. Bisakah seseorang mengklarifikasi apa yang sebenarnya terjadi di sini?
Saya tahu saya akan lebih suka mengurangi kapan saja saya bisa :)
rohit
2
Anda tidak harus memasukkan rangepanggilan ke dalam timeformulir. Letakkan di luar untuk menghilangkan gangguan konstruksi urutan. Dalam kasus saya, reducesecara konsisten mengungguli apply.
Davyzhu
3
Keindahan penerapan diberikan fungsi (+ dalam hal ini) dapat diterapkan pada daftar argumen yang dibentuk oleh argumen intervensi sebelumnya dengan koleksi akhir. Reduce adalah abstraksi untuk memproses item koleksi yang menerapkan fungsi untuk masing-masing dan tidak bekerja dengan kasus args variabel.
(apply + 1 2 3 [3 4])
=> 13
(reduce + 1 2 3 [3 4])
ArityException Wrong number of args (5) passed to: core/reduce clojure.lang.AFn.throwArity (AFn.java:429)
sum
fungsi bawaan seperti di haskell? Sepertinya operasi yang sangat umum.sum
Saya akan mengatakan bahwa Clojure memiliki fungsi ini, namanya+
dan Anda dapat menggunakannyaapply
. :-) Secara serius, saya pikir dalam Lisp, secara umum, jika fungsi variadik disediakan, biasanya tidak disertai oleh pembungkus yang beroperasi pada koleksi - itulah yang Anda gunakanapply
untuk (ataureduce
, jika Anda tahu itu lebih masuk akal).reduce
ketika ragu,apply
ketika Anda tahu pasti ada optimasi.reduce
Kontrak lebih tepat dan dengan demikian lebih rentan terhadap optimasi umum.apply
lebih kabur dan dengan demikian hanya dapat dioptimalkan berdasarkan kasus per kasus.str
danconcat
merupakan dua pengecualian umum.reduce
danapply
setara dalam hal hasil, saya berharap penulis fungsi tersebut tahu bagaimana cara terbaik untuk mengoptimalkan kelebihan variadic mereka dan hanya menerapkannya dalam halreduce
jika memang itulah yang paling masuk akal (opsi untuk melakukannya tentu selalu tersedia dan menjadikannya default yang masuk akal). Saya melihat dari mana Anda berasal,reduce
jelas merupakan pusat kisah kinerja Clojure (dan semakin meningkat), sangat dioptimalkan dan sangat jelas.Untuk pemula yang melihat jawaban ini,
berhati-hatilah, mereka tidak sama:
sumber
Pendapat bervariasi- Di dunia Lisp yang lebih besar,
reduce
pasti dianggap lebih idiomatis. Pertama, ada masalah variadik yang sudah dibahas. Juga, beberapa kompiler Lisp Umum sebenarnya akan gagal ketikaapply
diterapkan terhadap daftar yang sangat panjang karena cara mereka menangani daftar argumen.Di antara Clojurists di lingkaran saya, menggunakan
apply
dalam kasus ini tampaknya lebih umum. Saya merasa lebih mudah untuk grok dan lebih suka juga.sumber
Itu tidak membuat perbedaan dalam kasus ini, karena + adalah kasus khusus yang dapat diterapkan pada sejumlah argumen. Reduce adalah cara untuk menerapkan fungsi yang mengharapkan sejumlah argumen (2) ke daftar argumen yang panjang dan sewenang-wenang.
sumber
Saya biasanya menemukan diri saya lebih suka mengurangi ketika bertindak pada segala jenis koleksi - berkinerja baik, dan merupakan fungsi yang cukup berguna secara umum.
Alasan utama yang akan saya gunakan berlaku adalah jika parameter berarti hal yang berbeda di posisi yang berbeda, atau jika Anda memiliki beberapa parameter awal tetapi ingin mendapatkan sisanya dari koleksi, mis.
sumber
Dalam kasus khusus ini saya lebih suka
reduce
karena lebih mudah dibaca : ketika saya membacaSaya segera tahu bahwa Anda mengubah urutan menjadi nilai.
Dengan
apply
saya harus mempertimbangkan fungsi mana yang sedang diterapkan: "ah, itu+
fungsinya, jadi saya mendapatkan ... satu nomor". Sedikit kurang lugas.sumber
Saat menggunakan fungsi sederhana seperti +, tidak masalah yang mana yang Anda gunakan.
Secara umum, idenya adalah
reduce
operasi yang terakumulasi. Anda menyajikan nilai akumulasi saat ini dan satu nilai baru ke fungsi akumulasi Anda. Hasil dari fungsi tersebut adalah nilai kumulatif untuk iterasi berikutnya. Jadi, iterasi Anda terlihat seperti:Untuk diterapkan, idenya adalah bahwa Anda mencoba memanggil fungsi yang mengharapkan sejumlah argumen skalar, tetapi mereka saat ini dalam koleksi dan perlu ditarik keluar. Jadi, alih-alih mengatakan:
kita bisa bilang:
dan itu dikonversi menjadi setara dengan:
Jadi, menggunakan "berlaku" seperti "menghapus tanda kurung" di sekitar urutan.
sumber
Agak terlambat pada topik tetapi saya melakukan percobaan sederhana setelah membaca contoh ini. Ini adalah hasil dari balasan saya, saya tidak bisa menyimpulkan apa pun dari respons, tetapi sepertinya ada semacam tendangan caching di antara pengurangan dan penerapan.
Melihat kode sumber clojure mengurangi rekursi yang cukup bersih dengan internal-reduction, tidak menemukan apa pun pada penerapan yang berlaku sekalipun. Implementasi Clojure dari + untuk menerapkan pengurangan invoke secara internal, yang di-cache dengan repl, yang tampaknya menjelaskan panggilan ke-4. Bisakah seseorang mengklarifikasi apa yang sebenarnya terjadi di sini?
sumber
range
panggilan ke dalamtime
formulir. Letakkan di luar untuk menghilangkan gangguan konstruksi urutan. Dalam kasus saya,reduce
secara konsisten mengungguliapply
.Keindahan penerapan diberikan fungsi (+ dalam hal ini) dapat diterapkan pada daftar argumen yang dibentuk oleh argumen intervensi sebelumnya dengan koleksi akhir. Reduce adalah abstraksi untuk memproses item koleksi yang menerapkan fungsi untuk masing-masing dan tidak bekerja dengan kasus args variabel.
sumber