Bir tür liste dışında kat, ne demek?
Tek bağlı bir liste düşünün. Gibi bir şey görünüyor
data List x = Node x (List x) | End
Doğal katlama gibi bir işlev tanımlayın
reduce :: (x -> y -> y) -> y -> List x -> y
, reduce f x0
bir anlamda değiştirir f
29 *Her x0
End
her. Bu Başlangıcı olarak ifade ederkat.
Şimdi düşünün basit bir ikili ağaç:
data Tree x = Leaf x | Branch (Tree x) (Tree x)
Benzer şekilde doğal böyle bir fonksiyonu olarak tanımlamak için
reduce :: (y -> y -> y) -> (x -> y) -> Tree x -> y
Dikkat edinbuazalma oldukça farklı bir karakteri var; liste tabanlı bir doğal olarak sıralı ise, ağaç tabanlı bu yeni bir böl ve fethet hissediyorum daha fazla. Orada par
birkaç combinators atma düşünebiliriz bile. (Liste sürümünde böyle bir şey koyar mısınız?)
Benim sorum: bu fonksiyonu hala bir olarak sınıflandırılır"", yoksa başka bir şey mi? kat. (Ve eğer öyleyse, ne oldu?)
Herkes katlanır bahsettiği zaman aslında, onlar her zaman katlanır hakkında konuşmaklistelerdoğal olarak sıralı. "Sıralı" ne kat tanımı var mı, yoksa bu sadece katlanır. yaygın olarak kullanılan en örnek rastlantısal bir özelliği olup olmadığını bir parçası olup olmadığını merak ediyorum
CEVAP
Bir Her Durum için Katlayın
Aslında farklı bir sürü için geçerli olan katlama genel bir kavram bulabiliriz. Yani, sistematik listeler, ağaçlar ve daha fazlası için fold
bir fonksiyon tanımlayabiliriz.
fold
bu genel kavram @pelotom yaptığı açıklamada söz konusu catamorphisms karşılık gelir.
Yinelemeli Tipler
Anahtar ınsight fold
bu işlevler yoluyla belirlenirözyinelemelitürleri. Özellikle:
data List a = Cons a (List a) | Nil
data Tree a = Branch (Tree a) (Tree a) | Leaf a
Bu tür İki netCons
ve Branch
durumda Tree
--List
tekrar eder.
Puan Sabit
Sadece fonksiyonlar gibi, bu tür sabit noktaları kullanarak yazabiliriz. fix
tanımı unutma
fix f = f (fix f)
Aslında ekstra yapıcı bir sarıcı olmak zorunda dışında bu tür için çok benzer bir şey yazabilir miyiz:
newtype Fix f = Roll (f (Fix f))
fix
bir sabit nokta tanımlar gibiişlevibu bir sabit nokta tanımlarfunctor. Tüm özyinelemeli tipleri Fix
bu yeni türünü kullanarak ifade edebiliriz.
Bu bize aşağıdaki gibidir: List
türleri yeniden yazmak için izin verir
data ListContainer a rest = Cons a rest | Nil
type List a = Fix (ListContainer a)
Esasen, Fix
bize keyfi derinliklerine ListContainer
s yuva sağlar. Sahip olmamız için:
Roll Nil
Roll (Cons 1 (Roll Nil))
Roll (Cons 1 (Roll (Cons 2 (Roll Nil))))
[]
, [1]
[1,2]
sırasıyla karşılık.
ListContainer
Functor
bir olduğunu görmek kolaydır:
instance Functor (ListContainer a) where
fmap f (Cons a rest) = Cons a (f rest)
fmap f Nil = Nil
List
60 *eşleme gayet doğal bence: açıkça recursing yerine, özyinelemeli parçası olan bir değişken yapıyoruz. O zaman sadece Fix
değişken uygun olarak doldurmak için kullanın.
Tree
benzer bir türü de yazabiliriz.
"Açılması" Sabit Noktalar
Neden önemsiyoruz? fold
tanımlayabilirizkeyfitür yazılı Fix
kullanarak. Özellikle:
fold :: Functor f => (f a -> a) -> (Fix f -> a)
fold h = h . fmap (fold h) . unRoll
where unRoll (Roll a) = a
Tüm kat "yuvarlandı" türü bir kerede, sonuç her zaman için bir işlevi uygulamak. aç aslında yok. Bu "sağlar, bizi tanımlayan" herhangi bir özyinelemeli türü için katlayın ve düzgün ve doğal bir kavram genelleme. çözümü
Bu liste, örneğin, bu gibi çalışır:
- Her adımda,
Roll
Cons
Nil
bir de almamız çıkar - Listede adı
fmap
kullanarak geri kalanını biz recurse.Nil
durumunda,fmap (fold h) Nil = Nil
, bu yüzden biz sadece 73* *dönüş.Cons
durumunda,fmap
sadece listenin geri kalanı üzerinde kat devam ediyor.
- Sonunda
fold
iç içe geçmiş bir sürü arama 78 **standardı gibiNil
--bir bitiş.
Karşılaştırma Türleri
Şimdi iki kat fonksiyonları türleri bakmak sağlar. , 79**: ilk
foldr :: (a -> b -> b) -> b -> [a] -> b
Şimdi 82**, fold
uzman:
fold :: (ListContainer a b -> b) -> (Fix (ListContainer a) -> b)
İlk başta, bu tamamen farklı görünüyor. Ancak masaj yaparak biraz, hepsi aynı gösterebiliriz. foldr
ilk iki argümanın a -> b -> b
86*. Bir fonksiyon ve bir sabit var. () -> b
b
düşünebiliriz. Şimdi _
()
a -> b
iki fonksiyonları _ -> b
var. Hayatı daha basit hale getirmek için, ikinci işlevi bize (a, b) -> b
köri izin veriyor. Şimdi bir olarak yazabiliriztekfonksiyonu Either
kullanarak:
Either (a, b) () -> b
Bu gerçek f :: a -> c
ve her zaman aşağıdaki yazabiliriz g :: b -> c
verilen: çünkü
h :: Either a b -> c
h (Left a) = f a
h (Right b) = g b
Şimdi foldr
olarak görebiliriz:
foldr :: (Either (a, b) () -> b) -> ([a] -> b)
(Hep böyle ->
parantez içine doğru ilişkilendirilebilir oldukları sürece eklemek ücretsizdir.)
Şimdi ListContainer
bakalım. Bu tip iki durum vardır: hiçbir bilgi taşıyan Nil
ve de a
ve b
. bir olan Cons
, ()
109 *yazabiliriz:* 110*, gibi gibi başka bir yol, 107 ** konur
type ListContainer a rest = Either (a, rest) ()
Açıkça bu foldr
ben yukarıda kullanılan aynı. Şimdi var:
foldr :: (Either (a, b) () -> b) -> ([a] -> b)
fold :: (Either (a, b) () -> b) -> (List a -> b)
Yani, aslında, bu tür aynı şeyi yazma izomorfik ... sadece farklı yolları vardır! Bu oldukça serin olduğunu düşünüyorum.
Eğer türleriyle akıl yürütme bu tür hakkında daha fazla şey öğrenmek istiyorsanız (bir yan not olarak, The Algebra of Algebraic Data Types, sadece bu konuda blog yazıları güzel bir dizi göz atın.)
Ağaçlar için geri
Yani, türler sabit noktalar olarak yazılmış fold
jenerik tanımlayabiliriz nasıl gördük. Ayrıca bu doğrudan listeler için foldr
nasıl karşılık geldiğini gördük. Şimdi ikinci örnek olarak, ikili ağaç görünüm sağlar. Tipi var:
data Tree a = Branch a (Tree a) (Tree a) | Leaf a
yukarıda yaptığım kurallara uyarak bu Fix
kullanarak yazabiliriz: a tipi değişken kısım: özyinelemeli değiştirdik
data TreeContainer a rest = Branch rest rest | Leaf a
type Tree a = Fix (TreeContainer a)
Şimdi bir ağaç 119**:
fold :: (TreeContainer a b -> b) -> (Tree a -> b)
foldTree
orijinal bu gibi görünüyor:
foldTree :: (b -> b -> b) -> (a -> b) -> Tree a -> b
foldTree
iki işlevi kabul eder; önce ve sonra Either
tımar kullanarak: birine birleştireceğiz
foldTree :: (Either (b, b) a -> b) -> (Tree a -> b)
Ayrıca Either (b, b) a
TreeContainer a b
izomorfik olduğunu nasıl görebiliriz. Ağaç kaba sahip iki vaka:* *128, b
s Leaf
, 131* *birini içeren iki içeren.
Bu tür listesi örnek olarak aynı şekilde izomorfik olan kat.
Genelleme
Kesin bir dizi ortaya çıktı. Normal özyinelemeli veri türü dikkate alındığında, sistematik bir şekilde bizi bir functor sabit bir nokta olarak türünü ifade etmelerini sağlayan türü, olmayan özyinelemeli bir sürümünü oluşturabilirsiniz. Bu mekanik olarak tüm bu farklı aslında ... fold
fonksiyonları ile, muhtemelen tüm işlemi otomatik olarak yapabiliriz böyle DZD Jenerik falan kullanarak bulabiliriz anlamına gelir.
Bir anlamda, bu gerçekten farklı türleri için fold
farklı işlevlere sahip değiliz anlamına gelir. Daha doğrusu, olan fold
tek bir işlevi varçokpolimorfik.
Daha fazla
Ben ilk tam a talk Conal Elliott tarafından verilen bu fikirleri anlaşılır. Bu daha ayrıntılı gider ve de 136* * * çift ** 135, bahsediyor.
Eğer bu tür şeylere dalmak istiyorsanız biledaha fazladerinden, "Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire" paper fantastik okuyun. Diğer şeyler arasında, bu kavramları "" ve "anamorphisms" hangi kıvrımlar ve izlerken karşılık gelir. catamorphisms tanıttı
Cebir (ve Coalgebras)
Ayrıca, dayanamıyorum ekleme Eklentisi için kendimi :P. görebilirsiniz bazı ilginç benzerlikler arasındaki yolu kullanıyoruz Either
burada ve ne zaman konuşmak hakkında algebras başka bir cevap.
Aslında fold
ve Cebir arasında derin bir bağ vardır; ayrıca, unfold
--fold
--söz konusu çift cebiri çift olan coalgebras bağlı. Önemli fikir cebirsel veri türleri için "ilk cebiri" da cevabımı geri kalanı özetlendiği gibi kıvrımlar tanımlayın. karşılık gelen.
fold
genel türü bu bağlantıda görebilirsiniz:
fold :: Functor f => (f a -> a) -> (Fix f -> a)
Çok tanıdık geldi f a -> a
terim! F-Cebir gibi bir şey olarak tanımlandı: bir hatırlayın
class Functor f => Algebra f a where
op :: f a -> a
fold
gibi düşünebiliriz yani:
fold :: Algebra f a => Fix f -> a
Esasen, fold
bize "" yapıları Cebir kullanılarak tanımlanan. özetlemek sağlar
Ne zaman bir Liste kullanın vs bir Lin...
Yazıyor dosya "ihtiyacı update&qu...
Liste Görünümü altbilgi gizlemek?...
Neden liste başlatma (kıvırcık parante...
Liste<int> test = {1, 2, 3} - bi...