Apa cara paling idiomatis untuk mencapai sesuatu seperti berikut ini, di Haskell:
foldl (+) 0 [1,2,3,4,5]
--> 15
Atau yang setara di Ruby:
[1,2,3,4,5].inject(0) {|m,x| m + x}
#> 15
Jelas, Python menyediakan reduce
fungsi, yang merupakan implementasi dari lipatan, persis seperti di atas, namun, saya diberitahu bahwa cara pemrograman 'pythonic' adalah untuk menghindari lambda
istilah dan fungsi tingkat tinggi, lebih memilih pemahaman daftar jika memungkinkan. Oleh karena itu, apakah ada cara yang disukai untuk melipat daftar, atau struktur seperti daftar di Python yang bukan reduce
fungsinya, atau reduce
cara idiomatik untuk mencapai ini?
sum
tidak cukup baik?sum
, Anda mungkin ingin memberikan beberapa jenis contoh yang berbeda.sum()
sebenarnya menyediakan fungsionalitas terbatas dengan ini.sum([[a], [b, c, d], [e, f]], [])
kembali[a, b, c, d, e, f]
misalnya.+
daftar adalah operasi waktu linier baik dalam waktu maupun memori, membuat seluruh panggilan menjadi kuadrat. Penggunaannyalist(itertools.chain.from_iterable([a], [b,c,d],[e,f],[]])
linier secara keseluruhan - dan jika Anda hanya perlu mengulanginya satu kali, Anda dapat menghentikan panggilanlist
untuk membuatnya konstan dalam hal memori.Jawaban:
Cara Pythonic untuk menjumlahkan sebuah array digunakan
sum
. Untuk tujuan lain, terkadang Anda dapat menggunakan beberapa kombinasireduce
(darifunctools
modul) danoperator
modul, misalnya:Sadarilah bahwa
reduce
sebenarnya adalahfoldl
, dalam istilah Haskell. Tidak ada sintaks khusus untuk melakukan lipatan, tidak ada bawaanfoldr
, dan sebenarnya menggunakanreduce
dengan operator non-asosiatif dianggap gaya yang buruk.Menggunakan fungsi tingkat tinggi cukup bersifat pythonic; itu memanfaatkan prinsip Python bahwa semuanya adalah objek, termasuk fungsi dan kelas. Anda benar bahwa lambda disukai oleh beberapa Pythonistas, tetapi sebagian besar karena mereka cenderung tidak terlalu mudah dibaca ketika menjadi kompleks.
sumber
reduce()
cukup terbatas pada operator asosiatif, dan dalam semua kasus lain lebih baik untuk menulis lingkaran akumulasi secara eksplisit." Jadi, penggunaannya terbatas, tetapi bahkan GvR pun tampaknya harus mengakui bahwa ini cukup berguna untuk menyimpannya di pustaka standar.Haskell
foldl (+) 0 [1,2,3,4,5]
Python
reduce(lambda a,b: a+b, [1,2,3,4,5], 0)
Jelas, itu adalah contoh yang sepele untuk mengilustrasikan suatu hal. Dengan Python Anda hanya akan melakukannya
sum([1,2,3,4,5])
dan bahkan puritan Haskell umumnya lebih sukasum [1,2,3,4,5]
.Untuk skenario non-trivial ketika tidak ada fungsi kemudahan yang jelas, pendekatan pythonic idiomatik adalah dengan secara eksplisit menulis loop for dan menggunakan tugas variabel yang bisa berubah daripada menggunakan
reduce
atau afold
.Itu sama sekali bukan gaya fungsional, tetapi itu adalah cara "pythonic". Python tidak dirancang untuk puritan fungsional. Lihat bagaimana Python menyukai pengecualian untuk kontrol aliran untuk melihat bagaimana python idiomatik non-fungsional itu.
sumber
Di Python 3,
reduce
telah dihapus: Catatan rilis . Meskipun demikian, Anda dapat menggunakan modul functoolsDi sisi lain, dokumentasi mengungkapkan preferensi terhadap
for
-loop daripadareduce
, karenanya:sumber
reduce
tidak dihapus dari pustaka standar Python 3.reduce
dipindahkan kefunctools
modul saat Anda tunjukkan.Memulai
Python 3.8
, dan pengenalan ekspresi tugas (PEP 572) (:=
operator), yang memberikan kemungkinan untuk memberi nama hasil ekspresi, kita dapat menggunakan pemahaman daftar untuk mereplikasi apa yang oleh bahasa lain disebut operasi lipat / lipat / kurangi:Diberikan daftar, fungsi pereduksi dan akumulator:
kita dapat melipat
items
denganf
untuk mendapatkan dihasilkan tersebutaccumulation
:atau dalam bentuk kental:
Perhatikan bahwa ini sebenarnya juga merupakan operasi "scanleft" karena pemahaman daftar menunjukkan status akumulasi di setiap langkah:
sumber
Anda juga dapat menemukan kembali roda:
sumber
f
sekitar dalam kasus rekursif Anda.reduce
sudah ditawarkan (perhatikan bahwa tanda tangan fungsi reduce adalahreduce(function, sequence[, initial]) -> value
- ini, juga, mencakup fungsi memberikan nilai awal untuk aki).Tidak benar-benar menjawab pertanyaan tersebut, tetapi satu baris untuk foldl dan foldr:
sumber
reduce(lambda y, x: x**y, reversed(a))
. Sekarang memiliki penggunaan yang lebih alami, bekerja dengan iterator, dan mengkonsumsi lebih sedikit memori.Jawaban sebenarnya untuk masalah (kurangi) ini adalah: Cukup gunakan satu putaran!
Ini akan lebih cepat daripada pengurangan dan hal-hal seperti PyPy dapat mengoptimalkan loop seperti itu.
BTW, kasus penjumlahan harus diselesaikan dengan
sum
fungsisumber
reduce
adalah cara umum untuk mengoptimalkan program Python.product
terhadap satu dalam gaya Anda, dan itu lebih cepat (meskipun secara marginal).operator.add
) sebagai argumen untuk dikurangi: Panggilan ekstra itu adalah panggilan C (yang jauh lebih murah daripada panggilan Python), dan menghemat pengiriman dan interpretasi beberapa instruksi bytecode, yang dapat dengan mudah menyebabkan lusinan panggilan fungsi.Saya yakin beberapa responden dari pertanyaan ini telah melewatkan implikasi yang lebih luas dari
fold
fungsi sebagai alat abstrak. Ya,sum
dapat melakukan hal yang sama untuk daftar bilangan bulat, tetapi ini adalah kasus yang sepele.fold
lebih umum. Ini berguna saat Anda memiliki urutan struktur data dengan berbagai bentuk dan ingin mengekspresikan agregasi dengan rapi. Jadi, alih-alih harus membangunfor
loop dengan variabel agregat dan menghitung ulang secara manual setiap kali,fold
fungsi (atau versi Python, yangreduce
tampaknya sesuai) memungkinkan pemrogram untuk mengekspresikan maksud agregasi jauh lebih jelas dengan hanya menyediakan dua hal:sumber
fold
yang sulit dilakukan dengan bersih dengan Python, dan kemudian "fold
" itu di Python :-)Saya mungkin terlambat ke pesta, tetapi kita dapat membuat kustom
foldr
menggunakan kalkulus lambda sederhana dan fungsi kari. Berikut adalah implementasi saya dari foldr dengan python.Meskipun implementasinya rekursif (mungkin lambat), itu akan mencetak nilai
15
dan120
masing - masingsumber