पुनरावृत्ति और corecursion के बीच अंतर क्या है?


55

इनमें क्या अंतर है?

विकिपीडिया पर, इन शर्तों को स्पष्ट करने वाली कोई जानकारी और कोई स्पष्ट कोड नहीं है।

इन शर्तों को समझाने वाले कुछ बहुत ही सरल उदाहरण क्या हैं?

पुनरावृत्ति की दोहरी व्याख्या कैसे होती है?

क्या कोई क्लासिक corecusive एल्गोरिदम हैं?


45
SO stackoverflow.com/questions/10138735/… (क्षमा करें, खुद को रोक नहीं सका) का उत्तर देखें
— उच्च प्रदर्शन मार्क

7
@HighPerformanceMark, यह स्पष्ट नहीं करता है कि वाहवाही क्या है, हमें एक और सवाल चाहिए
— Abyx

5
लेकिन गंभीरता से, इन शर्तों के विकिपीडिया स्पष्टीकरण के साथ क्या गलत है?
— उच्च प्रदर्शन मार्क

5
विकिपीडिया पर corecursion स्पष्टीकरण भयानक है। मुझे संदेह है कि यह किसी के लिए भी समझ में आता है जो पहले से नहीं जानता है कि युद्ध क्या है।
— मैरिन

9
@ उच्च प्रदर्शन मार्क: मैंने तीन बार लिंक पर क्लिक किया, यह सोचने से पहले कि मुझे सजा समझ में कोई गलती थी। LOL
— जियोर्जियो

जवाबों:


24

इसको देखने के कई अच्छे तरीके हैं। मेरे लिए सबसे आसान काम "इंडक्टिव" और "कोइंडिक्टिव परिभाषाओं" के बीच के संबंध के बारे में सोचना है।

एक सेट की एक आगमनात्मक परिभाषा इस तरह से होती है।

सेट "नेट" को सबसे छोटे सेट के रूप में परिभाषित किया गया है जैसे कि "शून्य" नेट में है, और यदि नेट में है "एनसीसी" सूट एन "।

जो निम्नलिखित Ocaml से मेल खाती है

type nat = Zero | Succ of nat

इस परिभाषा के बारे में एक बात ध्यान देने वाली है कि एक संख्या

omega = Succ(omega)

इस सेट का सदस्य नहीं है। क्यों? मान लें कि यह था, अब सेट एन पर विचार करें जिसमें सभी समान तत्व हैं जैसे कि नेट में ओमेगा नहीं है। स्पष्ट रूप से शून्य N में है, और यदि y N में है, तो Succ (y) N में है, लेकिन N Nat से छोटा है जो एक विरोधाभास है। तो, ओमेगा नेट में नहीं है।

या, शायद एक कंप्यूटर वैज्ञानिक के लिए अधिक उपयोगी:

कुछ सेट "ए" को देखते हुए, सेट "लिस्ट ऑफ़" को सबसे छोटे सेट के रूप में परिभाषित किया गया है जैसे कि "निल" ए की सूची में है, और यदि एक्स ए की सूची में है और एक्स "कॉन्स एक्स एक्स" में है की सूची में है।

जो कुछ इस तरह से मेल खाता है

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

यहां ऑपरेटिव शब्द "सबसे छोटा" है। अगर हमने "सबसे छोटा" नहीं कहा, तो हमारे पास यह बताने का कोई तरीका नहीं होगा कि क्या सेट नट में केला है!

फिर,

zeros = Cons(Zero,zeros)

nats की सूची के लिए एक वैध परिभाषा नहीं है, जैसे ओमेगा एक वैध नेट नहीं था।

इस तरह से डेटा को परिभाषित करना हमें उन कार्यों को परिभाषित करने की अनुमति देता है जो पुनरावृत्ति का उपयोग करके उस पर काम करते हैं

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

फिर हम इसके बारे में तथ्यों को साबित कर सकते हैं, जैसे "प्लस एक शून्य =" एक प्रेरण का उपयोग (विशेष रूप से, संरचनात्मक प्रेरण)

हमारा प्रमाण संरचनात्मक प्रेरण द्वारा a पर बढ़ता है।
आधार मामले के लिए शून्य होना चाहिए। plus Zero Zero = match Zero with |Zero -> Zero | Succ(c) -> let r = plus c b in Succ(r)तो हम जानते हैं plus Zero Zero = Zero। आज्ञा देना a। आगमनात्मक परिकल्पना मान लें कि plus a Zero = a। अब हम दिखाते हैं कि plus (Succ(a)) Zero = Succ(a)यह स्पष्ट है कि इस 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) प्रकार, नेट में plus a Zero = aसभी के लिए प्रेरण द्वाराa

हम बेशक अधिक दिलचस्प बातें साबित कर सकते हैं, लेकिन यह सामान्य विचार है।

अब तक हमने इंडक्टिवली डिफाइन किए गए डेटा को निपटाया है, जो हमें "सबसे छोटा" सेट देने से मिला। इसलिए अब हम सहिष्णुता से परिभाषित कोडता के साथ काम करना चाहते हैं जो हमें सबसे बड़ा सेट होने की अनुमति देता है।

इसलिए

एक सेट होने दो। सेट "स्ट्रीम ऑफ़" को सबसे बड़े सेट के रूप में परिभाषित किया गया है, जैसे कि x की धारा में प्रत्येक x के लिए x में आदेशित जोड़ी (सिर, पूंछ) होती है जैसे कि सिर एक में है और पूंछ स्ट्रीम में है

हास्केल में हम इसे व्यक्त करेंगे

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

दरअसल, हास्केल में हम निर्मित सूचियों का सामान्य रूप से उपयोग करते हैं, जो एक ऑर्डर की गई जोड़ी या एक खाली सूची हो सकती है।

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

केला इस प्रकार का सदस्य नहीं है, क्योंकि यह एक आदेशित जोड़ी या खाली सूची नहीं है। लेकिन, अब हम कह सकते हैं

ones = 1:ones

और यह पूरी तरह से मान्य परिभाषा है। Whats अधिक, हम इस सह-डेटा पर सह-पुनरावृत्ति कर सकते हैं। वास्तव में, एक फ़ंक्शन के लिए सह-पुनरावर्ती और पुनरावर्ती होना संभव है। जबकि रिकर्सन को फ़ंक्शन द्वारा डेटा से युक्त डोमेन द्वारा परिभाषित किया गया था , सह-पुनर्संयोजन का अर्थ है कि इसका सह-डोमेन है (सीमा भी कहा जाता है) जो सह-डेटा है। आदिम पुनरावृत्ति का अर्थ हमेशा छोटे डेटा पर "स्वयं को कॉल करना" होता है जब तक कि कुछ सबसे छोटे डेटा तक नहीं पहुंच जाते। आदिम सह-पुनरावृत्ति हमेशा "स्वयं को कॉल करता है" जो आपके पास पहले था उससे अधिक या उसके बराबर डेटा पर।

ones = 1:ones

मुख्य रूप से सह-पुनरावर्ती है। जबकि फ़ंक्शन map(अनिवार्य भाषाओं में "फॉरच" की तरह) आदिम रूप से पुनरावर्ती (प्रकार) और आदिम रूप से सह-पुनरावर्ती है।

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

वही फ़ंक्शन के लिए जाता है zipWithजो एक फ़ंक्शन और सूचियों की एक जोड़ी लेता है और उस फ़ंक्शन का उपयोग करके उन्हें एक साथ जोड़ता है।

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

कार्यात्मक भाषाओं का क्लासिक उदाहरण फाइबोनैचि अनुक्रम है

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

जो प्राथमिक रूप से पुनरावर्ती है, लेकिन अनंत सूची के रूप में अधिक सुरुचिपूर्ण ढंग से व्यक्त किया जा सकता है

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

इंडक्शन / कोइंडक्शन का एक दिलचस्प उदाहरण यह साबित कर रहा है कि ये दोनों परिभाषाएँ एक ही चीज़ की गणना करती हैं। इसे पाठक के लिए एक अभ्यास के रूप में छोड़ दिया जाता है।


1
@ user1131997 धन्यवाद। मैं जावा में कुछ कोड का अनुवाद करने की योजना बना रहा हूं, तो बने रहिए
— फिलिप जेएफ

@PhilipJF: मुझे बेवकूफ लगता है, लेकिन मैं यह नहीं देखता कि "... स्पष्ट रूप से शून्य N में है, और यदि y N में है, तो Succ (y) N में है ..."। क्या होगा यदि y किसी चीज को संतुष्ट कर रहा है Succ (y) = omega? (क्योंकि आप ज़ीरो और सक्सेस की किसी भी संपत्ति का उपयोग नहीं करते हैं, मैं Succ = रूट स्क्वायर और Zero = 2 की जगह ले सकता हूं)
— Ta Thanh Dinh

... और फिर मुझे ओमेगा = 1.
— ता थान दीन्ह

लक्ष्य यह दिखाना है कि ओमेगा नेट में नहीं है। हम विरोधाभास से ऐसा करते हैं। यदि ओमेगा सेट एन = नेट - {ओमेगा} की तुलना में नेट में था, तो कानूनों को संतुष्ट करेगा। ऐसा इसलिए है क्योंकि नेट कानूनों को संतुष्ट करता है। यदि n में y, 1. y ओमेगा नहीं है और 2. n में y। 2 से हम nat में Succ (y) को जानते हैं, और 1 y द्वारा ओमेगा नहीं है Succ (y) ओमेगा नहीं है। इस प्रकार N. N में Succ (y) में शून्य भी शामिल है। लेकिन, N, nat से छोटा है। यह एक विरोधाभास है। इस प्रकार, नट में ओमेगा शामिल नहीं है।
— फिलिप जेएफ

यह सब कुछ झूठ है क्योंकि ओसमल में मूल्य पुनरावर्तन है मुझे वास्तव में एसएमएल का उपयोग करना चाहिए था जो केवल "मुख्यधारा" -इश भाषा है जो आगमनात्मक तर्क का समर्थन करता है।
— फिलिप जेएफ

10

मूल रूप से, corecursion पुनरावृत्ति संचायक-शैली है , जिसका परिणाम शुरुआती मामले से आगे बढ़ने के रास्ते पर होता है, जबकि नियमित पुनरावर्तन आधार मामले से पीछे के रास्ते पर अपना परिणाम बनाता है।

(अब हास्केल बोल रहा हूं)। यही कारण है कि foldr(एक सख्त संयोजन समारोह के साथ) पुनरावृत्ति को व्यक्त करता है, और foldl'(सख्त कंघी के साथ। एफ) / scanl/ until/ iterate/ unfoldr/ आदि corecursion व्यक्त करते हैं। हर जगह भ्रष्टाचार है। foldrगैर-सख्त कंघी के साथ। च। पूंछ पुनरावृत्ति modulo विपक्ष व्यक्त करता है ।

और हास्केल के पहरा प्रत्यावर्तन बस है की तरह पूंछ प्रत्यावर्तन सापेक्ष विपक्ष ।

यह पुनरावृत्ति है:

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]

( $"के रूप में पढ़ें )"। यह सहसंबंध है:

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

सिलवटों: http://en.wikipedia.org/wiki/Fold_(higher-order_function)


4

Vitomir Kovanovic के ब्लॉग पर इसे देखें । मैंने इसे इस बिंदु पर पाया:

कार्यात्मक प्रोग्रामिंग क्षमताओं जैसे लिस्प, हैसेल, पायथन आदि के साथ प्रोग्रामिंग भाषाओं में पाए जाने वाले एक बहुत अच्छी सुविधा में आलसी मूल्यांकन। यह मान लेता है कि चर मान के मूल्यांकन में उस चर के वास्तविक उपयोग में देरी हो रही है।

इसका मतलब है कि उदाहरण के लिए यदि आप कुछ तत्वों के साथ लाख तत्वों की एक सूची बनाना चाहते हैं तो (defn x (range 1000000))यह वास्तव में निर्मित नहीं है, लेकिन यह केवल निर्दिष्ट है और जब आप पहली बार उस चर का उपयोग करते हैं, उदाहरण के लिए जब आप 10 वां तत्व चाहते हैं वह सूची दुभाषिया उस सूची के पहले 10 तत्वों को बनाता है। तो (10 एक्स ले) का पहला रन वास्तव में इन तत्वों को बनाता है और एक ही फ़ंक्शन के सभी बाद के कॉल पहले से मौजूद तत्वों के साथ काम कर रहे हैं।

यह बहुत उपयोगी है क्योंकि आप मेमोरी त्रुटियों के बिना अनंत सूची बना सकते हैं। यह सूची केवल आपके द्वारा अनुरोधित बड़ी होगी। बेशक, यदि आपका प्रोग्राम बड़े डेटा संग्रह के साथ काम कर रहा है तो यह इन अनंत सूचियों के उपयोग में मेमोरी सीमा को प्रभावित कर सकता है।

दूसरी ओर पुनरावृत्ति दोहराव है। इसका क्या मतलब है? अच्छी तरह से पुनरावर्ती कार्यों की तरह, जो स्वयं के संदर्भ में व्यक्त किए जाते हैं, स्वयं के संदर्भ में करकरे चर को व्यक्त किया जाता है।

यह सबसे अच्छा उदाहरण पर व्यक्त किया गया है।

मान लीजिए कि हम सभी अभाज्य संख्याओं की सूची चाहते हैं ...


1
ब्लॉग साइट मृत है।
— जेसन हू
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.