तह का उपयोग करके तह लिखना


79

में असली दुनिया हास्केल , पर अध्याय 4. कार्यात्मक प्रोग्रामिंग :

तह के साथ तह लिखें:

-- file: ch04/Fold.hs
myFoldl :: (a -> b -> a) -> a -> [b] -> a

myFoldl f z xs = foldr step id xs z
    where step x g a = g (f a x)

उपरोक्त कोड ने मुझे बहुत भ्रमित किया, और किसी ने डीपीएस कहा, इसे थोड़ा स्पष्ट करने के लिए इसे सार्थक नाम के साथ फिर से लिखा:

myFoldl stepL zeroL xs = (foldr stepR id xs) zeroL
where stepR lastL accR accInitL = accR (stepL accInitL lastL)

किसी और ने, जेफ जी ने, फिर एक उदाहरण प्रदान करके एक उत्कृष्ट कार्य किया और चरणबद्ध तरीके से अंतर्निहित तंत्र को दिखाया:

myFoldl (+) 0 [1, 2, 3]
= (foldR step id [1, 2, 3]) 0
= (step 1 (step 2 (step 3 id))) 0
= (step 1 (step 2 (\a3 -> id ((+) a3 3)))) 0
= (step 1 (\a2 -> (\a3 -> id ((+) a3 3)) ((+) a2 2))) 0
= (\a1 -> (\a2 -> (\a3 -> id ((+) a3 3)) ((+) a2 2)) ((+) a1 1)) 0
= (\a1 -> (\a2 -> (\a3 -> (+) a3 3) ((+) a2 2)) ((+) a1 1)) 0
= (\a1 -> (\a2 -> (+) ((+) a2 2) 3) ((+) a1 1)) 0
= (\a1 -> (+) ((+) ((+) a1 1) 2) 3) 0
= (+) ((+) ((+) 0 1) 2) 3
= ((0 + 1) + 2) + 3

लेकिन मैं अभी भी पूरी तरह से समझ नहीं पा रहा हूँ, यहाँ मेरे सवाल हैं:

  1. आईडी फंक्शन किस लिए होता है की भूमिका क्या है? हमें यहां इसकी आवश्यकता क्यों होनी चाहिए?
  2. उपरोक्त उदाहरण में, आईडी फ़ंक्शन लंबा फ़ंक्शन में संचायक है?
  3. तह का प्रोटोटाइप है foldr :: (a -> b -> b) -> b -> [a] -> b, और पहला पैरामीटर एक फ़ंक्शन है जिसमें दो मापदंडों की आवश्यकता होती है, लेकिन myFoldl के कार्यान्वयन में चरण फ़ंक्शन 3 मापदंडों का उपयोग करता है, मैं पूरी तरह से भ्रमित हूं!

2
असली मसोकिस्टिक के लिए,step = curry $ uncurry (&) <<< (flip f) *** (.)
वीजुन झोउ

जवाबों:


99

कुछ स्पष्टीकरण क्रम में हैं!

आईडी फंक्शन किस लिए होता है की भूमिका क्या है? हमें यहां इसकी आवश्यकता क्यों होनी चाहिए?

idहै पहचान समारोह , id x = xहै, और जब साथ कार्यों की एक श्रृंखला का निर्माण शून्य के बराबर के रूप में प्रयोग किया जाता है समारोह रचना , (.)। आप इसे प्रस्तावना में परिभाषित कर सकते हैं ।

उपरोक्त उदाहरण में, आईडी फ़ंक्शन लंबा फ़ंक्शन में संचायक है?

संचायक एक फ़ंक्शन है जिसे बार-बार फ़ंक्शन एप्लिकेशन के माध्यम से बनाया जा रहा है। कोई स्पष्ट लैम्ब्डा,, नहीं है क्योंकि हम संचायक नाम step। आप चाहें तो इसे लंबोदर के साथ लिख सकते हैं:

foldl f a bs = foldr (\b g x -> g (f x b)) id bs a

या ग्राहम हटन के रूप में लिखेंगे :

5.1 foldlऑपरेटर

अब हम sumlउदाहरण से सामान्यीकरण करते हैं और मानक ऑपरेटर पर विचार करते हैं foldlजो fमानों को संयोजित करने के लिए एक फ़ंक्शन का उपयोग करके सूची के तत्वों को बाएं से दाएं क्रम में संसाधित करता है , और vशुरुआती मूल्य के रूप में एक मूल्य:

foldl :: (β → α → β) → β → ([α] → β)
foldl f v [ ] = v
foldl f v (x : xs) = foldl f (f v x) xs

इस ऑपरेटर का उपयोग करके, sumlबस द्वारा इसे फिर से परिभाषित किया जा सकता है suml = foldl (+) 0। कई अन्य कार्यों को सरल तरीके से उपयोग करके परिभाषित किया जा सकता है foldl। उदाहरण के लिए, मानक फ़ंक्शन को निम्नानुसार reverseउपयोग करके पुनर्परिभाषित किया जा सकता foldlहै:

reverse :: [α] → [α]
reverse = foldl (λxs x → x : xs) [ ]

यह परिभाषा तह का उपयोग करके हमारी मूल परिभाषा से अधिक कुशल है, क्योंकि यह (++)सूचियों के लिए अक्षम अपेंडेंट ऑपरेटर के उपयोग से बचती है ।

फ़ंक्शन के लिए पिछले अनुभाग में गणना का एक सरल सामान्यीकरण sumlदिखाता है कि फ़ंक्शन को कैसे फिर से परिभाषित करना foldlहै fold:

foldl f v xs = fold (λx g → (λa → g (f a x))) id xs v

इसके विपरीत, यह फिर से परिभाषित करना संभव नहीं है foldके मामले में foldlतथ्य यह है कि के कारण, foldlइसकी सूची तर्क की पूंछ में सख्त है, लेकिन foldनहीं है। उपयोगी 'द्वंद्व प्रमेयों' की एक संख्या के विषय में कर रहे हैं foldऔर foldl, और भी निर्णय लेने से जो ऑपरेटर सबसे अच्छा विशेष अनुप्रयोगों (बर्ड, 1998) के लिए उपयुक्त है के लिए कुछ दिशा निर्देश।

तह का प्रोटोटाइप तह है :: (a -> - b -> b) -> b -> [a] -> b

एक Haskell प्रोग्रामर का कहना है कि प्रकार की foldrहै (a -> b -> b) -> b -> [a] -> b

और पहला पैरामीटर एक फ़ंक्शन है जिसमें दो मापदंडों की आवश्यकता होती है, लेकिन myFoldl के कार्यान्वयन में चरण फ़ंक्शन 3 मापदंडों का उपयोग करता है, मैं पूरी तरह से भ्रमित हूं

यह भ्रामक और जादुई है! हम एक चाल खेलते हैं और एक फ़ंक्शन के साथ संचायक को बदलते हैं, जिसके परिणामस्वरूप परिणाम प्राप्त करने के लिए प्रारंभिक मूल्य पर लागू किया जाता है।

ग्राहम हटन चाल चालू करने के लिए बताते हैं foldlमें foldrउपरोक्त लेख में। हम नीचे एक पुनरावर्ती परिभाषा लिखकर शुरू करते हैं foldl:

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

और फिर इस पर स्थैतिक तर्क परिवर्तन के माध्यम से इसे रिफलेक्टर करें f:

foldl :: (a -> b -> a) -> a -> [b] -> a    
foldl f v xs = g xs v
    where
        g []     v = v
        g (x:xs) v = g xs (f v x)

चलिए अब फिर से लिखते हैं gताकि vअंदर की ओर तैरें:

foldl f v xs = g xs v
    where
        g []     = \v -> v
        g (x:xs) = \v -> g xs (f v x)

जो gएक तर्क के एक समारोह के रूप में सोचने के समान है, जो एक फ़ंक्शन देता है:

foldl f v xs = g xs v
    where
        g []     = id
        g (x:xs) = \v -> g xs (f v x)

अब हमारे पास gएक फ़ंक्शन है , जो किसी सूची को पुन: बनाता है, कुछ फ़ंक्शन लागू करता है f। अंतिम मान पहचान फ़ंक्शन है, और प्रत्येक चरण किसी फ़ंक्शन में भी परिणाम करता है।

लेकिन , हमारे पास सूचियों पर पहले से ही एक समान पुनरावर्ती कार्य है foldr!

2 गुना ऑपरेटर

foldऑपरेटर, जबकि का उपयोग करते हैं, प्रत्यावर्तन सिद्धांत (क्लीन, 1952) में अपनी मूल है foldबैकस एक प्रोग्रामिंग भाषा दिनांकों में एक केंद्रीय अवधारणा के रूप में एपीएल की कमी ऑपरेटर (इवर्सन, 1962) के लिए वापस, और बाद में एफपी की प्रविष्टि ऑपरेटर के लिए ( , 1978)। हास्केल में, foldसूचियों के ऑपरेटर को निम्नानुसार परिभाषित किया जा सकता है:

fold :: (α → β → β) → β → ([α] → β)
fold f v [ ] = v
fold f v (x : xs) = f x (fold f v xs)

यही कारण है, एक समारोह में दिया जाता है fप्रकार के α → β → βऔर एक मूल्य के vप्रकार के β, समारोह fold f vप्रकार की एक सूची को संसाधित करता है [α]प्रकार का एक मूल्य देने के लिए βनहीं के बराबर निर्माता की जगह []मूल्य द्वारा सूची के अंत में v, और प्रत्येक विपक्ष निर्माता (:)द्वारा सूची के भीतर समारोह f। इस तरीके से, foldऑपरेटर प्रसंस्करण सूचियों के लिए पुनरावृत्ति का एक सरल पैटर्न संलग्न करता है, जिसमें सूचियों के लिए दो निर्माणकर्ता बस अन्य मूल्यों और कार्यों द्वारा प्रतिस्थापित किए जाते हैं। सूचियों पर कई परिचित कार्यों का उपयोग करने की एक सरल परिभाषा है fold

यह हमारे gफ़ंक्शन के लिए एक बहुत ही समान पुनरावर्ती योजना जैसा दिखता है । अब चाल: हाथ में सभी उपलब्ध जादू (उर्फ बर्ड, मेर्टेंस और मैल्कम) का उपयोग करते हुए हम एक विशेष नियम लागू करते हैं, गुना की सार्वभौमिक संपत्ति , जो एक फ़ंक्शन के लिए दो डी for nitions के बीच एक तुल्यता है जो gप्रक्रियाओं को सूचीबद्ध करता है, जैसा कि कहा गया है।

g [] = v
g (x:xs) = f x (g xs)

यदि और केवल यदि

g = fold f v

इसलिए, सिलवटों की सार्वभौमिक संपत्ति बताती है कि:

    g = foldr k v

जहां gदो समीकरणों के बराबर होना चाहिए कुछ के लिए kऔर v:

    g []     = v
    g (x:xs) = k x (g xs)

हमारे पहले के डिजाइनों से, हम जानते हैं v == id। दूसरे समीकरण के लिए हालांकि, हमें इसकी परिभाषा की गणना करने की आवश्यकता है k:

    g (x:xs)         = k x (g xs)        
<=> g (x:xs) v       = k x (g xs) v      -- accumulator of functions
<=> g xs (f v x)     = k x (g xs) v      -- definition of foldl
<=  g' (f v x)       = k x g' v          -- generalize (g xs) to g'
<=> k = \x g' -> (\a -> g' (f v x))      -- expand k. recursion captured in g'

जो, हमारी गणना की गई परिभाषाओं को प्रतिस्थापित करता है kऔर vनिम्न की परिभाषा देता है:

foldl :: (a -> b -> a) -> a -> [b] -> a    
foldl f v xs =
    foldr
        (\x g -> (\a -> g (f v x)))
        id
        xs
        v

पुनरावर्ती gको फोल्डर कॉम्बीनेटर के साथ बदल दिया जाता है, और संचायक fरिवर्स फ़ंक्शन में सूची के प्रत्येक तत्व पर रचनाओं की एक श्रृंखला के माध्यम से निर्मित एक फ़ंक्शन बन जाता है (इसलिए हम दाएं के बजाय बाएं मुड़ते हैं)।

यह निश्चित रूप से कुछ हद तक उन्नत है, इसलिए इस परिवर्तन को गहराई से समझने के लिए, सिलवटों की सार्वभौमिक संपत्ति , जो परिवर्तन को संभव बनाती है, मैं नीचे से जुड़े हटन के ट्यूटोरियल की सलाह देता हूं।


संदर्भ


1
Plz में टाइपो ठीक k = \x g' -> (\a -> g' (f v x)) और(\x g -> (\a -> g (f v x)))
कामेल

10

इसके प्रकार पर विचार करें foldr:

foldr :: (b -> a -> a) -> a -> [b] -> a

जबकि प्रकार stepकुछ ऐसा है b -> (a -> a) -> a -> a। चूंकि चरण को पारित किया जा रहा है foldr, हम यह निष्कर्ष निकाल सकते हैं कि इस मामले में गुना का एक प्रकार है (b -> (a -> a) -> (a -> a)) -> (a -> a) -> [b] -> (a -> a)

aविभिन्न हस्ताक्षरों के विभिन्न अर्थों में भ्रमित न हों ; यह सिर्फ एक प्रकार का चर है। इसके अलावा, ध्यान रखें कि फंक्शन एरो सही एसोसिएटिव है, इसलिए a -> b -> cजैसा है वैसा ही है a -> (b -> c)

तो, हाँ, संचायक मान प्रकार foldrका एक फ़ंक्शन है a -> a, और प्रारंभिक मान है id। यह कुछ समझ में आता है, क्योंकि idएक ऐसा फ़ंक्शन है जो कुछ भी नहीं करता है - यह वही कारण है जो आप सूची में सभी मूल्यों को जोड़ते समय प्रारंभिक मूल्य के रूप में शून्य के साथ शुरू करेंगे।

stepतीन तर्क लेने के लिए , इसे इस तरह से लिखने का प्रयास करें:

step :: b -> (a -> a) -> (a -> a)
step x g = \a -> g (f a x)

क्या यह देखना आसान है कि क्या हो रहा है? यह एक अतिरिक्त पैरामीटर लेता है क्योंकि यह एक फ़ंक्शन लौटा रहा है, और इसे लिखने के दो तरीके समान हैं। नोट foldr: के बाद भी अतिरिक्त पैरामीटर (foldr step id xs) z। कोष्ठक में भाग अपने आप में गुना है, जो एक फ़ंक्शन देता है, जिसे बाद में लागू किया जाता है z


6

(जल्दी से मेरे उत्तरों के माध्यम से स्किम करें [१] , [२] , [३] , [४] यह सुनिश्चित करने के लिए कि आप हास्केल के वाक्यविन्यास, उच्च-क्रम के कार्यों, करीने, कार्य रचना, $ ऑपरेटर, infix / उपसर्ग ऑपरेटरों, अनुभागों और लैम्ब्डा को समझते हैं। )

गुना की सार्वभौमिक संपत्ति

एक गुना कुछ प्रकार की पुनरावृत्ति का एक कोडीकरण है। और सार्वभौमिकता संपत्ति केवल यह बताती है कि, यदि आपकी पुनरावृत्ति एक निश्चित रूप के अनुरूप है, तो इसे कुछ औपचारिक नियमों के अनुसार गुना में तब्दील किया जा सकता है। और इसके विपरीत, हर तह को उस तरह की पुनरावृत्ति में बदला जा सकता है। एक बार फिर, कुछ पुनरावृत्तियों को उन सिलवटों में अनुवादित किया जा सकता है जो बिल्कुल एक ही उत्तर देते हैं, और कुछ पुनरावृत्ति नहीं कर सकते हैं, और ऐसा करने के लिए एक सटीक प्रक्रिया है।

मूल रूप से, यदि आपका पुनरावर्ती कार्य बाईं ओर की तरह सूचियों पर काम करता है , तो आप इसे एक सही , प्रतिस्थापित करने fऔर vवास्तव में क्या है , के लिए बदल सकते हैं।

g []     = v              ⇒
g (x:xs) = f x (g xs)     ⇒     g = foldr f v

उदाहरण के लिए:

sum []     = 0   {- recursion becomes fold -}
sum (x:xs) = x + sum xs   ⇒     sum = foldr 0 (+)

यहाँ v = 0और sum (x:xs) = x + sum xsके बराबर है sum (x:xs) = (+) x (sum xs), इसलिए f = (+)। 2 और उदाहरण

product []     = 1
product (x:xs) = x * product xs  ⇒  product = foldr 1 (*)

length []     = 0
length (x:xs) = 1 + length xs    ⇒  length = foldr (\_ a -> 1 + a) 0

व्यायाम:

  1. लागू map, filter, reverse, concatऔर concatMapरिकर्सिवली, बस पर ऊपर कार्यों की तरह छोड़ दिया ओर।

  2. ऊपर दिए गए एक सूत्र के अनुसार इन 5 कार्यों को गुना में बदलें, अर्थात् , प्रतिस्थापन fऔर दाईं ओरv गुना सूत्र में ।

तह के माध्यम से मोड़ो

एक पुनरावर्ती फ़ंक्शन कैसे लिखें जो बाएं से दाएं तक संख्याओं को लिखते हैं?

sum [] = 0     -- given `sum [1,2,3]` expands into `(1 + (2 + 3))`
sum (x:xs) = x + sum xs

पहले पुनरावर्ती फ़ंक्शन जो पूरी तरह से खोजने के लिए आता है, वह जोड़ना शुरू करने से पहले पूरी तरह से फैलता है, यही वह नहीं है जो हमें चाहिए। एक दृष्टिकोण एक पुनरावर्ती फ़ंक्शन बनाने के लिए है जिसमें संचायक होता है , जो तुरंत प्रत्येक चरण पर संख्याओं को जोड़ता है ( पुनरावृत्ति रणनीतियों के बारे में अधिक जानने के लिए पूंछ पुनरावृत्ति के बारे में पढ़ें ):

suml :: [a] -> a
suml xs = suml' xs 0
  where suml' [] n = n   -- auxiliary function
        suml' (x:xs) n = suml' xs (n+x)

ठीक है, रुक जाओ! इस कोड को जीएचसीआई में चलाएं और सुनिश्चित करें कि आप समझते हैं कि यह कैसे काम करता है, फिर सावधानीपूर्वक और सावधानीपूर्वक आगे बढ़ें। sumlएक गुना के साथ पुनर्परिभाषित नहीं किया suml'जा सकता है , लेकिन हो सकता है।

suml' []       = v    -- equivalent: v n = n
suml' (x:xs) n = f x (suml' xs) n

suml' [] n = nफ़ंक्शन परिभाषा से, है ना? और v = suml' []सार्वभौमिक संपत्ति सूत्र से। यह एक साथ देता है v n = n, एक ऐसा फ़ंक्शन जो तुरंत प्राप्त करता है जो इसे प्राप्त करता है v = id:। आइए गणना करते हैं f:

suml' (x:xs) n = f x (suml' xs) n
-- expand suml' definition
suml' xs (n+x) = f x (suml' xs) n
-- replace `suml' xs` with `g`
g (n+x)        = f x g n

इस प्रकार, suml' = foldr (\x g n -> g (n+x)) idऔर इस प्रकार, suml = foldr (\x g n -> g (n+x)) id xs 0

foldr (\x g n -> g (n + x)) id [1..10] 0 -- return 55

अब हमें बस सामान्य करने की जरूरत है, +एक चर फ़ंक्शन द्वारा प्रतिस्थापित करें :

foldl f a xs = foldr (\x g n -> g (n `f` x)) id xs a
foldl (-) 10 [1..5] -- returns -5

निष्कर्ष

अब गुना की सार्वभौमिकता और स्पष्टता पर ग्राहम हटन के ए ट्यूटोरियल को पढ़ें । कुछ पेन और पेपर प्राप्त करें, वह सब कुछ जानने की कोशिश करें जो वह तब तक लिखता है जब तक कि आप अपने द्वारा अधिकांश तह प्राप्त न कर लें। अगर आपको कुछ समझ में नहीं आता है, तो आप पसीना न बहाएँ, आप हमेशा बाद में लौट सकते हैं, लेकिन बहुत अधिक विलंब न करें


मुझे यह उत्तर सरल और स्पष्ट है जो स्वीकृत है। बहुत बुरा यह वोट अप है ...
गीगाबाइट

5

यहाँ मेरा सबूत है कि के foldlरूप में व्यक्त किया जा सकता है foldr, जो मुझे लगता है कि इस stepसमारोह का परिचय स्पेगेटी नाम के अलावा बहुत सरल लगता है ।

प्रस्ताव यह है कि foldl f z xsइसके बराबर है

myfoldl f z xs = foldr step_f id xs z
        where step_f x g a = g (f a x)

यहां ध्यान देने वाली पहली महत्वपूर्ण बात यह है कि पहली पंक्ति के दाहिने हाथ की ओर वास्तव में मूल्यांकन किया गया है

(foldr step_f id xs) z

चूंकि foldrकेवल तीन पैरामीटर हैं। यह पहले से ही संकेत देता है कि foldrवसीयत एक मूल्य नहीं बल्कि एक करी फ़ंक्शन की गणना करेगा, जिसे तब लागू किया जाता है z। यह पता लगाने के लिए दो मामले हैं कि क्या myfoldlहै foldl:

  1. आधार मामला: खाली सूची

      myfoldl f z []
    = foldr step_f id [] z    (by definition of myfoldl)
    = id z                    (by definition of foldr)
    = z
    
      foldl f z []
    = z                       (by definition of foldl)
    
  2. गैर-रिक्त सूची

      myfoldl f z (x:xs)
    = foldr step_f id (x:xs) z          (by definition of myfoldl)
    = step_f x (foldr step_f id xs) z   (-> apply step_f)
    = (foldr step_f id xs) (f z x)      (-> remove parentheses)
    = foldr step_f id xs (f z x)
    = myfoldl f (f z x) xs              (definition of myfoldl)
    
      foldl f z (x:xs)
    = foldl f (f z x) xs
    

चूंकि 2. पहली और अंतिम पंक्ति में दोनों मामलों में समान रूप है, इसका उपयोग सूची को नीचे तक मोड़ने के लिए किया जा सकता है xs == [], जिस स्थिति में 1. समान परिणाम की गारंटी देता है। प्रेरण द्वारा तो, myfoldl == foldl


2

गणित के लिए कोई रॉयल रोड नहीं है, न ही हास्केल के माध्यम से। लश्कर

h z = (foldr step id xs) z where   
     step x g =  \a -> g (f a x)

बिल्ली क्या है h z? मान लिया कि xs = [x0, x1, x2]
तह की परिभाषा लागू करें:

h z = (step x0 (step x1 (step x2 id))) z 

चरण की परिभाषा लागू करें:

= (\a0 -> (\a1 -> (\a2 -> id (f a2 x2)) (f a1 x1)) (f a0 x0)) z

लैम्ब्डा कार्यों में स्थान:

= (\a1 -> (\a2 -> id (f a2 x2)) (f a1 x1)) (f z x0)

= (\a2 -> id (f a2 x2)) (f (f z x0) x1)

= id (f (f (f z x0) x1) x2)

आईडी की परिभाषा लागू करें:

= f (f (f z x0) x1) x2

तह की परिभाषा लागू करें:

= foldl f z [x0, x1, x2]

यह एक रॉयल रोड है या क्या?


2

डाउनवोट करने से पहले, कृपया निम्नलिखित पैराग्राफ को पढ़ें

मैं उन लोगों के लिए उत्तर पोस्ट कर रहा हूं जो इस दृष्टिकोण को अपने सोचने के तरीके के अनुकूल समझ सकते हैं। उत्तर में संभवतः अनावश्यक जानकारी और विचार शामिल हैं, लेकिन समस्या से निपटने के लिए मुझे इसकी आवश्यकता है। इसके अलावा, चूँकि यह एक ही प्रश्न का एक और उत्तर है, इसलिए यह स्पष्ट है कि इसके अन्य उत्तरों के साथ पर्याप्त ओवरलैप्स हैं, हालांकि यह इस कहानी को बताता है कि मैं इस अवधारणा को कैसे समझ सकता हूं।

वास्तव में मैंने इस विषय को समझने की कोशिश करते हुए इस नोट्स को अपने विचारों के व्यक्तिगत रिकॉर्ड के रूप में लिखना शुरू किया। अगर मुझे वास्तव में मिल गया है तो मुझे इसके मूल को छूने में पूरा दिन लग गया।

इस सरल अभ्यास को समझने का मेरा लंबा तरीका है

आसान हिस्सा: हमें क्या निर्धारित करने की आवश्यकता है?

निम्नलिखित उदाहरण कॉल के साथ क्या होता है

foldl f z [1,2,3,4]

निम्नलिखित आरेख के साथ कल्पना की जा सकती है (जो विकिपीडिया पर है , लेकिन मैंने पहली बार इसे दूसरे उत्तर पर देखा ):

          _____results in a number
         /
        f          f (f (f (f z 1) 2) 3) 4
       / \
      f   4        f (f (f z 1) 2) 3
     / \
    f   3          f (f z 1) 2
   / \
  f   2            f z 1
 / \
z   1

(एक साइड नोट के रूप में, जब foldlप्रत्येक एप्लिकेशन का उपयोग fनहीं किया जाता है, और अभिव्यक्तियों को जिस तरह से मैंने उन्हें ऊपर लिखा है, उसी तरह से फेंक दिया जाता है; सिद्धांत रूप में, उन्हें गणना की जा सकती है क्योंकि आप नीचे-ऊपर जाते हैं, और यही वास्तव में foldl'होता है।)

व्यायाम अनिवार्य रूप से चरण फ़ंक्शन को बदलने के foldrबजाय हमें उपयोग करने के लिए चुनौती देता है foldl(इसलिए हम sइसके बजाय उपयोग करते हैं f) और प्रारंभिक संचायक (इसलिए हम ?इसके बजाय उपयोग करते हैं z); सूची वही रहती है, अन्यथा हम किस बारे में बात कर रहे हैं?

इस foldrतरह दिखना होगा कॉल :

foldr s ? [1,2,3,4]

और इसी आरेख है:

    _____what does the last call return?
   /
  s
 / \
1   s
   / \
  2   s
     / \
    3   s
       / \
      4   ? <--- what is the initial accumulator?

में कॉल का परिणाम है

s 1 (s 2 (s 3 (s 4 ?)))

क्या हैं sऔर ?? और उनके प्रकार क्या हैं? ऐसा लगता है कि sयह एक दो तर्क समारोह है, बहुत पसंद है f, लेकिन चलो निष्कर्ष पर नहीं कूदते हैं। इसके अलावा, चलिए ?एक पल के लिए छोड़ देते हैं, और चलिए देखते हैं कि zखेल में आते ही 1खेलना पड़ता है; हालाँकि, zकॉल में प्ले-टू-संभव sफ़ंक्शन, अर्थात् कॉल में कैसे आ सकता है s 1 (…)? हम एनिग्मा के इस भाग को हल कर सकते हैं, sजिसमें 3 तर्क लेते हैं, 2 के बजाय जो हमने पहले उल्लेख किया है, ताकि सबसे बाहरी कॉल s 1 (…)में एक तर्क लेने वाले फ़ंक्शन का परिणाम होगा, जिसे हम पास कर सकते हैं z!

इसका मतलब है कि हम मूल कॉल चाहते हैं, जिसका विस्तार होता है

f (f (f (f z 1) 2) 3) 4

के बराबर होना

s 1 (s 2 (s 3 (s 4 ?))) z

या, दूसरे शब्दों में, हम आंशिक रूप से लागू फ़ंक्शन चाहते हैं

s 1 (s 2 (s 3 (s 4 ?)))

निम्नलिखित लैम्ब्डा फ़ंक्शन के बराबर होना

(\z -> f (f (f (f z 1) 2) 3) 4)

फिर से, "केवल" टुकड़े हमें चाहिए sऔर ?

टर्निंग पॉइंट: फंक्शन कंपोजीशन को पहचानें

चलो पिछले आरेख को फिर से लिखें और दाईं ओर लिखें जो हम चाहते हैं कि प्रत्येक कॉल इसके sसमकक्ष हो:

  s          s 1 (…) == (\z -> f (f (f (f z 1) 2) 3) 4)
 / \
1   s        s 2 (…) == (\z -> f (f (f    z    2) 3) 4)
   / \
  2   s      s 3 (…) == (\z -> f (f       z       3) 4)
     / \
    3   s    s 4  ?  == (\z -> f          z          4)
       / \
      4   ? <--- what is the initial accumulator?

मुझे आशा है कि यह आरेख की संरचना से स्पष्ट है कि (…)प्रत्येक पंक्ति में इसके नीचे की रेखा का दाहिना हाथ है; बेहतर है, यह पिछले (नीचे) कॉल से लौटाया गया फ़ंक्शन है s

यह भी स्पष्ट होना चाहिए कि sतर्कों के साथ एक कॉल xऔर एकमात्र तर्क के आंशिक आवेदन के लिए y(पूर्ण) आवेदन है । चूंकि का आंशिक आवेदन करने के लिए लैम्ब्डा के रूप में लिखा जा सकता है , पूरी तरह से लागू करने के लिए यह लैम्ब्डा में परिणाम है , जो इस मामले में मैं के रूप में फिर से लिखने होगा ; हम प्राप्त करने के लिए एक अभिव्यक्ति में शब्दों का अनुवादyfxfx(\z -> f z x)y(\z -> y (f z x))y . (\z -> f z x)s

s x y = y . (\z -> f z x)

(यह वही है s x y z = y (f z x), जो पुस्तक के समान है, यदि आप चर नाम बदलते हैं।)

अंतिम बिट है: संचायक का प्रारंभिक "मान" क्या है ?? उपरोक्त आरेख को रचना श्रृंखला बनाने के लिए नेस्टेड कॉल का विस्तार करके फिर से लिखा जा सकता है:

  s          s 1 (…) == (\z -> f z 4) . (\z -> f z 3) . (\z -> f z 2) . (\z -> f z 1)
 / \
1   s        s 2 (…) == (\z -> f z 4) . (\z -> f z 3) . (\z -> f z 2)
   / \
  2   s      s 3 (…) == (\z -> f z 4) . (\z -> f z 3)
     / \
    3   s    s 4  ?  == (\z -> f z 4)
       / \
      4   ? <--- what is the initial accumulator?

हम यहाँ देखते है कि sबस के लगातार आंशिक अनुप्रयोगों "ऊपर बवासीर" f, लेकिन yमें s x y = y . (\z -> f z x)पता चलता है कि की व्याख्या s 4 ?(और, बारी में, अन्य सभी) एक प्रमुख समारोह याद करते हैं वाम-पंथी लैम्ब्डा के साथ बना है।

यह सिर्फ हमारा ?कार्य है: यह कॉल करने के लिए एक जगह पर कब्जा करने के साथ, इसके अस्तित्व का कारण देने का समय है foldr। परिणामी कार्यों को नहीं बदलने के लिए हम इसे क्या चुन सकते हैं? उत्तर: रचना संचालक के संबंध idमें पहचान तत्व(.)

  s          s 1 (…) == id . (\z -> f z 4) . (\z -> f z 3) . (\z -> f z 2) . (\z -> f z 1)
 / \
1   s        s 2 (…) == id . (\z -> f z 4) . (\z -> f z 3) . (\z -> f z 2)
   / \
  2   s      s 3 (…) == id . (\z -> f z 4) . (\z -> f z 3)
     / \
    3   s    s 4 id  == id . (\z -> f z 4)
       / \
      4   id

तो मांगी गई फंक्शन है

myFoldl f z xs = foldr (\x g a -> g (f a x)) id xs z

1

यह मदद कर सकता है, मैंने एक अलग तरीके से विस्तार करने की कोशिश की।

myFoldl (+) 0 [1,2,3] = 
foldr step id [1,2,3] 0 = 
foldr step (\a -> id (a+3)) [1,2] 0 = 
foldr step (\b -> (\a -> id (a+3)) (b+2)) [1] 0 = 
foldr step (\b -> id ((b+2)+3)) [1] 0 = 
foldr step (\c -> (\b -> id ((b+2)+3)) (c+1)) [] 0 = 
foldr step (\c -> id (((c+1)+2)+3)) [] 0 = 
(\c -> id (((c+1)+2)+3)) 0 = ...

1
foldr step zero (x:xs) = step x (foldr step zero xs)
foldr _ zero []        = zero

myFold f z xs = foldr step id xs z
  where step x g a = g (f a x)

myFold (+) 0 [1, 2, 3] =
  foldr step id [1, 2, 3] 0
  -- Expanding foldr function
  step 1 (foldr step id [2, 3]) 0
  step 1 (step 2 (foldr step id [3])) 0
  step 1 (step 2 (step 3 (foldr step id []))) 0
  -- Expanding step function if it is possible
  step 1 (step 2 (step 3 id)) 0
  step 2 (step 3 id) (0 + 1)
  step 3 id ((0 + 1) + 2)
  id (((0 + 1) + 2) + 3)

खैर, कम से कम, इससे मुझे मदद मिली। यहां तक ​​कि यह काफी सही नहीं है।


वास्तविक अनुक्रम है foldr step id [1, 2, 3] 0-> step 1 (foldr step id [2, 3]) 0-> (foldr step id [2, 3]) (0 + 1)-> step 2 (foldr step id [3]) (0 + 1)-> (foldr step id [3]) ((0 + 1) + 2)-> step 3 (foldr step id []) ((0 + 1) + 2)-> (foldr step id []) (((0 + 1) + 2) + 3)-> id (((0 + 1) + 2) + 3)
नेस

0

यह उत्तर नीचे दी गई परिभाषा को तीन चरणों में आसानी से समझा जाता है।

-- file: ch04/Fold.hs
myFoldl :: (a -> b -> a) -> a -> [b] -> a

myFoldl f z xs = foldr step id xs z
    where step x g a = g (f a x)

चरण 1. फ़ंक्शन संयोजन के लिए फ़ंक्शन मूल्यांकन की तह को बदलना

foldl f z [x1 .. xn] = z & f1 & .. & fn = fn . .. . f1 z। जिसमें fi = \z -> f z xi

( z & f1 & f2 & .. & fnइसका उपयोग करके इसका मतलब है fn ( .. (f2 (f1 z)) .. )।)

चरण 2. फ़ंक्शन संयोजन को एक foldrतरीके से व्यक्त करें

foldr (.) id [f1 .. fn] = (.) f1 (foldr (.) id [f2 .. fn]) = f1 . (foldr (.) id [f2 .. fn])। बाकी पाने के लिए अनफोल्ड करें foldr (.) id [f1 .. fn] = f1 . .. . fn

यह देखते हुए कि अनुक्रम उलटा है, हमें इसका उलटा रूप प्रयोग करना चाहिए (.)rc f1 f2 = (.) f2 f1 = f2 . f1फिर परिभाषित करें foldr rc id [f1 .. fn] = rc f1 (foldr (.) id [f2 .. fn]) = (foldr (.) id [f2 .. fn]) . f1। बाकी पाने के लिए अनफोल्ड करें foldr rc id [f1 .. fn] = fn . .. . f1

स्टेप 3. फंक्शन लिस्ट पर फोल्ड को ऑपरेंड लिस्ट पर फोल्ड में बदलना

stepवह बनाता है खोजें foldr step id [x1 .. xn] = foldr rc id [f1 .. fn]। इसे खोजना आसान है step = \x g z -> g (f z x)

3 चरणों में, foldlउपयोग करने की परिभाषा foldrस्पष्ट है:

  foldl f z xs
= fn . .. . f1 z
= foldr rc id fs z
= foldr step id xs z

शुद्धता साबित करें:

foldl f z xs = foldr (\x g z -> g (f z x)) id xs z
             = step x1 (foldr step id [x2 .. xn]) z
             = s1 (foldr step id [x2 .. xn]) z
             = s1 (step x2 (foldr step id [x3 .. xn])) z
             = s1 (s2 (foldr step id [x3 .. xn])) z
             = ..
             = s1 (s2 (.. (sn (foldr step id [])) .. )) z
             = s1 (s2 (.. (sn id) .. )) z
             = (s2 (.. (sn id) .. )) (f z x1)
             = s2 (s3 (.. (sn id) .. )) (f z x1)
             = (s3 (.. (sn id) .. )) (f (f z x1) x2)
             = ..
             = sn id (f (.. (f (f z x1) x2) .. ) xn-1)
             = id (f (.. (f (f z x1) x2) .. ) xn)
             = f (.. (f (f z x1) x2) .. ) xn

in which xs = [x1 .. xn], si = step xi = \g z -> g (f z xi)

यदि आपको कुछ भी अस्पष्ट लगता है, तो कृपया एक टिप्पणी जोड़ें। :)

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