haskell - आलसी मूल्यांकन क्यों उपयोगी है?




functional-programming lazy-evaluation (15)

मैं लंबे समय से सोच रहा हूं कि आलसी मूल्यांकन क्यों उपयोगी है। मैंने अभी तक मुझे किसी ऐसे तरीके से समझाया है जो समझ में आता है; ज्यादातर यह "मुझ पर भरोसा" करने के लिए उबलते हुए समाप्त होता है।

नोट: मेरा मतलब ज्ञापन नहीं है।


  1. यह दक्षता को बढ़ावा दे सकता है। यह स्पष्ट दिखने वाला है, लेकिन यह वास्तव में सबसे महत्वपूर्ण नहीं है। (ध्यान दें कि आलस्य भी दक्षता को मार सकती है - यह तथ्य तुरंत स्पष्ट नहीं है। हालांकि, तुरंत उन्हें गणना करने के बजाय बहुत से अस्थायी परिणाम संग्रहीत करके, आप बड़ी मात्रा में रैम का उपयोग कर सकते हैं।)

  2. यह आपको सामान्य उपयोगकर्ता-स्तरीय कोड में प्रवाह नियंत्रण संरचनाओं को परिभाषित करने की बजाय, भाषा में हार्ड-कोड किए जाने की अनुमति देता है। (उदाहरण के लिए, जावा के पास लूप के for है; हास्केल के पास फ़ंक्शन के for है। जावा में अपवाद हैंडलिंग है; हास्केल में विभिन्न प्रकार के अपवाद मोनैड हैं। सी # goto ; हास्केल की निरंतरता है ...)

  3. यह आपको यह निर्धारित करने के लिए एल्गोरिदम से डेटा उत्पन्न करने के लिए एल्गोरिदम को कम करने देता है कि कितना डेटा उत्पन्न करना है। आप एक ऐसा फ़ंक्शन लिख सकते हैं जो परिणामों की एक वास्तविक रूप से अनंत सूची उत्पन्न करता है, और दूसरा फ़ंक्शन जो इस सूची में से अधिकतर प्रक्रियाओं को संसाधित करता है, क्योंकि यह इसकी आवश्यकता है। इस बिंदु पर, आपके पास पांच जनरेटर फ़ंक्शंस और पांच उपभोक्ता फ़ंक्शंस हो सकते हैं, और आप कुशलता से किसी भी संयोजन का उत्पादन कर सकते हैं - मैन्युअल रूप से 5 x 5 = 25 फ़ंक्शन कोडिंग करने के बजाय जो दोनों कार्यों को एक साथ जोड़ते हैं। (!) हम सभी जानते हैं कि decoupling एक अच्छी बात है।

  4. यह एक शुद्ध कार्यात्मक भाषा डिजाइन करने के लिए आपको कम या ज्यादा बल देता है। यह हमेशा कम कटौती करने के लिए मोहक है, लेकिन आलसी भाषा में, थोड़ी सी अशुद्धता आपके कोड को बेहद अप्रत्याशित बनाती है, जो शॉर्टकट लेने के खिलाफ दृढ़ता से जूझती है।


अधिकतर क्योंकि यह अधिक कुशल हो सकता है - यदि मूल्यों का उपयोग नहीं किया जा रहा है तो मूल्यों की गणना करने की आवश्यकता नहीं है। उदाहरण के लिए, मैं तीन मानों को फ़ंक्शन में पास कर सकता हूं, लेकिन सशर्त अभिव्यक्ति के अनुक्रम के आधार पर, केवल एक सबसेट का उपयोग किया जा सकता है। सी जैसी भाषा में, सभी तीन मानों की गणना की जाएगी; लेकिन हास्केल में, केवल आवश्यक मानों की गणना की जाती है।

यह अनंत सूचियों जैसे शांत सामानों की भी अनुमति देता है। मेरे पास सी जैसी भाषा में अनंत सूची नहीं हो सकती है, लेकिन हास्केल में, यह कोई समस्या नहीं है। अनंत सूचियों का गणित के कुछ क्षेत्रों में काफी बार उपयोग किया जाता है, इसलिए यह उपयोगी हो सकता है कि उन्हें कुशल बनाने की क्षमता हो।


अन्य लोगों ने पहले से ही सभी बड़े कारण दिए हैं, लेकिन मुझे लगता है कि एक सख्त भाषा में fixed-point फ़ंक्शन को आजमाने और लिखने के लिए आलस्य का महत्व क्यों है, यह समझने में मदद करने के लिए एक उपयोगी अभ्यास है।

हास्केल में, एक निश्चित-बिंदु फ़ंक्शन सुपर आसान है:

fix f = f (fix f)

यह फैलता है

f (f (f ....

लेकिन क्योंकि हास्केल आलसी है, गणना की अनंत श्रृंखला कोई समस्या नहीं है; मूल्यांकन "बाहर से अंदर" किया जाता है, और सब कुछ अद्भुत काम करता है:

fact = fix $ \f n -> if n == 0 then 1 else n * f (n-1)

महत्वपूर्ण बात यह है कि यह fix नहीं है कि fix आलसी हो, लेकिन वह आलसी हो। एक बार जब आप पहले से ही सख्त f , तो आप या तो हवा में अपने हाथ फेंक सकते हैं और छोड़ सकते हैं, या ईटा इसे विस्तारित कर सकते हैं और अव्यवस्थित सामग्री को बढ़ा सकते हैं। (यह ऐसा कुछ है जो नूह उस पुस्तकालय के बारे में कह रहा था जो सख्त / आलसी है, न कि भाषा)।

अब सख्त स्कैला में एक ही समारोह लिखने की कल्पना करें:

def fix[A](f: A => A): A = f(fix(f))

val fact = fix[Int=>Int] { f => n =>
    if (n == 0) 1
    else n*f(n-1)
}

आप निश्चित रूप से एक ढेर अतिप्रवाह मिलता है। यदि आप इसे काम करना चाहते हैं, तो आपको f तर्क को कॉल-बाय-आवश्यकता बनाने की आवश्यकता है:

def fix[A](f: (=>A) => A): A = f(fix(f))

def fact1(f: =>Int=>Int) = (n: Int) =>
    if (n == 0) 1
    else n*f(n-1)

val fact = fix(fact1)

आलसी मूल्यांकन का सबसे उपयोगी शोषण जो मैंने उपयोग किया है वह एक ऐसा कार्य था जिसे किसी विशेष क्रम में उप-कार्यों की श्रृंखला कहा जाता था। यदि इनमें से कोई भी उप-कार्य विफल रहा (झूठी लौटाई गई), तो कॉलिंग फ़ंक्शन तुरंत लौटने की आवश्यकता है। तो मैं इसे इस तरह से कर सकता था:

bool Function(void) {
  if (!SubFunction1())
    return false;
  if (!SubFunction2())
    return false;
  if (!SubFunction3())
    return false;

(etc)

  return true;
}

या, अधिक सुरुचिपूर्ण समाधान:

bool Function(void) {
  if (!SubFunction1() || !SubFunction2() || !SubFunction3() || (etc) )
    return false;
  return true;
}

एक बार जब आप इसे शुरू करना शुरू कर देते हैं, तो आप इसे अधिक से अधिक बार उपयोग करने के अवसर देखेंगे।


आलसी मूल्यांकन गरीब व्यक्ति के समीकरण तर्क (जिसे उम्मीद की जा सकती है, आदर्श रूप से, प्रकारों और संचालन के गुणों से कोड के गुणों को कम करने के लिए)।

उदाहरण जहां यह काफी अच्छी तरह से काम करता है: sum . take 10 $ [1..10000000000] sum . take 10 $ [1..10000000000] । जिसे हम केवल एक प्रत्यक्ष और सरल संख्यात्मक गणना के बजाय, 10 संख्याओं की संख्या में कम करने का मन नहीं मानते हैं। आलसी मूल्यांकन के बिना यह केवल अपने पहले 10 तत्वों का उपयोग करने के लिए स्मृति में एक विशाल सूची बना देगा। यह निश्चित रूप से बहुत धीमा होगा, और आउट-ऑफ-मेमोरी त्रुटि का कारण बन सकता है।

उदाहरण जहां यह उतना अच्छा नहीं है जितना हम चाहते हैं: sum . take 1000000 . drop 500 $ cycle [1..20] sum . take 1000000 . drop 500 $ cycle [1..20] sum . take 1000000 . drop 500 $ cycle [1..20] । जो वास्तव में 1 000 000 संख्याओं को जोड़ देगा, भले ही एक सूची में बजाय लूप में; फिर भी इसे कुछ सशर्त और कुछ सूत्रों के साथ, केवल एक प्रत्यक्ष संख्यात्मक गणना में कम किया जाना चाहिए । जो 1 000 000 संख्याओं को संक्षेप में तो बेहतर होगा । भले ही एक लूप में, और सूची में नहीं (यानी वनों की कटाई अनुकूलन के बाद)।

एक और बात यह है कि यह पूंछ रिकर्सन मॉड्यूलो कंस शैली में कोड करना संभव बनाता है, और यह सिर्फ काम करता है

सीएफ संबंधित उत्तर


आलसी मूल्यांकन डेटा संरचनाओं के साथ सबसे उपयोगी है। आप एक सरणी या वेक्टर को संरचना में केवल कुछ बिंदुओं को अनिवार्य रूप से निर्दिष्ट कर सकते हैं और पूरे सरणी के संदर्भ में सभी को व्यक्त कर सकते हैं। यह आपको डेटा संरचनाओं को बहुत संक्षेप में और उच्च रन-टाइम प्रदर्शन के साथ उत्पन्न करने देता है।

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

इसका उपयोग सक्रियण समारोह में और बैकप्रोपैगेशन लर्निंग एल्गोरिदम में भी किया जाता है (मैं केवल दो लिंक पोस्ट कर सकता हूं, इसलिए आपको AI.Instinct.Train.Delta मॉड्यूल में learnPat फ़ंक्शन को देखना होगा)। परंपरागत रूप से दोनों को अधिक जटिल पुनरावृत्त एल्गोरिदम की आवश्यकता होती है।


इस पर विचार करो:

if (conditionOne && conditionTwo) {
  doSomething();
}

विधि doSomething () केवल तभी निष्पादित की जाएगी यदि स्थिति एक सत्य है और शर्त दो सच है। जिस स्थिति में शर्त गलत है, आपको शर्त के परिणाम की गणना करने की आवश्यकता क्यों है? स्थिति के मूल्यांकन दो इस मामले में समय बर्बाद हो जाएगा, खासकर यदि आपकी हालत कुछ विधि प्रक्रिया का परिणाम है।

यह आलसी मूल्यांकन ब्याज का एक उदाहरण है ...


एक टिक-टैक-टो कार्यक्रम पर विचार करें। इसमें चार कार्य हैं:

  • एक चाल-पीढ़ी का कार्य जो वर्तमान बोर्ड लेता है और एक ही चाल के साथ प्रत्येक नए बोर्ड की एक सूची उत्पन्न करता है।
  • फिर एक "चाल पेड़" फ़ंक्शन होता है जो चालान फ़ंक्शन को लागू करता है ताकि सभी संभावित बोर्ड स्थितियों को प्राप्त किया जा सके जो इस से अनुसरण कर सकें।
  • एक मिनीमैक्स फ़ंक्शन है जो सबसे अच्छा अगला कदम खोजने के लिए पेड़ (या संभवतः इसका केवल एक हिस्सा) चलता है।
  • एक बोर्ड-मूल्यांकन फ़ंक्शन है जो यह निर्धारित करता है कि कोई भी खिलाड़ी जीता है या नहीं।

इससे चिंताओं का एक अच्छा स्पष्ट अलगाव पैदा होता है। विशेष रूप से चाल-निर्माण समारोह और बोर्ड मूल्यांकन कार्य केवल वे हैं जिन्हें खेल के नियमों को समझने की आवश्यकता होती है: चाल पेड़ और मिनीमैक्स फ़ंक्शन पूरी तरह से पुन: प्रयोज्य होते हैं।

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

हालांकि आलसी भाषा में चाल के पेड़ के तत्व केवल मिनीमैक्स फ़ंक्शन की मांगों के जवाब में उत्पन्न होते हैं: शीर्ष तत्व पर मिनीमैक्स को ढीला करने से पहले पूरे कदम पेड़ को उत्पन्न करने की आवश्यकता नहीं होती है। तो हमारी स्वच्छ मॉड्यूलर संरचना अभी भी असली गेम में काम करती है।


मुझे आलसी मूल्यांकन कई चीजों के लिए उपयोगी लगता है।

सबसे पहले, सभी मौजूदा आलसी भाषाएं शुद्ध हैं, क्योंकि आलसी भाषा में दुष्प्रभावों के बारे में तर्क करना बहुत मुश्किल है।

शुद्ध भाषाएं आपको समीकरण तर्क का उपयोग करके फ़ंक्शन परिभाषाओं के बारे में बताती हैं।

foo x = x + 3

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

दूसरा, मास्क में 'मूल्य प्रतिबंध' जैसी कई चीजें हैंस्सेल जैसी आलसी भाषाओं में जरूरी नहीं हैं। यह वाक्यविन्यास की एक बड़ी अस्वीकृति की ओर जाता है। भाषाओं की तरह एमएल var या मजेदार जैसे कीवर्ड का उपयोग करने की आवश्यकता है। हास्केल में ये चीजें एक धारणा के लिए गिर गईं।

तीसरा, आलस्य आपको बहुत कार्यात्मक कोड लिखने देता है जिसे टुकड़ों में समझा जा सकता है। हास्केल में फ़ंक्शन बॉडी लिखना आम है:

foo x y = if condition1
          then some (complicated set of combinators) (involving bigscaryexpression)
          else if condition2
          then bigscaryexpression
          else Nothing
  where some x y = ...
        bigscaryexpression = ...
        condition1 = ...
        condition2 = ...

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

प्रैक्टिस में, हम गार्ड का उपयोग करते हैं और पतन करते हैं कि आगे:

foo x y 
  | condition1 = some (complicated set of combinators) (involving bigscaryexpression)
  | condition2 = bigscaryexpression
  | otherwise  = Nothing
  where some x y = ...
        bigscaryexpression = ...
        condition1 = ...
        condition2 = ...

चौथा, आलस्य कभी-कभी कुछ एल्गोरिदम की अधिक सुंदर अभिव्यक्ति प्रदान करता है। हास्केल में एक आलसी 'त्वरित प्रकार' एक लाइनर है और इसका लाभ यह है कि यदि आप केवल कुछ ही वस्तुओं को देखते हैं, तो आप केवल उन वस्तुओं को चुनने की लागत के अनुपात में आनुपातिक भुगतान करते हैं। कुछ भी आपको सख्ती से ऐसा करने से रोकता है, लेकिन आपको एक ही एसिम्प्टोटिक प्रदर्शन को प्राप्त करने के लिए प्रत्येक बार एल्गोरिदम को रिकोड करना होगा।

पांचवां, आलस्य आपको भाषा में नए नियंत्रण संरचनाओं को परिभाषित करने की अनुमति देता है। आप एक सख्त भाषा में निर्माण की तरह एक नया 'अगर .. तो .. और ..' नहीं लिख सकते हैं। यदि आप किसी फ़ंक्शन को परिभाषित करने का प्रयास करते हैं जैसे:

if' True x y = x
if' False x y = y

एक सख्त भाषा में तो दोनों शाखाओं का मूल्यांकन मूल्य मूल्य के बावजूद किया जाएगा। जब आप लूप पर विचार करते हैं तो यह और भी खराब हो जाता है। सभी सख्त समाधानों के लिए भाषा आपको किसी प्रकार के उद्धरण या स्पष्ट लैम्ब्डा निर्माण प्रदान करने की आवश्यकता होती है।

अंत में, उसी नस में, प्रकार प्रणाली में साइड इफेक्ट्स से निपटने के लिए कुछ बेहतरीन तंत्र, जैसे मोनैड्स, वास्तव में केवल आलसी सेटिंग में प्रभावी रूप से व्यक्त किए जा सकते हैं। यह एफ # के वर्कफ़्लो की जटिलता की तुलना हास्केल मोनाड्स से की जाकर देखा जा सकता है। (आप एक सख्त भाषा में एक मोनड परिभाषित कर सकते हैं, लेकिन दुर्भाग्यवश आप आलस्य कानून और दो आलस्य की कमी के कारण अक्सर असफल हो जाते हैं और तुलनात्मक रूप से वर्कफ़्लो सख्त सामान का एक टन उठाते हैं।)


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

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

एक बार जब आप इसके बारे में सोचते हैं, तो उन सभी स्थानों के बारे में सोचें जहां आप एक डेटा संरचना लिखते हैं जिसे आप बाद में डेटा उत्पन्न करने के लिए उपयोग कर सकते हैं (इसके पहले इसे बहुत अधिक देखे बिना), और आप आलसी के लिए बहुत सारे उपयोग देखेंगे मूल्यांकन।


यदि आप साइमन पेटन जोन्स पर विश्वास करते हैं, आलसी मूल्यांकन प्रति महत्वपूर्ण नहीं है बल्कि केवल 'बाल शर्ट' के रूप में है जो डिजाइनरों को भाषा को शुद्ध रखने के लिए मजबूर करता है। मैं इस दृष्टिकोण के प्रति सहानुभूति पाता हूं।

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


यह स्निपेट आलसी और आलसी मूल्यांकन के बीच का अंतर दिखाता है। निस्संदेह इस फाइबोनैकी फ़ंक्शन को अनुकूलित किया जा सकता है और रिकर्सन के बजाय आलसी मूल्यांकन का उपयोग किया जा सकता है, लेकिन यह उदाहरण खराब कर देगा।

आइए मान लीजिए कि हमें किसी चीज़ के लिए 20 पहले नंबरों का उपयोग करना होगा, आलसी मूल्यांकन के साथ सभी 20 नंबरों को पहले से उत्पन्न नहीं किया जाना चाहिए, लेकिन आलसी मूल्यांकन के साथ वे केवल आवश्यकतानुसार उत्पन्न होंगे। इस प्रकार जब आप आवश्यकता हो तो केवल गणना मूल्य का भुगतान करेंगे।

नमूना उत्पादन

Not lazy generation: 0.023373
Lazy generation: 0.000009
Not lazy output: 0.000921
Lazy output: 0.024205
import time

def now(): return time.time()

def fibonacci(n): #Recursion for fibonacci (not-lazy)
 if n < 2:
  return n
 else:
  return fibonacci(n-1)+fibonacci(n-2)

before1 = now()
notlazy = [fibonacci(x) for x in range(20)]
after1 = now()
before2 = now()
lazy = (fibonacci(x) for x in range(20))
after2 = now()


before3 = now()
for i in notlazy:
  print i
after3 = now()

before4 = now()
for i in lazy:
  print i
after4 = now()

print "Not lazy generation: %f" % (after1-before1)
print "Lazy generation: %f" % (after2-before2)
print "Not lazy output: %f" % (after3-before3)
print "Lazy output: %f" % (after4-before4)

सामान्य आदेश मूल्यांकन के बीच एक आलसी मूल्यांकन (जैसे हास्केल में) के बीच एक अंतर है।

square x = x * x

निम्नलिखित अभिव्यक्ति का मूल्यांकन ...

square (square (square 2))

... उत्सुक मूल्यांकन के साथ:

> square (square (2 * 2))
> square (square 4)
> square (4 * 4)
> square 16
> 16 * 16
> 256

... सामान्य आदेश मूल्यांकन के साथ:

> (square (square 2)) * (square (square 2))
> ((square 2) * (square 2)) * (square (square 2))
> ((2 * 2) * (square 2)) * (square (square 2))
> (4 * (square 2)) * (square (square 2))
> (4 * (2 * 2)) * (square (square 2))
> (4 * 4) * (square (square 2))
> 16 * (square (square 2))
> ...
> 256

आलसी मूल्यांकन के साथ ...

> (square (square 2)) * (square (square 2))
> ((square 2) * (square 2)) * ((square 2) * (square 2))
> ((2 * 2) * (2 * 2)) * ((2 * 2) * (2 * 2))
> (4 * 4) * (4 * 4)
> 16 * 16
> 256

ऐसा इसलिए है क्योंकि आलसी मूल्यांकन वाक्यविन्यास पेड़ को देखता है और पेड़-परिवर्तन करता है ...

square (square (square 2))

           ||
           \/

           *
          / \
          \ /
    square (square 2)

           ||
           \/

           *
          / \
          \ /
           *
          / \
          \ /
        square 2

           ||
           \/

           *
          / \
          \ /
           *
          / \
          \ /
           *
          / \
          \ /
           2

... जबकि सामान्य आदेश मूल्यांकन केवल पाठ विस्तार करता है।

यही कारण है कि हम आलसी मूल्यांकन का उपयोग करते समय, अधिक शक्तिशाली हो जाते हैं (मूल्यांकन अक्सर अन्य रणनीतियों को समाप्त करता है) जबकि प्रदर्शन उत्सुक मूल्यांकन के बराबर होता है (कम से कम ओ-नोटेशन में)।


सीपीयू से संबंधित आलसी मूल्यांकन रैम से संबंधित कचरा संग्रह के समान ही है। जीसी आपको यह दिखाने का मौका देता है कि आपके पास असीमित मात्रा में स्मृति है और इस प्रकार आपको जितनी जरूरत हो उतनी मेमोरी में कई ऑब्जेक्ट्स का अनुरोध करें। रनटाइम स्वचालित रूप से अनुपयोगी वस्तुओं को पुनः प्राप्त कर देगा। LE आपको यह दिखाता है कि आपके पास असीमित कम्प्यूटेशनल संसाधन हैं - आप जितनी चाहें उतनी गणना कर सकते हैं। रनटाइम सिर्फ अनावश्यक (दिए गए मामले के लिए) गणना नहीं करेगा।

इन "नाटक" मॉडल का व्यावहारिक लाभ क्या है? यह संसाधनों के प्रबंधन से डेवलपर (कुछ हद तक) जारी करता है और आपके स्रोतों से कुछ बॉयलरप्लेट कोड हटा देता है। लेकिन अधिक महत्वपूर्ण यह है कि आप संदर्भों के व्यापक सेट में अपने समाधान का कुशलतापूर्वक उपयोग कर सकते हैं।

कल्पना करें कि आपके पास संख्या एस और संख्या संख्या की एक सूची है। आपको सूची एस से निकटतम संख्या संख्या संख्या एम खोजने की आवश्यकता है। आपके पास दो संदर्भ हो सकते हैं: एकल एन और कुछ सूची एनएस (एल में प्रत्येक एन के लिए ईआई आप एस में सबसे नज़दीकी एम देखते हैं)। यदि आप आलसी मूल्यांकन का उपयोग करते हैं, तो आप एस को क्रमबद्ध कर सकते हैं और निकटतम एम से एन खोजने के लिए बाइनरी खोज लागू कर सकते हैं। अच्छे आलसी सॉर्टिंग के लिए इसे एकल एन और ओ (एलएन (आकार (एस)) के लिए ओ (आकार (एस)) चरणों की आवश्यकता होगी * (आकार (एस) + आकार (एल))) समान रूप से वितरित एल के लिए कदम। यदि आपके पास इष्टतम दक्षता प्राप्त करने के लिए आलसी मूल्यांकन नहीं है तो आपको प्रत्येक संदर्भ के लिए एल्गोरिदम लागू करना होगा।


आलसी मूल्यांकन का एक उपयोगी उदाहरण quickSort का उपयोग है:

quickSort [] = []
quickSort (x:xs) = quickSort (filter (< x) xs) ++ [x] ++ quickSort (filter (>= x) xs)

अगर हम अब सूची में से न्यूनतम खोजना चाहते हैं, तो हम परिभाषित कर सकते हैं

minimum ls = head (quickSort ls)

जो सूची में पहला प्रकार है और फिर सूची का पहला तत्व लेता है। हालांकि, आलसी मूल्यांकन के कारण, केवल सिर की गणना की जाती है। उदाहरण के लिए, यदि हम न्यूनतम सूची [2, 1, 3,] लेते हैं तो QuickSort पहले दो से छोटे सभी तत्वों को फ़िल्टर करेगा। फिर यह उस पर quickSort करता है (सिंगलटन सूची [1] लौट रहा है) जो पहले से ही पर्याप्त है। आलसी मूल्यांकन के कारण, बाकी को कभी भी क्रमबद्ध नहीं किया जाता है, जो बहुत सारे कम्प्यूटेशनल समय को बचाता है।

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

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





lazy-evaluation