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


119

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

नोट: मेरा मतलब संस्मरण से नहीं है।

जवाबों:


96

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

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


6
पायथन ने पुनरावृत्तियों के माध्यम से अनंत सूचियों का मूल्यांकन किया है
मार्क सिडैड

4
: आप वास्तव में जनरेटर और जनरेटर भाव (एक सूची समझ करने के लिए एक समान तरीके से जो काम) का उपयोग कर पायथन में एक अनंत सूची का अनुकरण कर सकते हैं python.org/doc/2.5.2/ref/genexpr.html
जॉन मोंटगोमरी

24
पाइथन में जनरेटर आलसी सूचियों को आसान बनाते हैं, लेकिन अन्य आलसी मूल्यांकन तकनीक और डेटा संरचनाएं काफी कम सुरुचिपूर्ण हैं।
पीटर बर्न्स 21

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

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

71

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

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

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

minimum ls = head (quickSort ls)

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

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

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


19
आमतौर पर, take k $ quicksort listकेवल O (n + k log k) समय लगता है, जहां n = length list। एक गैर-आलसी तुलना प्रकार के साथ, यह हमेशा O (n लॉग एन) समय लेगा।
ईपीमिएंट

@ephemient का मतलब O (nk log k) नहीं है?
MaiaVictor

1
@ विलीब नहीं, मेरा मतलब था कि मैंने क्या कहा।
एपीमेन्सर

@ कुशल तो मुझे लगता है कि मैं इसे प्राप्त नहीं करता, दुख की बात है
MaiaVictor

2
@Viclib n में से शीर्ष k तत्वों को खोजने के लिए एक चयन एल्गोरिथ्म O (n + k log k) है। जब आप एक आलसी भाषा में क्विकॉर्ट को लागू करते हैं, और केवल पहले के तत्वों (इसके बाद मूल्यांकन रोकना) का निर्धारण करने के लिए केवल इसका पर्याप्त मूल्यांकन करते हैं, तो यह एक ही-आलसी चयन एल्गोरिदम के रूप में सटीक समान तुलना करता है।
ephemient

70

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

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

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

foo x = x + 3

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

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

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

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

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

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

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

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

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


5
बहुत अच्छा; ये असली जवाब हैं। मुझे लगता है कि यह दक्षता के बारे में था (बाद के लिए गणना में देरी) जब तक मैंने हास्केल को एक महत्वपूर्ण राशि का उपयोग नहीं किया और देखा कि वास्तव में इसका कारण नहीं है।
ओवेन

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

4
ज़रूर है। एक सख्त भाषा में पुनरावर्ती letएक खतरनाक जानवर है, R6RS योजना में यह #fआपके कार्यकाल में यादृच्छिक रूप से प्रकट होता है जहाँ गाँठ को सख्ती से बांधने से एक चक्र होता है! कोई सज़ा का इरादा नहीं है, लेकिन letएक आलसी भाषा में सख्ती से अधिक पुनरावर्ती बाइंडिंग समझदार हैं। कठोरता भी इस तथ्य को बढ़ाती है कि whereएससीसी को छोड़कर किसी भी तरह के सापेक्ष प्रभाव का आदेश देने का कोई तरीका नहीं है, यह एक बयान स्तर का निर्माण है, इसका प्रभाव किसी भी क्रम में सख्ती से हो सकता है, और यहां तक ​​कि अगर आपके पास एक शुद्ध भाषा है जो आप के साथ हवा है। #fमुद्दा। सख्त whereगैर-स्थानीय चिंताओं के साथ अपने कोड को कठोर करता है।
एडवर्ड KMETT

2
क्या आप बता सकते हैं कि आलस्य मूल्य प्रतिबंध से बचने में कैसे मदद करता है? मैं इसे समझ नहीं पा रहा हूं।
टॉम एलिस

3
@PaBBone आप किस बारे में बात कर रहे हैं? आलस्य का नियंत्रण संरचनाओं के साथ एक टन है। यदि आप एक सख्त भाषा में अपनी स्वयं की नियंत्रण संरचना को परिभाषित करते हैं तो या तो लैम्ब्डा या इसी तरह के एक गुच्छा का उपयोग करना होगा, या यह चूसना होगा। क्योंकि ifFunc(True, x, y)दोनों का मूल्यांकन करने जा रहा है xऔर yसिर्फ के बजाय x
अर्धविराम

28

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

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

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

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


25

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

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

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


जीसी के साथ सादृश्य ने मुझे थोड़ी मदद की, लेकिन क्या आप कृपया "कुछ बॉयलरप्लेट कोड हटाता है" का एक उदाहरण दे सकते हैं?
अब्दुल

1
@Abdul, किसी भी ORM उपयोगकर्ता के लिए एक उदाहरण है: आलसी एसोसिएशन लोड हो रहा है। यह "बस समय में" डीबी से एसोसिएशन को लोड करता है और साथ ही इसे लोड करने के लिए स्पष्ट रूप से निर्दिष्ट करने और इसे कैश करने का तरीका निर्दिष्ट करने की आवश्यकता से एक डेवलपर जारी करता है (यह मेरा मतलब बॉयलरप्लेट है)। यहाँ एक और उदाहरण है: projectlombok.org/features/GetterLazy.html
एलेक्सी

25

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

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


इसने उन्हें न केवल भाषा को शुद्ध रखने के लिए मजबूर किया , यह सिर्फ उन्हें ऐसा करने की अनुमति देता है, जब ( IOसन्यासी के परिचय से पहले ) का हस्ताक्षर mainहोगा String -> Stringऔर आप पहले से ही ठीक से इंटरेक्टिव प्रोग्राम लिख सकते थे।
लेफ्टरनैबाउट

@leftractionabout: एक सनक में सभी प्रभावों को मजबूर करने से एक सख्त भाषा क्या रोक रही है IO?
टॉम एलिस

13

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

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

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

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

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


1
[एक "उत्सुक" (यानी पारंपरिक) भाषा में यह काम नहीं करेगा क्योंकि चाल का पेड़ स्मृति में फिट नहीं होगा] - टिक-टैक-टो के लिए यह निश्चित रूप से होगा। स्टोर करने के लिए अधिकांश 3 ** 9 = 19683 स्थान हैं। यदि हम प्रत्येक को एक अतिरिक्त 50 बाइट में संग्रहीत करते हैं, तो यह लगभग एक मेगाबाइट है। यह कुछ भी नहीं है ...
जोनास कोल्कर

6
हाँ, मेरी बात यही है। उत्सुक भाषाओं में तुच्छ खेलों के लिए एक साफ संरचना हो सकती है, लेकिन वास्तविक कुछ के लिए उस संरचना से समझौता करना होगा। आलसी भाषाओं में वह समस्या नहीं है।
पॉल जॉनसन

3
हालांकि, निष्पक्ष होना, आलसी मूल्यांकन यह स्मृति मुद्दों के लिए नेतृत्व कर सकते हैं। लोगों के लिए यह पूछना असामान्य नहीं है कि हैस्केल क्यों उड़ रहा है, यह किसी ऐसी चीज के लिए स्मृति है, जो एक उत्सुक मूल्यांकन में, ओ (1) की मेमोरी खपत होगी
RHSeeger

@PaulJohnson यदि आप सभी पदों का मूल्यांकन करते हैं, तो इससे कोई फर्क नहीं पड़ता कि आप उनका उत्सुकता से या आलसी तरीके से मूल्यांकन करते हैं। वही काम करना पड़ता है। यदि आप बीच में रुकते हैं और केवल आधे पदों का मूल्यांकन करते हैं, तो इससे भी कोई फर्क नहीं पड़ता है, क्योंकि दोनों ही स्थिति में आधा काम करना होगा। दो मूल्यांकनों के बीच एकमात्र अंतर यह है, कि एल्गोरिथ्म अच्छा लग रहा है, अगर आलसी लिखा जाए।
9

12

यहां दो और बिंदु दिए गए हैं, जो मुझे नहीं लगता कि चर्चा में लाया गया है।

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

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


10

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

"आलसी" मूल्यांकन जब और जिस तरह की जरूरत होती है, ऑपरेशन कर रहा है। यह उपयोगी है जब यह एक प्रोग्रामिंग भाषा या पुस्तकालय की एक विशेषता है क्योंकि यह आम तौर पर अपने आप पर आलसी मूल्यांकन को लागू करने के लिए कठिन है बस सब कुछ सामने से पहले करना।


1
कुछ लोग कह सकते हैं कि यह वास्तव में "आलसी निष्पादन" है। हास्केल जैसी यथोचित शुद्ध भाषाओं को छोड़कर यह अंतर वास्तव में सारगर्भित है; लेकिन अंतर यह है कि यह केवल गणना में देरी नहीं है, बल्कि इसके साथ जुड़े दुष्प्रभाव भी हैं (जैसे फाइलें खोलना और पढ़ना)।
ओवेन

8

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

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

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

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


मैंने सोचा था कि कम-परिवृत्त, आलसी मूल्यांकन नहीं था।
थॉमस ओवेन्स

2
यह आलसी मूल्यांकन है क्योंकि कंडीशनट्वो की गणना केवल तभी की जाती है जब इसकी वास्तव में आवश्यकता होती है (अर्थात यदि स्थिति सही हो तो)।
बजे रोमेन लिंसोलास

7
मुझे लगता है कि शॉर्ट-सर्कुलेटिंग आलसी मूल्यांकन का एक पतित मामला है, लेकिन यह निश्चित रूप से इसके बारे में सोचने का एक सामान्य तरीका नहीं है।
rmeador

19
शॉर्ट-सर्किटिंग वास्तव में आलसी मूल्यांकन का एक विशेष मामला है। आलसी मूल्यांकन में केवल शॉर्ट-सर्कुलेटिंग से कहीं अधिक स्पष्ट रूप से शामिल है। या, आलसी मूल्यांकन के ऊपर और ऊपर शॉर्ट-सर्किटिंग का क्या मतलब है?
yfeldblum

2
@ जूलियट: आपके पास 'आलसी' की एक मजबूत परिभाषा है। दो मापदंडों को लेने वाले फ़ंक्शन का आपका उदाहरण स्टेटमेंट के अनुसार शॉर्ट सर्किट के समान नहीं है। शॉर्ट सर्किट यदि स्टेटमेंट अनावश्यक गणनाओं से बचता है। मुझे लगता है कि आपके उदाहरण की तुलना में विजुअल बेसिक के संचालक "औरलसो" होंगे जो दोनों स्थितियों का मूल्यांकन करने के लिए मजबूर करते हैं

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

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

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

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


6

आलस्य का एक बड़ा लाभ उचित परिशोधन सीमाओं के साथ अपरिवर्तनीय डेटा संरचनाओं को लिखने की क्षमता है। एक सरल उदाहरण एक अपरिवर्तनीय स्टैक (F # का उपयोग करके) है:

type 'a stack =
    | EmptyStack
    | StackNode of 'a * 'a stack

let rec append x y =
    match x with
    | EmptyStack -> y
    | StackNode(hd, tl) -> StackNode(hd, append tl y)

कोड उचित है, लेकिन दो स्टैक x और y को जोड़ना O (x की लंबाई) सबसे अच्छा, सबसे खराब और औसत मामलों में समय लेता है। दो ढेर लगाना एक अखंड ऑपरेशन है, यह स्टैक एक्स में सभी नोड्स को छूता है।

हम डेटा संरचना को एक आलसी स्टैक के रूप में फिर से लिख सकते हैं:

type 'a lazyStack =
    | StackNode of Lazy<'a * 'a lazyStack>
    | EmptyStack

let rec append x y =
    match x with
    | StackNode(item) -> Node(lazy(let hd, tl = item.Force(); hd, append tl y))
    | Empty -> y

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

आलसी संस्करण के साथ, एपेंड एक ओ (1) ऑपरेशन हैं: यह 1 नोड लौटाता है और सूची के वास्तविक पुनर्निर्माण को निलंबित करता है। जब आप इस सूची के प्रमुख को प्राप्त करते हैं, तो यह नोड की सामग्री का मूल्यांकन करेगा, जिससे वह सिर को वापस कर देगा और शेष तत्वों के साथ एक निलंबन बना सकता है, इसलिए सूची का प्रमुख लेना O (1) ऑपरेशन है।

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

ऊपर दी गई डेटा संरचना में प्रत्येक ट्रैवर्सल पर नोड्स को फिर से विभाजित करने की आवश्यकता नहीं होती है, इसलिए वे .NET में वेनिला IEnumerables से अलग हैं।


5

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

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

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

आलसी पीढ़ी नहीं: 0.023373
आलसी पीढ़ी: 0.000009
आलसी आउटपुट नहीं: 0.000921
आलसी आउटपुट: 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)

5

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

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

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


4

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

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

fix f = f (fix f)

इसका विस्तार होता है

f (f (f ....

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

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

महत्वपूर्ण रूप से, यह मायने नहीं रखता है कि fixआलसी हो, बल्कि fआलसी हो। एक बार जब आपको पहले से ही सख्त दिया जाता है 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)

3

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

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

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


1
सख्त भाषाओं में आलसी मूल्यांकन को लागू करना अक्सर ट्यूरिंग टारपिट होता है।
इसका कर्क

2

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

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;
}

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


2

आलसी मूल्यांकन के बिना आपको ऐसा कुछ लिखने की अनुमति नहीं दी जाएगी:

  if( obj != null  &&  obj.Value == correctValue )
  {
    // do smth
  }

खैर, imo, ऐसा करना एक बुरा विचार है। हालांकि यह कोड सही हो सकता है (आप जो हासिल करने की कोशिश करते हैं उसके आधार पर), यह पढ़ना मुश्किल है, जो हमेशा एक बुरी चीज है।
बर्न

12
मुझे ऐसा नहीं लगता। सी और उसके रिश्तेदारों में इसका एक मानक निर्माण।
पॉल जॉनसन

यह शॉर्ट-सर्किट मूल्यांकन का एक उदाहरण है, न कि आलसी मूल्यांकन। या कि प्रभावी रूप से एक ही बात है?
रुफुसवीस

2

अन्य बातों के अलावा, आलसी भाषाएँ बहुआयामी अनंत डेटा संरचनाओं की अनुमति देती हैं।

योजना, अजगर, आदि धाराओं के साथ एकल आयामी अनंत डेटा संरचनाओं की अनुमति देते हैं, आप केवल एक आयाम के साथ पार कर सकते हैं।

आलस्य एक ही फ्रिंज समस्या के लिए उपयोगी है , लेकिन यह उस लिंक में उल्लिखित coroutines कनेक्शन को ध्यान देने योग्य है।


2

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

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

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


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

सीएफ संबंधित जवाब


1

यदि "आलसी मूल्यांकन" द्वारा आप का अर्थ है कंघी बुलियन में, जैसे में

   if (ConditionA && ConditionB) ... 

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

अगर ओटोह, आपका मतलब है कि मुझे "आलसी इनिशियलाइज़र" के रूप में जाना जाता है, जैसे:

class Employee
{
    private int supervisorId;
    private Employee supervisor;

    public Employee(int employeeId)
    {
        // code to call database and fetch employee record, and 
        //  populate all private data fields, EXCEPT supervisor
    }
    public Employee Supervisor
    { 
       get 
          { 
              return supervisor?? (supervisor = new Employee(supervisorId)); 
          } 
    }
}

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


0

उच्चतर क्रम के कार्यों का अंश

आइए, 100,000 के तहत सबसे बड़ी संख्या ज्ञात करें जो कि 3829 से विभाज्य है। ऐसा करने के लिए, हम सिर्फ संभावनाओं का एक समूह फ़िल्टर करेंगे, जिसमें हम जानते हैं कि समाधान निहित है।

largestDivisible :: (Integral a) => a  
largestDivisible = head (filter p [100000,99999..])  
    where p x = x `mod` 3829 == 0 

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

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