Nasıl foldfarklılıklar sıklıkla kafa karıştırıcı bir kaynak gibi görünüyor, bu yüzden işte daha genel bir genel bakış:
Bir [x1, x2, x3, x4 ... xn ]işlev fve tohum içeren n değerlerinin bir listesini katlamayı düşünün z.
foldl dır-dir:
- Sol çağrışımlı :
f ( ... (f (f (f (f z x1) x2) x3) x4) ...) xn
- Kuyruk özyinelemeli : Listeyi yineleyerek değeri daha sonra üretir
- Tembel : Sonuç gerekene kadar hiçbir şey değerlendirilmez
- Geriye doğru :
foldl (flip (:)) []listeyi ters çevirir.
foldr dır-dir:
- Sağ çağrışımlı :
f x1 (f x2 (f x3 (f x4 ... (f xn z) ... )))
- Bir bağımsız değişkene özyinelemeli : Her yineleme
f, bir sonraki değere ve listenin geri kalanının katlanmasının sonucuna uygulanır .
- Tembel : Sonuç gerekene kadar hiçbir şey değerlendirilmez
- İleri :
foldr (:) []değişmemiş bir liste döndürür.
Biraz ince nokta var burada bazen gezileri insanlar kadar o: Çünkü foldlolduğunu geriye her uygulama feklenir dışarıdan sonucunun; ve tembel olduğu için, sonuç alınana kadar hiçbir şey değerlendirilmez. Bu, sonucun herhangi bir bölümünü hesaplamak için Haskell'in önce iç içe geçmiş işlev uygulamalarının bir ifadesini oluşturarak tüm listeyi yinelediği , ardından en dıştaki işlevi değerlendirdiği ve argümanlarını gerektiği gibi değerlendirdiği anlamına gelir. Her fzaman ilk argümanını kullanırsa, bu Haskell'in en içteki terime kadar tekrar etmesi ve ardından her uygulamasını geriye doğru hesaplayarak çalışması gerektiği anlamına gelir f.
Bu, çoğu işlevsel programcının bildiği ve sevdiği verimli kuyruk özyinelemesinden açıkça farklıdır!
Aslında, foldlteknik olarak kuyruk özyinelemeli olsa da , sonuç ifadesinin tamamı herhangi bir şey değerlendirilmeden önce oluşturulduğundan, foldlyığın taşmasına neden olabilir!
Öte yandan, düşünün foldr. Aynı zamanda tembeldir, ancak ileriye doğru çalıştığı için , föğesinin her uygulaması sonucun içine eklenir . Dolayısıyla, sonucu hesaplamak için Haskell , ikinci argümanı katlanmış listenin geri kalanı olan tek bir işlev uygulaması oluşturur. Eğer fveri yapıcı, örneğin - - ikinci bağımsız değişken yavaş olan sonuç olacaktır aşamalı yavaş hesaplanan kat her adım, sadece bu değerlendirilir ihtiyacı o sonucun bir kısmı.
Öyleyse neden foldrbazen sonsuz listelerde işe yaramadığında işe yaradığını görebiliriz foldl: İlki, sonsuz bir listeyi tembel olarak başka bir tembel sonsuz veri yapısına dönüştürebilirken, ikincisi sonucun herhangi bir bölümünü oluşturmak için tüm listeyi incelemelidir. Öte yandan, foldrhemen her iki argümana da ihtiyaç duyan bir işlevle, örneğin (+), işe yarıyor (veya daha doğrusu çalışmıyor) foldl, değerlendirmeden önce büyük bir ifade oluşturmaya çok benzer .
Dolayısıyla dikkat edilmesi gereken iki önemli nokta şunlardır:
foldr tembel özyinelemeli bir veri yapısını diğerine dönüştürebilir.
- Aksi takdirde, tembel kıvrımlar büyük veya sonsuz listelerde yığın taşmasıyla çökecektir.
Her foldrşeyi foldlyapabilir gibi göründüğünü ve daha fazlasını fark etmiş olabilirsiniz. Bu doğru! Aslında, foldl neredeyse işe yaramaz!
Peki ya büyük (ama sonsuz değil) bir listeyi katlayarak tembel olmayan bir sonuç üretmek istersek? Bunun için, bir istiyoruz sıkı kat , standart kütüphaneler thoughfully sağlamak :
foldl' dır-dir:
- Sol çağrışımlı :
f ( ... (f (f (f (f z x1) x2) x3) x4) ...) xn
- Kuyruk özyinelemeli : Listeyi yineleyerek değeri daha sonra üretir
- Katı : Her işlev uygulaması süreç boyunca değerlendirilir
- Geriye doğru :
foldl' (flip (:)) []listeyi ters çevirir.
Çünkü foldl'olduğunu sıkı , Haskell olacak sonuç hesaplamak için değerlendirmek f yerine sol argüman vermektense, her adımda büyük, unevaluated ifadesini birikir. Bu bize istediğimiz olağan, verimli kuyruk özyinelemesini verir! Diğer bir deyişle:
foldl' büyük listeleri verimli bir şekilde katlayabilir.
foldl' sonsuz bir listede sonsuz bir döngüde asılı kalır (yığın taşmasına neden olmaz).
Haskell wiki'nin de bunu tartışan bir sayfası var .
foldrdaha iyi olduğufoldliçinde Haskell tersi de doğrudur, Erlang (daha önce öğrendiği Haskell ). Yana Erlang tembel değildir ve işlevleri değildir curried yüzdenfoldlde Erlang gibi davranırfoldl'üzerindedir. Bu harika bir cevap! İyi iş ve teşekkürler!