Apa perbedaan antara rekursi dan korosi?

55

Apa perbedaannya?

Di Wikipedia, ada sedikit informasi dan tidak ada kode jelas yang menjelaskan istilah-istilah ini.

Apa saja contoh sederhana yang menjelaskan istilah-istilah ini?

Bagaimana korosi merupakan dual dari rekursi?

Apakah ada algoritma korususif klasik?

pengguna167908
sumber
45
Lihat jawaban untuk SO stackoverflow.com/questions/10138735/… (maaf, saya tidak bisa menahan diri)
High Performance Mark
7
@HighPerformanceMark, itu tidak menjelaskan apa itu korosi, kita perlu pertanyaan lain
Abyx
5
Namun serius, apa yang salah dengan penjelasan Wikipedia tentang istilah-istilah ini?
High Performance Mark
5
Penjelasan koreksi pada wikipedia sangat buruk. Saya ragu itu masuk akal bagi siapa saja yang belum tahu apa itu korosi.
Marcin
9
@ High Performance Mark: Saya mengklik tautan tiga kali berpikir ada kesalahan sebelum saya mengerti permainan kata. LOL
Giorgio

Jawaban:

24

Ada sejumlah cara bagus untuk melihat ini. Hal yang paling mudah bagi saya adalah berpikir tentang hubungan antara "Induktif" dan "definisi Coinductive"

Definisi induktif dari satu set seperti ini.

Set "Nat" didefinisikan sebagai set terkecil sehingga "Nol" berada di Nat, dan jika n ada di Nat "Succ n" ada di Nat.

Yang sesuai dengan Ocaml berikut

type nat = Zero | Succ of nat

Satu hal yang perlu diperhatikan tentang definisi ini adalah angka

omega = Succ(omega)

BUKAN anggota dari set ini. Mengapa? Asumsikan itu, sekarang pertimbangkan himpunan N yang memiliki semua elemen yang sama dengan Nat kecuali tidak memiliki omega. Jelas Zero ada di N, dan jika y ada di N, Succ (y) ada di N, tapi N lebih kecil dari Nat yang merupakan kontradiksi. Jadi, omega tidak ada di Nat.

Atau, mungkin lebih bermanfaat bagi ilmuwan komputer:

Diberikan beberapa himpunan "a", set "Daftar a" didefinisikan sebagai himpunan terkecil sehingga "Nil" ada dalam Daftar a, dan bahwa jika xs ada dalam Daftar a dan x adalah dalam "Cons x xs" ada dalam Daftar a.

Yang sesuai dengan sesuatu seperti

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

Kata yang digunakan di sini adalah "terkecil". Jika kita tidak mengatakan "terkecil" kita tidak akan memiliki cara untuk mengatakan jika set Nat mengandung pisang!

Lagi,

zeros = Cons(Zero,zeros)

bukan definisi yang valid untuk daftar nats, seperti omega bukan Nat yang valid.

Mendefinisikan data secara induktif seperti ini memungkinkan kita untuk mendefinisikan fungsi yang bekerja dengannya menggunakan rekursi

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

kita kemudian dapat membuktikan fakta tentang ini, seperti "plus a Nol = a" menggunakan induksi (khusus, induksi struktural)

Bukti kami hasil dengan induksi struktural pada a.
Untuk kasing dasar, biarkan nol. plus Zero Zero = match Zero with |Zero -> Zero | Succ(c) -> let r = plus c b in Succ(r)jadi kami tahu plus Zero Zero = Zero. Biarkan amenjadi nat. Asumsikan hipotesis induktif itu plus a Zero = a. Kami sekarang menunjukkan bahwa plus (Succ(a)) Zero = Succ(a)ini jelas sejak 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) demikian, dengan induksi plus a Zero = auntuk semua adalam nat

Tentu saja kita dapat membuktikan hal-hal yang lebih menarik, tetapi ini adalah gagasan umum.

Sejauh ini kami telah menangani data yang terdefinisi secara induktif yang kami dapatkan dengan membiarkannya menjadi set "terkecil". Jadi sekarang kita ingin bekerja dengan codata yang didefinisikan secara coinductivly yang kita dapatkan dengan membiarkannya menjadi set terbesar.

Begitu

Biarkan satu set. Himpunan "Aliran a" didefinisikan sebagai himpunan terbesar sehingga untuk setiap x dalam aliran a, x terdiri dari pasangan berurutan (kepala, ekor) sehingga kepala berada di dalam dan ekor berada dalam aliran dari

Dalam Haskell kita akan menyatakan ini sebagai

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

Sebenarnya, di Haskell kita menggunakan daftar bawaan secara normal, yang bisa berupa pasangan yang dipesan atau daftar kosong.

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

Pisang juga bukan anggota jenis ini, karena pisang bukan pasangan yang dipesan atau daftar kosong. Tapi, sekarang bisa kita katakan

ones = 1:ones

dan ini adalah definisi yang benar-benar valid. Terlebih lagi, kita dapat melakukan rekursi bersama pada data bersama ini. Sebenarnya, dimungkinkan untuk suatu fungsi bersifat ko-rekursif dan rekursif. Sementara rekursi didefinisikan oleh fungsi yang memiliki domain yang terdiri dari data, rekursi hanya berarti memiliki domain bersama (juga disebut rentang) yaitu data bersama. Rekursi primitif berarti selalu "memanggil diri sendiri" pada data yang lebih kecil hingga mencapai beberapa data terkecil. Rekursi bersama primitif selalu "menyebut dirinya" pada data yang lebih besar atau sama dengan apa yang Anda miliki sebelumnya.

ones = 1:ones

secara primitif bersifat rekursif. Sementara fungsi map(semacam "pendahuluan" dalam bahasa imperatif) keduanya bersifat rekursif primitif (semacam) dan rekursif secara primitif.

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

Hal yang sama berlaku untuk fungsi zipWithyang mengambil fungsi dan sepasang daftar dan menggabungkannya menggunakan fungsi itu.

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

contoh klasik dari bahasa fungsional adalah deret Fibonacci

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

yang secara primitif bersifat rekursif, tetapi dapat diekspresikan secara lebih elegan sebagai daftar tanpa batas

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

contoh menarik dari induksi / coinduction membuktikan bahwa kedua definisi ini menghitung hal yang sama. Ini dibiarkan sebagai latihan untuk pembaca.

Philip JF
sumber
1
@ user1131997 Terima kasih. Saya berencana menerjemahkan beberapa kode ke Jawa, tetap disini
Philip JF
@ PilipJF: Saya merasa bodoh tapi saya tidak mengerti mengapa "... Jelas Zero ada di N, dan jika y ada di N, Succ (y) ada di N ...". Apa yang terjadi jika y adalah sesuatu yang memuaskan Succ (y) = omega? (karena Anda tidak menggunakan properti apa pun dari Zero dan Succ, saya dapat mengganti Succ = root kuadrat dan Zero = 2)
Ta Thanh Dinh
... dan kemudian saya melihat omega = 1.
Ta Thanh Dinh
tujuannya adalah untuk menunjukkan omega tidak dalam nat. Kami melakukan ini dengan kontradiksi. Jika omega berada di nat dari himpunan N = nat - {omega} akan memenuhi hukum. Itu karena nat memenuhi hukum. Jika y dalam N, 1. y bukan omega dan 2. y dalam nat. Dari 2 kita tahu Succ (y) dalam nat, dan oleh 1 y bukan omega Succ (y) bukan omega. Jadi Succ (y) dalam N. N juga termasuk nol. Tapi, N lebih kecil dari nat. Ini adalah kontradiksi. Jadi, nat tidak termasuk omega.
Philip JF
Ini semua sedikit bohong karena ocaml memiliki rekursi nilai, saya benar-benar harus menggunakan SML yang merupakan satu-satunya bahasa "mainstream" yang mendukung penalaran induktif.
Philip JF
10

Pada dasarnya, korosi adalah gaya akumulator rekursi , membangun hasilnya pada jalan maju dari kasing awal, sedangkan rekursi reguler membangun hasilnya pada jalan kembali dari kasing dasar.

(Berbicara Haskell sekarang). Itu sebabnya foldr(dengan fungsi penggabungan yang ketat) mengekspresikan rekursi, dan foldl'(dengan sisir yang ketat. F) / scanl/ until/ iterate/ unfoldr/ dll mengekspresikan korosi. Koreksi ada di mana-mana. foldrdengan sisir yang tidak ketat. f. mengekspresikan kontra modulo rekursi ekor .

Dan rekursi yang dijaga Haskell sama seperti modulo rekursi ekor rekursi .

Ini adalah rekursi:

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]

(baca $"dari"). Ini adalah koreksi:

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
      = .....

Lipatan: http://en.wikipedia.org/wiki/Fold_(higher-order_function)

Will Ness
sumber
4

Lihat ini di blog Vitomir Kovanovic . Saya menemukannya langsung:

Evaluasi malas dalam satu fitur yang sangat bagus ditemukan dalam bahasa pemrograman dengan kemampuan pemrograman fungsional seperti lisp, haskell, python, dll. Ini berarti bahwa evaluasi nilai variabel tertunda untuk penggunaan aktual dari variabel itu.

Ini berarti bahwa misalnya jika Anda ingin membuat daftar juta elemen dengan sesuatu seperti ini, (defn x (range 1000000))itu sebenarnya tidak dibuat, tetapi hanya ditentukan dan ketika Anda benar-benar menggunakan variabel itu untuk pertama kalinya, misalnya ketika Anda ingin elemen ke-10 dari penerjemah daftar itu hanya membuat 10 elemen pertama dari daftar itu. Jadi jalankan pertama (ambil 10 x) sebenarnya menciptakan elemen-elemen ini dan semua panggilan berikutnya ke fungsi yang sama bekerja dengan elemen yang sudah ada.

Ini sangat berguna karena Anda dapat membuat daftar yang tak terbatas tanpa kesalahan memori. Daftarnya akan menjadi besar seberapa banyak yang Anda minta. Tentu saja, jika program Anda bekerja dengan koleksi data besar itu dapat mencapai batas memori dalam penggunaan daftar tak terbatas ini.

Di sisi lain, korosi adalah dua kali lipat dari rekursi. Apa ini artinya? Seperti halnya fungsi rekursif, yang diekspresikan dalam istilah mereka sendiri, variabel korektif diekspresikan dalam istilah mereka sendiri.

Ini paling baik diungkapkan pada contoh.

Katakanlah kita ingin daftar semua bilangan prima ...

Priyadarshi Kunal
sumber
1
situs blog sudah mati.
Jason Hu