Pertama, Real World Haskell , yang saya baca, mengatakan untuk tidak pernah menggunakan foldl
dan sebaliknya menggunakannya foldl'
. Jadi saya percaya itu.
Tapi aku kabur pada saat menggunakan foldr
vs foldl'
. Meskipun saya dapat melihat struktur bagaimana mereka bekerja secara berbeda diletakkan di depan saya, saya terlalu bodoh untuk mengerti kapan "mana yang lebih baik." Saya kira menurut saya seharusnya tidak masalah yang digunakan, karena mereka berdua menghasilkan jawaban yang sama (bukan?). Sebenarnya, pengalaman saya sebelumnya dengan konstruksi ini adalah dari versi Ruby inject
dan Clojure reduce
, yang sepertinya tidak memiliki versi "kiri" dan "kanan". (Pertanyaan sampingan: versi apa yang mereka gunakan?)
Wawasan apa pun yang dapat membantu orang yang memiliki kecerdasan seperti saya akan sangat dihargai!
sumber
fodlWhile
); 2. mengubahnya menjadi pemindaian kiri (scanl
) dan hentikan denganlast . takeWhile p
atau serupa. Uh, dan 3. gunakanmapAccumL
. :)foldl
, yang mengumpulkan hasil keseluruhannya dan memproduksinya hanya setelah semua pekerjaan selesai dan tidak ada lagi langkah untuk dilakukan. Jadi misalnyafoldl (flip (:)) [] [1..3] == [3,2,1]
, jadiscanl (flip(:)) [] [1..] = [[],[1],[2,1],[3,2,1],...]
... TKI,foldl f z xs = last (scanl f z xs)
dan daftar tak terbatas tidak memiliki elemen terakhir (yang, dalam contoh di atas, itu sendiri akan menjadi daftar tak terbatas, dari INF ke 1).foldr
terlihat seperti ini:foldl
terlihat seperti ini:Konteks: Lipat di wiki Haskell
sumber
Semantik mereka berbeda sehingga Anda tidak bisa hanya bertukar
foldl
danfoldr
. Yang satu melipat elemen dari kiri, yang lain dari kanan. Dengan begitu, operator diterapkan dalam urutan yang berbeda. Ini penting untuk semua operasi non-asosiatif, seperti pengurangan.Haskell.org memiliki artikel yang menarik tentang masalah ini.
sumber
Singkatnya,
foldr
lebih baik ketika fungsi akumulator malas pada argumen keduanya. Baca lebih lanjut di Stack Overflow Haskell wiki (pun intended).sumber
Alasannya
foldl'
lebih disukaifoldl
untuk 99% dari semua penggunaan adalah dapat digunakan dalam ruang konstan untuk sebagian besar penggunaan.Ambil fungsinya
sum = foldl['] (+) 0
. Ketikafoldl'
digunakan, jumlahnya langsung dihitung, jadi menerapkansum
ke daftar tak terbatas hanya akan berjalan selamanya, dan kemungkinan besar di ruang konstan (jika Anda menggunakan hal-hal sepertiInt
s,Double
s,Float
s.Integer
S akan menggunakan lebih dari ruang konstan jika nomor menjadi lebih besar darimaxBound :: Int
).Dengan
foldl
, thunk dibangun (seperti resep bagaimana mendapatkan jawabannya, yang dapat dievaluasi nanti, daripada menyimpan jawabannya). Pemukul ini bisa menghabiskan banyak ruang, dan dalam hal ini, jauh lebih baik untuk mengevaluasi ekspresi daripada menyimpan pemogokan (mengarah ke tumpukan meluap ... dan membawa Anda ke ... oh tidak apa-apa)Semoga itu bisa membantu.
sumber
foldl
melakukan apa-apa selain menerapkan konstruktor ke satu atau lebih argumennya.foldl
sebenarnya pilihan terbaik? (Seperti daftar yang tidak terbatas kapanfoldr
pilihan yang salah, darifoldr
menjadi pilihan yang salah untuk daftar tak terbatas. Bahkan, Anda tidak boleh menggunakanfoldl
ataufoldl'
untuk daftar yang tak terbatas. Lihat wiki Haskell di stack overflowsBy the way, Ruby
inject
dan Clojure inireduce
adalahfoldl
(ataufoldl1
, tergantung pada versi yang Anda gunakan). Biasanya, ketika hanya ada satu bentuk dalam bahasa, itu adalah lipatan kiri, termasuk Pythonreduce
, PerlList::Util::reduce
, C ++accumulate
, C #Aggregate
, Smalltalkinject:into:
, PHParray_reduce
, MathematicaFold
, dll. Standar umum Lispreduce
untuk lipatan kiri tetapi ada opsi untuk lipatan kanan.sumber
reduce
tidak malas, jadi itufoldl'
dan banyak pertimbangan di sini tidak berlaku.foldl'
, karena mereka adalah bahasa yang ketat, bukan? Kalau tidak, bukankah itu berarti semua versi itu menyebabkan stack overflow seperti yangfoldl
terjadi?Seperti yang ditunjukkan Konrad , semantik mereka berbeda. Mereka bahkan tidak memiliki tipe yang sama:
Sebagai contoh, daftar append operator (++) dapat diimplementasikan dengan
foldr
assementara
akan memberi Anda kesalahan ketik.
sumber
foldl subtract 0 [1, 2, 3, 4]
evaluate to-10
, sementarafoldr subtract 0 [1, 2, 3, 4]
evaluate to-2
.foldl
sebenarnya0 - 1 - 2 - 3 - 4
saatfoldr
ini4 - 3 - 2 - 1 - 0
.foldr (-) 0 [1, 2, 3, 4]
adalah-2
danfoldl (-) 0 [1, 2, 3, 4]
sedang-10
. Di sisi lain,subtract
adalah mundur dari apa yang Anda harapkan (subtract 10 14
adalah4
), sehinggafoldr subtract 0 [1, 2, 3, 4]
adalah-10
danfoldl subtract 0 [1, 2, 3, 4]
adalah2
(positif).