कैसे fold
भिन्नता भ्रम का लगातार स्रोत प्रतीत होती है, इसलिए यहां एक अधिक सामान्य अवलोकन है:
[x1, x2, x3, x4 ... xn ]
कुछ फ़ंक्शन f
और बीज के साथ n मानों की एक सूची तह पर विचार करें z
।
foldl
है:
- बाएं सहयोगी :
f ( ... (f (f (f (f z x1) x2) x3) x4) ...) xn
- पूंछ पुनरावर्ती : यह सूची के माध्यम से पुनरावृत्त करता है, बाद में मूल्य का उत्पादन करता है
- आलसी : परिणाम की आवश्यकता होने तक कुछ भी मूल्यांकन नहीं किया जाता है
- पीछे की ओर :
foldl (flip (:)) []
एक सूची को उलट देता है।
foldr
है:
- सही सहयोगी :
f x1 (f x2 (f x3 (f x4 ... (f xn z) ... )))
- एक तर्क में पुनरावर्ती : प्रत्येक पुनरावृत्ति
f
अगले मूल्य और बाकी सूची को तह करने के परिणामस्वरूप लागू होता है।
- आलसी : परिणाम की आवश्यकता होने तक कुछ भी मूल्यांकन नहीं किया जाता है
- आगे :
foldr (:) []
एक सूची अपरिवर्तित देता है।
यहां थोड़ा सूक्ष्म बिंदु है जो कभी-कभी लोगों को यात्रा करता है: क्योंकि पीछेfoldl
की तरफ प्रत्येक एप्लिकेशन को परिणाम f
के बाहर जोड़ा जाता है ; और क्योंकि यह आलसी है , परिणाम की आवश्यकता होने तक कुछ भी मूल्यांकन नहीं किया जाता है। इसका मतलब यह है कि परिणाम के किसी भी हिस्से की गणना करने के लिए, हास्केल नेस्टेड फ़ंक्शन एप्लिकेशन की अभिव्यक्ति का निर्माण करते हुए पूरी सूची के माध्यम से पहले पुनरावृत्ति करता है, फिर सबसे बाहरी फ़ंक्शन का मूल्यांकन करता है, आवश्यकतानुसार उसके तर्कों का मूल्यांकन करता है। यदि f
हमेशा अपने पहले तर्क का उपयोग किया जाता है, तो इसका मतलब है कि हास्केल को सभी तरह के अंतरतम पद पर वापस जाना होगा, फिर प्रत्येक एप्लिकेशन की गणना करते हुए पीछे की ओर काम करना होगा f
।
यह स्पष्ट रूप से कुशल पूंछ-पुनरावृत्ति से बहुत दूर रोना है जो अधिकांश कार्यात्मक प्रोग्रामर जानते हैं और प्यार करते हैं!
वास्तव में, भले ही foldl
तकनीकी रूप से पूंछ-पुनरावर्ती हो, क्योंकि कुछ भी मूल्यांकन करने से पहले संपूर्ण परिणाम अभिव्यक्ति का निर्माण किया जाता है, foldl
जिससे स्टैक ओवरफ्लो हो सकता है!
दूसरी ओर, विचार करें foldr
। यह आलसी भी है, लेकिन क्योंकि यह आगे की ओर चलता है , के प्रत्येक अनुप्रयोग को परिणाम f
के अंदर जोड़ा जाता है । इसलिए, परिणाम की गणना करने के लिए, हास्केल एक एकल फ़ंक्शन एप्लिकेशन का निर्माण करता है, जिसमें से दूसरा तर्क बाकी तह सूची है। यदि f
इसके दूसरे तर्क में आलसी है - एक डेटा कंस्ट्रक्टर, उदाहरण के लिए - परिणाम वृद्धिशील रूप से आलसी होगा , जिसके प्रत्येक चरण को केवल गणना की जाती है, जब परिणाम के कुछ हिस्से की आवश्यकता होती है जिसका मूल्यांकन किया जाता है।
इसलिए हम देख सकते हैं कि foldr
कभी-कभी अनंत सूचियों पर काम क्यों foldl
नहीं किया जाता है: पूर्व में आलसी अनंत सूची को एक और आलसी अनंत डेटा संरचना में बदल सकता है, जबकि उत्तरार्द्ध को परिणाम के किसी भी हिस्से को उत्पन्न करने के लिए पूरी सूची का निरीक्षण करना चाहिए। दूसरी ओर, foldr
एक ऐसे फ़ंक्शन के साथ , जिसे दोनों तर्कों की तुरंत आवश्यकता होती है, जैसे (+)
, काम करता है (या बल्कि, काम नहीं करता है) बहुत पसंद है foldl
, इसका मूल्यांकन करने से पहले एक विशाल अभिव्यक्ति का निर्माण करना।
तो नोट करने के लिए दो महत्वपूर्ण बिंदु ये हैं:
foldr
एक आलसी पुनरावर्ती डेटा संरचना को दूसरे में बदल सकता है।
- अन्यथा, आलसी सिलवटों बड़े या अनंत सूचियों पर एक ढेर अतिप्रवाह के साथ दुर्घटनाग्रस्त हो जाएगा।
आपने देखा होगा कि ऐसा लगता है कि foldr
सब कुछ foldl
कर सकता है, और अधिक। यह सच है! वास्तव में, गुना लगभग बेकार है!
लेकिन क्या होगा अगर हम एक बड़ी (लेकिन अनंत नहीं) सूची को मोड़कर एक गैर-आलसी परिणाम उत्पन्न करना चाहते हैं? इसके लिए, हम एक सख्त तह चाहते हैं , जो मानक पुस्तकालयों को यथोचित प्रदान करते हैं :
foldl'
है:
- बाएं सहयोगी :
f ( ... (f (f (f (f z x1) x2) x3) x4) ...) xn
- पूंछ पुनरावर्ती : यह सूची के माध्यम से पुनरावृत्त करता है, बाद में मूल्य का उत्पादन करता है
- सख्त : प्रत्येक फ़ंक्शन अनुप्रयोग का मूल्यांकन रास्ते में किया जाता है
- पीछे की ओर :
foldl' (flip (:)) []
एक सूची को उलट देता है।
क्योंकि foldl'
है सख्त , गणना करने के लिए परिणाम हास्केल होगा मूल्यांकन f
के बजाय छोड़ दिया तर्क दे के प्रत्येक चरण पर, एक विशाल, unevaluated अभिव्यक्ति जमा। यह हमें हमेशा की तरह, कुशल पूंछ पुनरावृत्ति देता है जो हम चाहते हैं! दूसरे शब्दों में:
foldl'
बड़ी सूची को कुशलता से मोड़ सकते हैं।
foldl'
अनंत सूची में एक अनन्त लूप (एक ढेर अतिप्रवाह का कारण नहीं) में लटकाएगा।
हास्केल विकी का एक पृष्ठ है , जो इस पर चर्चा करता है।
foldr
बेहतर हैfoldl
में हास्केल , जबकि विपरीत में सच है Erlang (जो मैंने पहले सीखा हास्केल )। चूंकि Erlang आलसी नहीं है और कार्य नहीं कर रहे हैं curried , इसलिएfoldl
में Erlang की तरह बर्ताव करता हैfoldl'
ऊपर। यह एक महान जवाब है! अच्छी नौकरी और धन्यवाद!