Corecursion - Corecursion
Gelen bilgisayar bilimleri , corecursion olan operasyonun bir türüdür çift için özyineleme . Özyineleme, temel bir durumdan daha uzaktaki verilerden başlayıp daha küçük verilere bölerek ve bir temel duruma ulaşana kadar tekrar ederek analitik olarak çalışırken, corecursion, temel bir durumdan başlayıp onu oluşturarak sentetik olarak çalışır ve yinelemeli olarak bir temel durum. Basitçe ifade etmek gerekirse, corecursive algoritmalar, daha fazla veri biti üretmek için kullanılabilir hale geldikçe ve ihtiyaç duyuldukça kendi ürettikleri verileri parça parça kullanırlar. Benzer bir ama farklı bir kavramdır generatif yineleme corecursion ve yineleme doğasında kesin bir "yönü" eksikliği olabilir.
Özyinelemenin, programların keyfi olarak karmaşık veriler üzerinde çalışmasına izin verdiği durumlarda, basit verilere (temel durumlar) indirgenebildikleri sürece, corecursion, programların, üretilebildiği sürece akışlar gibi rastgele karmaşık ve potansiyel olarak sonsuz veri yapıları üretmesine izin verir. basit verilerden (temel durumlar) bir dizi sonlu adımda. Özyinelemenin sona ermediği, hiçbir zaman bir temel duruma ulaşamadığı durumlarda, çözümleme temel bir durumdan başlar ve böylece sonsuza kadar ilerleyebilir (ve bu nedenle katı değerlendirme altında sona ermese de) belirleyici olarak sonraki adımları üretir veya ürettiğinden daha fazlasını tüketebilir ve böylece üretken olmaz . Geleneksel olarak özyinelemeli olarak analiz edilen birçok işlev, alternatif olarak ve muhtemelen daha doğal olarak, belirli bir aşamada sonlandırılan, örneğin faktöriyel gibi yineleme ilişkileri olarak yorumlanabilir.
Corecursion her ikisini de üretebilir sıvılan ve sonsuz veri yapıları olarak kullanılması, ve kullanabilir kendini referans veri yapıları. Corecursion, genellikle potansiyel olarak sonsuz bir yapının yalnızca sonlu bir alt kümesini üretmek için (aynı anda tüm sonsuz bir yapıyı üretmeye çalışmak yerine) tembel değerlendirme ile birlikte kullanılır . Corecursion, corecursion ve codata'nın toplam dillerin sonsuz veri yapılarıyla çalışmasına izin verdiği işlevsel programlamada özellikle önemli bir kavramdır .
Örnekler
Corecursion, daha tanıdık olan özyinelemenin aksine anlaşılabilir. Düzeltme, öncelikle fonksiyonel programlamaya ilgi duysa da, aşağıda Python'daki jeneratör tesisi kullanılarak yapılan zorunlu programlama kullanılarak gösterilebilir . Bu örneklerde yerel değişkenler kullanılır ve zorunlu olarak (yıkıcı bir şekilde) değerler atanır , ancak bunlar saf fonksiyonel programlamada anlatım için gerekli değildir. Saf fonksiyonel programlamada, yerel değişkenlere atamak yerine, bu hesaplanan değerler değişmez bir sıra oluşturur ve önceki değerlere kendi kendine referansla erişilir (sıradaki daha sonraki değerler hesaplanacak sıradaki önceki değerleri referans alır). Ödevler bunu basitçe zorunlu paradigmada ifade eder ve açıklamayı netleştirmeye hizmet eden hesaplamaların nerede olduğunu açıkça belirtir.
Faktöriyel
Klasik bir özyineleme örneği, özyinelemeli olarak 0 ile tanımlanan faktöriyel hesaplamaktır ! : = 1 ve n! : = n × (n - 1)! .
İçin yinelemeli Belirli bir girdi üzerindeki sonucu hesaplamak, bir özyinelemeli fonksiyon çağrıları (bir kopyası) kendisi farklı bir ile (bir şekilde "küçük") giriş ve sonuç oluşturmak için bu çağrısının sonucunu kullanır. Özyinelemeli çağrı, temel duruma ulaşılmadığı sürece aynı şeyi yapar . Böylece süreç içinde bir çağrı yığını oluşur. Örneğin, fac (3) 'ü hesaplamak için , bu yinelemeli olarak fac (2) , fac (1) , fac (0) (yığını "sarma" ) çağırır , bu noktada özyineleme, fac (0) = 1 ile sona erer. ve daha sonra yığın ters sırada çözülür ve sonuçlar , son sonucu 3 × olarak hesaplamak için fac (2) = 2 sonucunu kullanan ilk çağrı çerçevesi fac (3) ' e çağrı yığını boyunca geri giderken hesaplanır. 2 = 3 × fac (2) =: fac (3) ve sonunda fac (3) = 6 döndürür . Bu örnekte, bir işlev tek bir değer döndürür.
Bu yığın çözülme açıklanabilir, faktöriyelleri tutarlı bir şekilde tanımlayarak, bir yineleyici olarak tanımlanabilir , burada biri büyük / küçük harf ile başlar , daha sonra bu başlangıç değerinden 1, 2, 3 ... sayılarını artırmak için faktöriyel değerler oluşturur , yukarıdaki özyinelemeli tanımda olduğu gibi onu okuyarak, olduğu gibi "zaman ok", tersine geriye kadar . Bu şekilde tanımlanan corecursive algoritma , tüm faktöriyellerin bir akışını üretir . Bu somut bir jeneratör olarak uygulanabilir . Sembolik olarak, aşağıdaki faktöriyel değerinin hesaplanması her ikisinin tutulması parçayı gerektirir belirterek n ve f (önceki bir faktöriyel değeri), bu şekilde temsil edilebilir:
veya Haskell'de ,
(\(n,f) -> (n+1, f*(n+1))) `iterate` (0,1)
yani " her adımda bir sonraki değerler şu şekilde hesaplanır ". Bu da matematiksel olarak denk ve yinelemeli tanımı ile hemen hemen aynıdır, fakat faktör değerleri inşa edilmektedir vurgular kadar , başlangıç durumunda ileri doğru giderek yerine, önce geri gittikten sonra hesaplanan olan aşağı a, temel duruma kadar azaltmaya. Corecursive fonksiyonun doğrudan çıktısı sadece faktöriyel değerleri içermez , aynı zamanda her bir değer için dizideki indeksinin n yardımcı verilerini de içerir , böylece gerektiğinde ve gerektiğinde hepsi arasından herhangi bir spesifik sonuç seçilebilir.
Bir bağlantı yoktur denotasyonel semantik , özyinelemeli programların gösterimlerin bu şekilde corecursively inşa edilmiştir.
Python'da, özyinelemeli bir faktöryel fonksiyon şu şekilde tanımlanabilir:
def factorial(n):
"""Recursive factorial function."""
if n == 0:
return 1
else:
return n * factorial(n - 1)
Bu daha sonra örneğin 5'ifactorial(5) hesaplamak için çağrılabilir ! .
Karşılık gelen bir corecursive generator şu şekilde tanımlanabilir:
def factorials():
"""Corecursive generator."""
n, f = 0, 1
while True:
yield f
n, f = n + 1, f * (n + 1)
Bu, sırayla sonsuz bir faktöriyel akışı oluşturur; bunun sınırlı bir kısmı şu şekilde üretilebilir:
def n_factorials(k):
n, f = 0, 1
while n <= k:
yield f
n, f = n + 1, f * (n + 1)
Bu daha sonra 5'e kadar olan faktöriyelleri üretmek için çağrılabilir ! üzerinden:
for f in n_factorials(5):
print(f)
Sadece belirli bir faktöryel ile ilgileniyorsak, sadece son değer alınabilir veya üretimi ve erişimi tek bir fonksiyonda birleştirebiliriz,
def nth_factorial(k):
n, f = 0, 1
while n < k:
n, f = n + 1, f * (n + 1)
yield f
Hali hazırda burada görüldüğü gibi, bu (sadece ikame pratik olarak eşdeğerdir return sadece yield Akümülatör bağımsız değişken tekniğine orada) kuyruk yineleme açık döngü içine, açılana. Bu nedenle, düzeltme kavramının, uygulanabilir olduğunda yinelemeli tanımlarla yinelemeli hesaplama süreçlerinin düzenlemesinin bir açıklaması olduğu söylenebilir.
Fibonacci Dizisi
Aynı şekilde, Fibonacci dizisi şu şekilde temsil edilebilir:
Fibonacci dizisi 2. dereceden bir tekrarlama ilişkisi olduğu için, corecursive ilişki birbirini izleyen iki terimi takip etmelidir, buna karşılık gelen bir adım ileriye kaymalı ve bir sonraki terimi hesaplamaya karşılık gelmelidir . Bu daha sonra aşağıdaki gibi uygulanabilir ( paralel atama kullanılarak ):
def fibonacci_sequence():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
Haskell'de,
map fst ( (\(a,b) -> (b,a+b)) `iterate` (0,1) )
Ağaç geçişi
Önce derinlik yaklaşımı yoluyla ağaç dolaşımı , klasik bir özyineleme örneğidir. Dually, genişliği birinci geçişi çok doğal corecursion yoluyla uygulanabilir.
Özyineleme veya özyineleme özel olarak kullanılmadan, kök düğümden başlayarak, alt düğümlerini bir veri yapısına yerleştirerek, ardından çıkarılan her düğümün çocuklarını bu veri yapısına geri yerleştirirken veri yapısından düğümler ardına düğümleri kaldırarak yineleme yaparak bir ağaçtan geçilebilir. . Veri yapısı bir yığın (LIFO) ise, bu derinlikten önce geçiş sağlar ve veri yapısı bir kuyruksa (FIFO), bu genişlikten önce geçiş sağlar.
Özyinelemeyi kullanarak, bir (sıra sonrası) derinlik-birinci geçiş, kök düğümden başlayarak ve sırayla her bir alt ağacın yinelemeli olarak (her bir alt düğüme dayanan alt ağaç) geçişi ile uygulanabilir - ikinci alt ağaç, ilk alt ağaç bitmiştir. Bir yaprak düğüme ulaşıldığında veya bir dal düğümünün çocukları tükendiğinde, düğümün kendisi ziyaret edilir (örneğin, düğümün kendisinin değeri çıkarılır). Bu durumda, çağrı yığını (özyinelemeli işlevlerin) üzerinde yinelenen yığın görevi görür.
Corecursion kullanılarak, bir genişlik-ilk geçiş, kök düğümden başlayarak, değerini çıkararak, ardından alt-ağaçların tüm listesini bir sonraki adıma (tek bir alt ağaç değil) geçerek gerçekleştirilebilir. özyinelemeli yaklaşımda) - bir sonraki adımda tüm kök düğümlerinin değerini çıktılar, sonra alt ağaçlarını geçirirler, vb. Bu durumda, üreteç işlevi, aslında çıktı dizisinin kendisi kuyruk görevi görür. Faktöriyel örnekte (yukarıda) olduğu gibi, indeksin yardımcı bilgisinin (hangi adımda, n'de olduğu ) ileri itildiği, n ! 'Nin gerçek çıktısına ek olarak, bu durumda kalan alt ağaçların yardımcı bilgileri gerçek çıktıya ek olarak ileri itildi. Sembolik:
yani her adımda, kök düğümlerin değerlerinin listesi çıkarılır ve ardından alt ağaçlara geçilir. Bu diziden sadece düğüm değerlerinin üretilmesi, basitçe yardımcı alt ağaç verilerinin atılmasını ve ardından listelerin listesinin düzleştirilmesini gerektirir (değerler başlangıçta seviyeye (derinlik) göre gruplandırılır; düzleştirme (grup çözme) düz bir doğrusal liste verir). Haskell'de,
concatMap fst ( (\(v, t) -> (rootValues v t, childTrees t)) `iterate` ([], fullTree) )
Bunlar aşağıdaki gibi karşılaştırılabilir. Özyinelemeli geçiş, bir yaprak düğümünü ( altta ) temel durum olarak ele alır (çocuk olmadığında, sadece değeri verir) ve bir ağacı alt ağaçlara analiz eder , her birini sırayla geçerek sonuçta sadece yaprak düğümleri - gerçek yaprak düğümleri ve çocukları zaten ele alınmış olan dal düğümleri ( aşağıda kesilmiştir ). Buna karşılık, corecursive traversal, bir kök düğümü (en üstte ) temel durum olarak ele alır (bir düğüm verilir, önce değeri verir), bir ağacı bir kök düğüm ve onun alt öğelerinin sentezlenmiş olarak ele alır , sonra yardımcı çıktı olarak a üretir her adımdaki alt ağaçların listesi, bunlar daha sonra bir sonraki adımın girdisidir - orijinal kökün alt düğümleri, ebeveynleriyle zaten ilgilenildiği için bir sonraki adımdaki kök düğümlerdir ( yukarıda kesilmiştir ). Özyinelemeli geçişte, yaprak düğümleri ile dal düğümleri arasında bir ayrım varken, corecursive geçişte hiçbir ayrım yoktur, çünkü her düğüm, tanımladığı alt ağacın kök düğümü olarak değerlendirilir.
Bilhassa, sonsuz bir ağaç verildiğinde, corecursive en-ilk geçiş, sonlu bir ağaçta olduğu gibi tüm düğümleri geçecektir; bununla birlikte, özyinelemeli derinlik-ilk geçiş bir daldan aşağı gidecek ve tüm düğümleri geçmeyecek ve gerçekten de son-sırayı geçiyorsanız , bu örnekte olduğu gibi (veya sırayla), hiçbir düğümü ziyaret etmeyecektir, çünkü hiçbir zaman bir yaprağa ulaşmaz. Bu, sonsuz veri yapılarıyla uğraşmak için özyinelemeden ziyade düzeltmenin yararlılığını gösterir.
Python'da bu aşağıdaki şekilde uygulanabilir. Olağan sipariş sonrası derinlik geçişi şu şekilde tanımlanabilir:
def df(node):
"""Post-order depth-first traversal."""
if node is not None:
df(node.left)
df(node.right)
print(node.value)
Bu daha sonra df(t) ağacın düğümlerinin değerlerini sipariş sonrası derinlik sırasına göre yazdırmak için çağrılabilir .
Genişlik ilk corecursive generator şu şekilde tanımlanabilir:
def bf(tree):
"""Breadth-first corecursive generator."""
tree_list = [tree]
while tree_list:
new_tree_list = []
for tree in tree_list:
if tree is not None:
yield tree.value
new_tree_list.append(tree.left)
new_tree_list.append(tree.right)
tree_list = new_tree_list
Bu, daha sonra ağacın düğümlerinin değerlerini enine birinci sırayla yazdırmak için çağrılabilir:
for i in bf(t):
print(i)
Tanım
İlk veri türleri , bazı tip denklemlerin en az sabit noktası ( izomorfizme kadar ) olarak tanımlanabilir; izomorfizm sonra verilir , ilk cebir . İkili olarak, son (veya son) veri türleri , bir tip denkleminin en büyük sabit noktası olarak tanımlanabilir ; izomorfizm daha sonra son bir kömür cürufu tarafından verilir .
Söylem alanı kümeler ve toplam işlevler kategorisiyse, son veri türleri sonsuz, iyi temellendirilmemiş değerler içerebilir , oysa başlangıç türleri içermez . Öte yandan, söylem alanı, kabaca Haskell programlama diline karşılık gelen tam kısmi düzenler ve sürekli fonksiyonlar kategorisiyse, son türler başlangıç türleriyle çakışır ve karşılık gelen son kömür cebiri ve ilk cebir bir izomorfizm oluşturur.
Corecursion daha sonra, aralığı (codomain) bir son veri türü olan ve alanı bir ilk veri türü olan fonksiyonları sıradan özyinelemeli olarak tanımlama biçimine iki kat olan işlevleri özyinelemeli olarak tanımlamak için bir tekniktir .
Aşağıdaki tartışma Haskell'de ıslahı ayırt eden birkaç örnek sunar. Kabaca konuşursak, eğer biri bu tanımları kümeler kategorisine taşıyacak olsaydı, yine de anlaşılır olurlar. Bu gayri resmi kullanım, Haskell hakkındaki mevcut ders kitaplarıyla tutarlıdır. Bu makalede kullanılan örnekler, ıslahı tanımlama ve ne olduğunu açıklama girişimlerinden öncedir.
Tartışma
Kural ilkel corecursion üzerinde CODATA için olana çift olan ilkel özyineleme verilerine. Yapıcıları üzerinde örüntü eşleştirerek argümana inmek yerine ( daha önce bir yerde çağrılmışlardı , böylece hazır bir veri alırız ve onu oluşturan alt bölümlerine, yani "alanlara" ulaşırız), sonuca yükseliriz onun "yıkıcılarını" (veya daha sonra bir yerde çağrılacak olan "gözlemcileri") doldurarak - yani aslında bir kurucu çağırıyoruz, daha sonra gözlemlenecek sonucun başka bir parçasını oluşturuyoruz). Böylece corecursion oluşturur (potansiyel olarak sonsuz) CODATA sıradan yineleme ise analizler (zorunlu olarak sonlu) verileri. Sıradan özyineleme, kod veriye uygulanamayabilir çünkü sona ermeyebilir. Tersine, sonuç türü veri ise düzeltme kesinlikle gerekli değildir, çünkü verilerin sonlu olması gerekir.
"Coq'ta akışlarla programlama: bir örnek olay: Eratosthenes Kalburu" nda
hd (conc a s) = a
tl (conc a s) = s
(sieve p s) = if div p (hd s) then sieve p (tl s)
else conc (hd s) (sieve p (tl s))
hd (primes s) = (hd s)
tl (primes s) = primes (sieve (hd s) (tl s))
burada "asalların akışa (Enu 2) uygulanarak" elde edilir. Yukarıdaki gösterimi takiben, asalların dizisi (ön ekli 0 ile birlikte) ve aşamalı olarak elenen sayı akışları şu şekilde temsil edilebilir:
veya Haskell'de,
(\(p, s@(h:t)) -> (h, sieve h t)) `iterate` (0, [2..])
Yazarlar, tanımının nasıl sieve her zaman üretken olmasının garanti edilmediğini ve örneğin [5,10..] ilk akış olarak çağrıldığında takılıp kalabileceğini tartışıyorlar .
İşte Haskell'den başka bir örnek. Aşağıdaki tanım, Fibonacci sayılarının listesini doğrusal zamanda üretir :
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
Bu sonsuz liste tembel değerlendirmeye bağlıdır; öğeler ihtiyaç duyulduğunda hesaplanır ve yalnızca sonlu önekler bellekte açıkça temsil edilir. Bu özellik, kod verilerinin bölümlerindeki algoritmaların sonlandırılmasına izin verir; bu tür teknikler Haskell programlamasının önemli bir parçasıdır.
Bu, Python'da da yapılabilir:
from itertools import tee, chain, islice, imap
def add(x, y):
return x + y
def fibonacci():
def deferred_output():
for i in output:
yield i
result, c1, c2 = tee(deferred_output(), 3)
paired = imap(add, c1, islice(c2, 1, None))
output = chain([0, 1], paired)
return result
for i in islice(fibonacci(), 20):
print(i)
Tanımı zipWith satır içine alınabilir ve buna yol açar:
fibs = 0 : 1 : next fibs
where
next (a: t@(b:_)) = (a+b):next t
Bu örnek, kendine referanslı bir veri yapısı kullanır . Sıradan özyineleme, kendine başvuran işlevlerden yararlanır , ancak kendi kendine başvuran verileri barındırmaz. Ancak, bu Fibonacci örneği için gerekli değildir. Aşağıdaki gibi yeniden yazılabilir:
fibs = fibgen (0,1)
fibgen (x,y) = x : fibgen (y,x+y)
Bu , sonucu oluşturmak için yalnızca kendine gönderme işlevini kullanır . Katı liste yapıcısı ile kullanılmış olsaydı, kontrolden çıkmış özyinelemeye bir örnek olurdu, ancak katı olmayan liste yapıcısıyla bu korumalı özyineleme, kademeli olarak belirsiz tanımlı bir liste üretir.
Corecursion'un sonsuz bir nesne üretmesine gerek yoktur; corecursive kuyruk, bu fenomenin özellikle iyi bir örneğidir. Aşağıdaki tanım , bir ikili ağacın doğrusal zamanda enine ilk geçişini üretir :
data Tree a b = Leaf a | Branch b (Tree a b) (Tree a b)
bftrav :: Tree a b -> [Tree a b]
bftrav tree = queue
where
queue = tree : gen 1 queue
gen 0 p = []
gen len (Leaf _ : s) = gen (len-1) s
gen len (Branch _ l r : s) = l : r : gen (len+1) s
Bu tanım bir ilk ağacı alır ve bir alt ağaç listesi oluşturur. Bu liste, hem kuyruk hem de sonuç olarak ikili amaca hizmet eder ( giriş arka işaretçisinden sonra , boyunca gen len p çıktı len çentiklerini üretir ). Ancak ve ancak ilk ağaç sonlu ise sonludur. Sonlandırmayı sağlamak için kuyruğun uzunluğu açıkça izlenmelidir; Bu tanım yalnızca sonsuz ağaçlara uygulanırsa, bu güvenli bir şekilde ortadan kaldırılabilir.
pqueue
Özellikle iyi bir başka örnek, genişlik ilk etiketleme sorununa bir çözüm sunar. İşlev label , bir ikili ağaçtaki her düğümü enine kadar ilk olarak ziyaret eder ve her etiketi bir tamsayı ile değiştirir; sonraki her tam sayı, bir sonrakinden daha büyüktür. Bu çözüm, kendine başvuran bir veri yapısı kullanır ve ikili ağaç sonlu veya sonsuz olabilir.
label :: Tree a b -> Tree Int Int
label t = t′
where
(t′, ns) = go t (1:ns)
go :: Tree a b -> [Int] -> (Tree Int Int, [Int])
go (Leaf _ ) (n:ns) = (Leaf n , n+1 : ns )
go (Branch _ l r) (n:ns) = (Branch n l′ r′ , n+1 : ns′′)
where
(l′, ns′ ) = go l ns
(r′, ns′′) = go r ns′
Bir apomorphism (örneğin, bir şekilde Anamorfizma gibi, katlanmış ), bir aynı şekilde corecursion şeklidir paramorphism (örneğin, bir şekilde catamorphism gibi kat ) yineleme şeklidir.
Coq kanıtı asistanı destekleri corecursion ve coinduction CoFixpoint komutunu kullanarak.
Tarih
Döngüsel programlama olarak anılan Corecursion, en azından John Hughes ve Philip Wadler'e kredi veren ( Bird 1984 ) ; ( Allison 1989 ) 'da daha genel formlar geliştirilmiştir . Orijinal motivasyonlar, daha verimli algoritmalar üretmeyi (bazı durumlarda birden fazla geçiş gerektirmek yerine 1 veri geçişine izin vermek) ve işlevsel dillerde çift bağlantılı listeler ve kuyruklar gibi klasik veri yapılarını uygulamayı içeriyordu.
Ayrıca bakınız
Notlar
Referanslar
- Kuş, Richard Simpson (1984). "Çoklu veri geçişlerini ortadan kaldırmak için dairesel programlar kullanma". Acta Informatica . 21 (3): 239–250. doi : 10.1007 / BF00264249 .
- Lloyd Allison (Nisan 1989). "Döngüsel Programlar ve Kendinden Referans Yapılar" . Yazılım Uygulaması ve Deneyimi . 19 (2): 99–109. doi : 10.1002 / spe.4380190202 .
- Geraint Jones ve Jeremy Gibbons (1992). Doğrusal zaman genişliği ilk ağaç algoritmaları: Kıvrımların ve fermuarların aritmetiğindeki bir alıştırma (Teknik rapor). Bilgisayar Bilimleri Bölümü, Auckland Üniversitesi.
- Jon Barwise ; Lawrence S Moss (Haziran 1996). Kısır Çevreler . Dil ve Bilgi Çalışmaları Merkezi. ISBN 978-1-57586-009-1 . 2010-06-21 tarihinde orjinalinden arşivlendi . Erişim tarihi: 2011-01-24 .
- Lawrence S Moss; Norman Danner (1997). "Corecursion'un Temelleri Üzerine". IGPL'nin Mantık Dergisi . 5 (2): 231–257. CiteSeerX 10.1.1.40.4243 . doi : 10.1093 / jigpal / 5.2.231 .
- Kees Doets; Jan van Eijck (Mayıs 2004). Mantık, Matematik ve Programlamaya Giden Haskell Yolu . King's College Yayınları. ISBN 978-0-9543006-9-2 .
- David Turner (2004-07-28). "Toplam Fonksiyonel Programlama" . Evrensel Bilgisayar Bilimleri Dergisi . 10 (7): 751–768. doi : 10.3217 / jucs-010-07-0751 .
- Jeremy Gibbons; Graham Hutton (Nisan 2005). "Düzeltmeli programlar için ispat yöntemleri" . Fundamenta Informaticae . 66 (4): 353–366.
- Leon P Smith (2009-07-29), "Lloyd Allison'ın Temel Ders Sıraları: Devamlar Neden Önemlidir" , Monad Okuyucu (14): 37-68
- Raymond Hettinger (2009-11-19). "Reçete 576961: Döngüsel yineleme tekniği" .
- MB Smyth ve GD Plotkin (1982). "Yinelemeli Alan Denklemlerinin Kategori-Teorik Çözümü" (PDF) . Bilgi İşlem Üzerine SIAM Dergisi . 11 (4): 761–783. doi : 10.1137 / 0211062 .
- Leclerc, Francois; Paulin-Mohring Christine (1993). Coq'ta Akışlarla Programlama: Bir Örnek Olay: Eratosthenes Elek . İspat ve Program Türleri: Uluslararası Çalıştay TİPLERİ '93. Springer-Verlag New York, Inc. s. 191–212. ISBN 978-3-540-58085-0 .