इनमें क्या अंतर है?
विकिपीडिया पर, इन शर्तों को स्पष्ट करने वाली कोई जानकारी और कोई स्पष्ट कोड नहीं है।
इन शर्तों को समझाने वाले कुछ बहुत ही सरल उदाहरण क्या हैं?
पुनरावृत्ति की दोहरी व्याख्या कैसे होती है?
क्या कोई क्लासिक corecusive एल्गोरिदम हैं?
इनमें क्या अंतर है?
विकिपीडिया पर, इन शर्तों को स्पष्ट करने वाली कोई जानकारी और कोई स्पष्ट कोड नहीं है।
इन शर्तों को समझाने वाले कुछ बहुत ही सरल उदाहरण क्या हैं?
पुनरावृत्ति की दोहरी व्याख्या कैसे होती है?
क्या कोई क्लासिक corecusive एल्गोरिदम हैं?
जवाबों:
इसको देखने के कई अच्छे तरीके हैं। मेरे लिए सबसे आसान काम "इंडक्टिव" और "कोइंडिक्टिव परिभाषाओं" के बीच के संबंध के बारे में सोचना है।
एक सेट की एक आगमनात्मक परिभाषा इस तरह से होती है।
सेट "नेट" को सबसे छोटे सेट के रूप में परिभाषित किया गया है जैसे कि "शून्य" नेट में है, और यदि नेट में है "एनसीसी" सूट एन "।
जो निम्नलिखित 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
इंडक्शन / कोइंडक्शन का एक दिलचस्प उदाहरण यह साबित कर रहा है कि ये दोनों परिभाषाएँ एक ही चीज़ की गणना करती हैं। इसे पाठक के लिए एक अभ्यास के रूप में छोड़ दिया जाता है।
मूल रूप से, 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)
Vitomir Kovanovic के ब्लॉग पर इसे देखें । मैंने इसे इस बिंदु पर पाया:
कार्यात्मक प्रोग्रामिंग क्षमताओं जैसे लिस्प, हैसेल, पायथन आदि के साथ प्रोग्रामिंग भाषाओं में पाए जाने वाले एक बहुत अच्छी सुविधा में आलसी मूल्यांकन। यह मान लेता है कि चर मान के मूल्यांकन में उस चर के वास्तविक उपयोग में देरी हो रही है।
इसका मतलब है कि उदाहरण के लिए यदि आप कुछ तत्वों के साथ लाख तत्वों की एक सूची बनाना चाहते हैं तो
(defn x (range 1000000))यह वास्तव में निर्मित नहीं है, लेकिन यह केवल निर्दिष्ट है और जब आप पहली बार उस चर का उपयोग करते हैं, उदाहरण के लिए जब आप 10 वां तत्व चाहते हैं वह सूची दुभाषिया उस सूची के पहले 10 तत्वों को बनाता है। तो (10 एक्स ले) का पहला रन वास्तव में इन तत्वों को बनाता है और एक ही फ़ंक्शन के सभी बाद के कॉल पहले से मौजूद तत्वों के साथ काम कर रहे हैं।यह बहुत उपयोगी है क्योंकि आप मेमोरी त्रुटियों के बिना अनंत सूची बना सकते हैं। यह सूची केवल आपके द्वारा अनुरोधित बड़ी होगी। बेशक, यदि आपका प्रोग्राम बड़े डेटा संग्रह के साथ काम कर रहा है तो यह इन अनंत सूचियों के उपयोग में मेमोरी सीमा को प्रभावित कर सकता है।
दूसरी ओर पुनरावृत्ति दोहराव है। इसका क्या मतलब है? अच्छी तरह से पुनरावर्ती कार्यों की तरह, जो स्वयं के संदर्भ में व्यक्त किए जाते हैं, स्वयं के संदर्भ में करकरे चर को व्यक्त किया जाता है।
यह सबसे अच्छा उदाहरण पर व्यक्त किया गया है।
मान लीजिए कि हम सभी अभाज्य संख्याओं की सूची चाहते हैं ...