Nasıl fold
farklı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 f
ve 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ü foldl
olduğunu geriye her uygulama f
eklenir 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 f
zaman 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, foldl
teknik olarak kuyruk özyinelemeli olsa da , sonuç ifadesinin tamamı herhangi bir şey değerlendirilmeden önce oluşturulduğundan, foldl
yığı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 f
veri 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 foldr
bazen 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, foldr
hemen 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 foldl
yapabilir 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 .
foldr
daha iyi olduğufoldl
içinde Haskell tersi de doğrudur, Erlang (daha önce öğrendiği Haskell ). Yana Erlang tembel değildir ve işlevleri değildir curried yüzdenfoldl
de Erlang gibi davranırfoldl'
üzerindedir. Bu harika bir cevap! İyi iş ve teşekkürler!