2009-05-07 21 views
31

मेरे पास कुछ हास्केल कोड है जो एक अनंत सूची पर सही ढंग से काम करता है, लेकिन मुझे क्यों नहीं समझता है यह सफलतापूर्वक ऐसा कर सकता है। (मैंने अपना मूल कोड संशोधित किया - जिसने अनंत सूचियों को संभाल नहीं लिया - ऑनलाइन किसी अन्य कोड से कुछ शामिल करने के लिए, और अचानक मुझे लगता है कि यह काम करता है लेकिन पता नहीं क्यों)।यह हास्केल कोड अनंत सूचियों के साथ सफलतापूर्वक क्यों काम करता है?

myAny :: (a -> Bool) -> [a] -> Bool 
myAny p list = foldr step False list 
    where 
     step item acc = p item || acc 

foldr की मेरी समझ है कि यह होगा की सूची में हर आइटम के माध्यम से लूप (और शायद कि समझे नहीं जा सके) है। यदि ऐसा है, तो इससे कोई फर्क नहीं पड़ता कि कैसे "चरण" फ़ंक्शन वाक्यांशित किया जाता है ... कोड अनंत लूप को संभालने में असमर्थ होना चाहिए।

हालांकि, निम्नलिखित काम करता है:

*Main Data.List> myAny even [1..] 
True 

कृपया मदद मुझे समझने: क्यों ??

उत्तर

46

के कैसे हास्केल आपकी अभिव्यक्ति का मूल्यांकन करेंगे की हमारे सिर में एक छोटे से ट्रेस करते हैं। स्थानापन्न प्रत्येक पंक्ति पर बराबरी के लिए बराबर होती है, अभिव्यक्ति को बहुत शीघ्र सही का आकलन:

myAny even [1..] 
foldr step False [1..] 
step 1 (foldr step False [2..]) 
even 1 || (foldr step False [2..]) 
False || (foldr step False [2..]) 
foldr step False [2..] 
step 2 (foldr step False [3..]) 
even 2 || (foldr step False [3..]) 
True || (foldr step false [3..]) 
True 

यह काम करता है क्योंकि acc एक unevaluated thunk (आलसी मूल्यांकन) के रूप में पारित हो जाता है, लेकिन यह भी || समारोह में अपनी पहले सख्त है क्योंकि तर्क।

तो यह समाप्त हो जाता है:

True || and (repeat True) 

लेकिन यह नहीं करता है: की परिभाषा को

and (repeat True) || True 

देखो || देखने के लिए क्यों यह मामला है:

True || _ = True 
False || x = x 
+4

इसके अतिरिक्त आप यह सत्यापित कर सकते हैं कि कोड 2 से अधिक तत्वों की गणना नहीं करता है: 'myAny p list = foldr (\ ia -> trace (show i) (pi || a))' - जो केवल '1 2 दिखाएगा) सही ' – viraptor

+0

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

+0

बीटीडब्ल्यू, || है अपने पहले तर्क में पूरी तरह सख्त है, या यह केवल अपने पहले तर्क को "वरीयता देता है"? उदाहरण के लिए, अगर तर्क 2 पहले से ही eval'd किया गया था, लेकिन क्या तर्क 1 अभी भी एक थंक था? और कहें कि तर्क 2 झूठा था। विपरीत दिशा में हास्केल शॉर्ट सर्किट होगा? धन्यवाद। –

1

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

प्रारंभ से this Wiki article

+0

ठीक है, मुझे पता था कि आलसी मूल्यांकन के बारे में। लेकिन मुझे उस बिंदु से कनेक्ट करने में मदद की ज़रूरत है कि उपर्युक्त कोड क्यों काम करता है। इस बीच, मैं विकी लेख देखेंगे। धन्यवाद। –

1

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

देखें http://en.wikipedia.org/wiki/Lazy_evaluation

+0

यह एक अच्छा लेख है, लिंक के लिए धन्यवाद। –

18

foldr की मेरी समझ है कि यह सूची में हर आइटम के माध्यम से इच्छा पाश (और शायद उस समझ अधूरा है) है।

foldr (foldl के विपरीत) सूची के हर आइटम लूप करने के लिए नहीं है। यह देखने के लिए निर्देशक है कि कैसे foldr परिभाषित किया गया है।

foldr f z []  = z 
foldr f z (x:xs) = f x (foldr f z xs) 

जब foldr के लिए एक कॉल मूल्यांकन किया जाता है, यह समारोह f के लिए एक कॉल के मूल्यांकन बाध्य करती है। लेकिन ध्यान दें कि foldr पर रिकर्सिव कॉल फ़ंक्शन f पर तर्क में एम्बेड किया गया है। f अपने दूसरे तर्क का मूल्यांकन नहीं करता है तो उस रिकर्सिव कॉल का मूल्यांकन नहीं किया जाता है।

+0

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

+2

चार्ली, क्योंकि यह हैस्केल का उपयोग कर रहे हैं, * कुछ भी * मूल्यांकन नहीं किया जाता है जब तक कि कुछ इसे मजबूर नहीं करता। –