Özyineleme ve corecursion arasındaki fark nedir?


55

Bunlar arasındaki fark nedir?

Wikipedia'da çok az bilgi var ve bu terimleri açıklayan net bir kod yok.

Bu terimleri açıklayan çok basit örnekler nelerdir?

Düzeltme özyinelemenin ikilisi nasıldır?

Herhangi bir klasik corecusive algoritması var mı?


45
SO stackoverflow.com/questions/10138735/… kullanıcısının yanıtına bakın (Üzgünüz, kendimi durduramadım)
High Performance Mark

7
@HighPerformanceMark, düzeltmenin ne olduğunu açıklamıyor, başka bir soruya ihtiyacımız var
Abyx

5
Fakat cidden, bu terimlerin Wikipedia açıklamasında yanlış olan ne?
Yüksek Performans İşareti

5
Wikipedia'daki düzeltme açıklaması korkunç. Düzeltmenin ne olduğunu henüz bilmeyen bir kimseye mantıklı geldiğinden şüpheliyim.
Marcin

9
@High Performance Mark: Bağlantıyı anlamadan önce bir hata olduğunu düşünerek bağlantıya üç kez tıkladım. LOL
Giorgio

Yanıtlar:


24

Buna bakmanın birkaç iyi yolu var. Benim için en kolay olan şey “Endüktif” ve “Coinductive tanımları” arasındaki ilişkiyi düşünmektir.

Bir kümenin endüktif bir tanımı bu şekilde gider.

"Nat" kümesi, "Sıfır" Nat konumunda ve n ise Nat ise "Succ n" Nat konumunda ise en küçük set olarak tanımlanır.

Aşağıdaki Ocaml'a karşılık gelir

type nat = Zero | Succ of nat

Bu tanımla ilgili dikkat edilmesi gereken bir nokta, bir sayıdır.

omega = Succ(omega)

bu setin bir üyesi değil. Neden? Diyelim ki, şimdi omega içermeyen hariç, Nat ile aynı öğelere sahip olan N kümesini düşünün. Açıkçası, Sıfır N, ve y N ise, Succ (y) N'dir, ancak N, Nat olandan daha küçüktür ve bu bir çelişkidir. Öyleyse, omega Nat'ta değil.

Veya bir bilgisayar bilimcisi için belki de daha yararlı:

Bazı "a" kümelerine bakıldığında, "a" listesinin "nilüferler Listesi" ndeki en küçük küme olarak tanımlanır ve xs a ve x'in "Cons x xs" de yer alması durumunda listesinde.

Hangi gibi bir şey karşılık gelir

type 'a list = Nil | Cons of 'a * 'a list

Buradaki operatif kelime "en küçük". Eğer "en küçük" dememiş olsaydık, Nat setinin bir muz içerip içermediğini söylemenin bir yolu olmazdı!

Tekrar,

zeros = Cons(Zero,zeros)

bir nat listesi için geçerli bir tanım değildir, tıpkı omega'nın geçerli bir Nat olmadığı gibi.

Tanımlanması verileri bu kullanarak üzerinde çalışmaya işlevleri tanımlamak için bize izin verir gibi indüktif özyinelemeye

let rec plus a b = match a with
                   | Zero    -> b
                   | Succ(c) -> let r = plus c b in Succ(r)

daha sonra indüksiyon kullanarak (özellikle yapısal indüksiyon) "artı bir Sıfır = a" gibi gerçekleri ispatlayabiliriz.

Kanıtımız a.
Temel durum için Sıfır olalım. plus Zero Zero = match Zero with |Zero -> Zero | Succ(c) -> let r = plus c b in Succ(r)öyleyse biliyoruz plus Zero Zero = Zero. aBir nat olalım . İndüktif hipotezi varsayalım plus a Zero = a. Şimdi plus (Succ(a)) Zero = Succ(a)bunun açıkça belli olduğunu gösteriyoruz. plus (Succ(a)) Zero = match a with |Zero -> Zero | Succ(a) -> let r = plus a Zero in Succ(r) = let r = a in Succ(r) = Succ(a) Dolayısıyla, doğuştan plus a Zero = aherkes için indüksiyon yaparak a.

Elbette daha ilginç şeyler ispatlayabiliriz, ama genel fikir bu.

Şimdiye kadar “en küçük” set olmasını sağlayarak elde ettiğimiz endüktif olarak tanımlanmış verileri ele aldık. Şimdi en büyük küme olmasını sağlayarak elde ettiğimiz ve ortak olarak tanımlanmış kodalarla çalışmak istiyoruz .

Yani

Bir küme olalım. "A Akışı" kümesi, a akışındaki her x için, x, başın bir ve kuyruğunun Akışta olduğu şekilde sıralanan çiftten (kafa, kuyruk) oluşacağı şekilde en büyük küme olarak tanımlanır.

Haskell'de bunu şöyle ifade ederdik:

data Stream a = Stream a (Stream a) --"data" not "newtype"

Aslında, Haskell'de yerleşik listeleri normal bir şekilde kullanıyoruz; bu, düzenli bir çift veya boş bir liste olabilir.

data [a] = [] | a:[a]

Muz da bu tipte bir üye değildir, çünkü sıralı bir çift veya boş liste değildir. Ama şimdi söyleyebiliriz

ones = 1:ones

ve bu tamamen geçerli bir tanımdır. Dahası, bu co-data üzerinde ortak tekrarlar yapabiliriz. Aslında, bir işlevin hem özyinelemeli hem de özyinelemeli olması mümkündür. Özyineleme, verilerden oluşan bir alana sahip olan işlev tarafından tanımlanırken , eşzamanlılık yalnızca eş-veri olan bir ortak etki alanına (aralık olarak da adlandırılır) sahip olduğunu gösterir. İlkel özyineleme, en küçük bazı verilere ulaşana kadar daima daha küçük verilerde "kendini aramak" anlamına geliyordu . İlkel ortak yineleme, daha önce sahip olduklarınızdan büyük veya ona eşit olan veriler üzerinde her zaman "kendini" çağırır.

ones = 1:ones

ilkel olarak ortak özyinelemelidir. İşlev map(zorunlu dillerdeki "foreach" türüne benzer) hem ilkel olarak özyinelemeli (tür) hem de ilkel olarak özyinelemelidir.

map :: (a -> b) -> [a] -> [b]
map f []     = []
map f (x:xs) = (f x):map f xs

Aynısı zipWith, bir işlevi ve bir çift listeyi alan ve bu işlevi kullanarak bunları birleştiren işlev için de geçerlidir.

zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith f (a:as) (b:bs) = (f a b):zipWith f as bs
zipWith _ _ _           = [] --base case

Klasik fonksiyonel dil örneği Fibonacci dizisidir.

fib 0 = 0
fib 1 = 1
fib n = (fib (n-1)) + (fib (n-2))

ilkel olarak özyinelemeli olan, ancak sonsuz bir liste olarak daha zarif bir şekilde ifade edilebilir

fibs = 0:1:zipWith (+) fibs (tail fibs)
fib' n = fibs !! n --the !! is haskell syntax for index at

ilginç bir indüksiyon / coinduction örneği, bu iki tanımın aynı şeyi hesapladığını kanıtlamaktır. Bu, okuyucu için bir egzersiz olarak bırakılmıştır.


1
@ user1131997 Teşekkürler. Bazı kodları Java'ya çevirmeyi planlıyorum, bizi izlemeye devam edin
Philip JF

@PhilipJF: Kendimi aptal hissediyorum ama nedenini bilmiyorum "... Açıkça Sıfır N'de ve y N ise, Succ (y) N'de ...". Eğer y Succ (y) = omega'yı tatmin eden bir şeyse ne olur? (Sıfır ve Succ hiçbir özelliğini kullanmadığınız için, Succ = kök kare ve Sıfır = 2'yi değiştirebilirim)
Ta Thanh Dinh

... ve sonra omega = 1'i görüyorum.
Ta Thanh Dinh

amaç omega'nın natta olmadığını göstermektir. Bunu çelişki ile yapıyoruz. Eğer omega, N = nat ayarından daha doğalsa - {omega} yasaları yerine getirirdi. Çünkü Nat yasaları yerine getiriyor. N'de y ise 1. y omega değildir ve 2. y de nat. 2'den itibaren biz doğada Succ (y) 'yi biliyoruz ve 1 y' de omega değil. Succ (y) omega değil. Böylece N'deki Succ (y) ayrıca sıfır içerir. Ancak, N, nat'tan daha küçüktür. Bu bir çelişkidir. Böylece, nat omega içermez.
Philip JF

Ocaml değer özyinelemeye sahip olduğu için bu biraz yalan . Endüktif akıl yürütmeyi destekleyen tek "ana" dil olan SML'yi kullanmalıydım.
Philip JF

10

Temel olarak, düzeltme, sonucunu başlangıç ​​durumundan ileriye doğru oluştururken özyinelemeli akümülatör tarzıdır , oysa düzenli tekrarlanma sonucu temel durumdan geri dönüş yolunda oluşturur.

(şimdi Haskell konuşuyor). Bu nedenle foldr(katı bir birleştirme işleviyle) özyinelemeyi ve foldl'(katı bir taranan f. İle ) / scanl/ until/ iterate/ unfoldr/ / vb. Düzeltmeyi ifade eder. Düzeltme her yerdedir. foldrkatı olmayan tarak ile. f. kuyruk özyineleme modulo eksilerini ifade eder .

Ve Haskell'ın korunan özyineleme adildir gibi kuyruk özyineleme modülo eksileri .

Bu özyineleme:

fib n | n==0 = 0
      | n==1 = 1
      | n>1  = fib (n-1) + fib (n-2)

fib n = snd $ g n
  where
    g n | n==0 = (1,0)
        | n>0  = let { (b,a) = g (n-1) } in (b+a,b)

fib n = snd $ foldr (\_ (b,a) -> (b+a,b)) (1,0) [n,n-1..1]

( $"of" olarak okuyun ). Bu düzeltmedir:

fib n = g (0,1) 0 n where
  g n (a,b) i | i==n      = a 
              | otherwise = g n (b,a+b) (i+1)

fib n = fst.snd $ until ((==n).fst) (\(i,(a,b)) -> (i+1,(b,a+b))) (0,(0,1))
      = fst $ foldl (\(a,b) _ -> (b,a+b)) (0,1) [1..n]
      = fst $ last $ scanl (\(a,b) _ -> (b,a+b)) (0,1) [1..n]
      = fst (fibs!!n)  where  fibs = scanl (\(a,b) _ -> (b,a+b)) (0,1) [1..]
      = fst (fibs!!n)  where  fibs = iterate (\(a,b) -> (b,a+b)) (0,1)
      = (fibs!!n)  where  fibs = unfoldr (\(a,b) -> Just (a, (b,a+b))) (0,1)
      = (fibs!!n)  where  fibs = 0:1:map (\(a,b)->a+b) (zip fibs $ tail fibs)
      = (fibs!!n)  where  fibs = 0:1:zipWith (+) fibs (tail fibs)
      = (fibs!!n)  where  fibs = 0:scanl (+) 1 fibs
      = .....

Kıvrımlar: http://en.wikipedia.org/wiki/Fold_(higher-order_function)


4

Bunu Vitomir Kovanoviç'in blogunda kontrol edin . Bu noktaya buldum:

Programlama dillerinde lisp, haskell, python vb. Gibi işlevsel programlama yeteneklerine sahip tembel değerlendirme. Değişken değerinin değerlendirilmesinin, bu değişkenin fiili kullanımına geciktirildiği anlamına gelir.

Örneğin, (defn x (range 1000000))bunun gibi bir şeyle milyonlarca öğenin bir listesini oluşturmak istiyorsanız, aslında yaratılmadı, ancak yalnızca belirtildi ve ilk kez bu değişkeni gerçekten kullandığınızda, örneğin 10'uncu öğeyi istediğinizde Bu liste yorumlayıcısı, o listenin yalnızca ilk 10 öğesini oluşturur. Böylece, ilk çalıştırma (10 x al) aslında bu elemanları yaratır ve sonraki tüm çağrılar aynı işleve zaten mevcut elemanlarla çalışır.

Bu çok kullanışlıdır, çünkü hafıza hatası olmadan sonsuz listeler oluşturabilirsiniz. Liste ne kadar talep ettiğinizde büyük olacaktır. Tabii ki, eğer programınız büyük veri koleksiyonlarıyla çalışıyorsa, bu sonsuz listelerin kullanımında hafıza sınırına varabilir.

Öte yandan, düzeltme , yinelemenin iki katıdır . Bu ne anlama geliyor? Tıpkı özyinelemeli özyinelemeli işlevler gibi, özniteliksel değişkenler de özniteliklerle ifade edilir.

Bu en iyi örnekte ifade edilir.

Diyelim ki tüm asal sayıların listesini istiyoruz ...


1
blog sitesi öldü.
Jason Hu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.