2012-07-01 7 views
7

के आलसी मूल्यांकन व्यवहार को समझने में मदद की ज़रूरत है मैंने निक्सन समस्या के दो संस्करण लिखे हैं और मुझे लगता है कि उनके पास समान दक्षता होनी चाहिए लेकिन ऐसा नहीं है। मुझे लगता है कि यह हास्केल के आलसी मूल्यांकन व्यवहार के कारण है। कृपया कोई व्याख्या कर सकते हैं कि यह कैसे निम्न उदाहरण के लिए काम करता है,हास्केल

nqueens1 n 1 = [[i] | i <- [1..n]] 
nqueens1 n k = [ i:q | i <- [1..n], q <- (nqueens1 n (k - 1)), isSafe i q k] 

isSafe i q n = isSafeHelper i (zip q [(n-1),(n-2)..1]) 
     where isSafeHelper i [] = True 
       isSafeHelper i (x:xs) = (i /= fst x) && abs(i - (fst x)) /= abs(n - (snd x)) && 
             isSafeHelper i xs 
nqueens2 n 1 = [[i] | i <- [1..n]] 
nqueens2 n k = [ i:q | i <- [1..n], q <- boards, isSafe i q k] 
      where boards = nqueens2 n (k-1) 

आप उन्हें मूल्यांकन कर सकते हैं nqueens1 8 8 या nqueens2 8 8 बुला आकार 8.

के एक बोर्ड के लिए यह मूल्यांकन करने के लिए एक ओर जहां nqueens2 काफी काम करता है के द्वारा कुशलता से nqueens1 प्रदर्शन मुद्दों है। मुझे विश्वास है कि ऐसा इसलिए है क्योंकि रिकर्सिव कॉल (nqueens n (k-1)) का मूल्यांकन कई बार किया जा रहा है। हास्कल्स आलसी मूल्यांकन की मेरी समझ से यह मामला नहीं होना चाहिए।

कृपया इस व्यवहार को समझने में मेरी मदद करें।

अग्रिम धन्यवाद।

+1

"लेज़ी मूल्यांकन" बातें देर से मूल्यांकन के बारे में है - नहीं के मूल्यांकन में कई बार कुछ से बचने के बारे। –

+4

@DanielWagner असल में आलसी मूल्यांकन और कॉल-बाय-नाम के बीच का अंतर बिल्कुल सही है कि कॉल-बाय-नाम का उपयोग करके कई बार मूल्यांकन किए जाने वाले कुछ अभिव्यक्तियों का मूल्यांकन केवल आलसी मूल्यांकन का उपयोग करके किया जाता है। हालांकि यह समस्या से संबंधित नहीं है। – sepp2k

+2

@ sepp2k आप सही हैं, मुझे "आलसी मूल्यांकन" के बजाय "कॉल-बाय-नाम" या "कई बार मूल्यांकन करने से बचने" के बजाय "यादगार" कहकर अधिक सटीक होना चाहिए था। –

उत्तर

10

हां, रिकर्सिव कॉल का मूल्यांकन कई बार किया जाता है। विशेष रूप से i के लिए प्रत्येक मान के लिए इसका मूल्यांकन किया जाता है।

आप इस से बचना चाहते हैं, तो आप जनरेटर पुनर्व्यवस्थित कर सकते हैं ताकि q <- हिस्सा i <- भाग से पहले आता है:

[ i:q | q <- nqueens2 n (k - 1), i <- [1..n], isSafe i q k] 

हालांकि इस परिणामों का क्रम बदल जाएगा। तो यह स्वीकार्य नहीं है, तो आप let उपयोग कर सकते हैं एक बार पुनरावर्ती कॉल के परिणाम की गणना करने के लिए और उसके बाद इस तरह इसका इस्तेमाल:

[ i:q | let rec = nqueens2 n (k - 1), i <- [1..n], q <- rec, isSafe i q k] 
+0

मैंने अनुमान लगाया कि रिकर्सिव कॉल का मूल्यांकन एक से अधिक बार किया जा रहा था और यह nqueens1 में मंदी का कारण था, लेकिन nqueens2 में एकमात्र परिवर्तन उस अभिव्यक्ति का नाम देना है। यह आसानी से हास्केल कंपाइलर द्वारा किया जा सकता था। मैं जानना चाहता था कि संकलक ऐसा क्यों नहीं कर सकता। धन्यवाद। – prasannak

+2

इस तरह के "अनुकूलन" समय के लिए अंतरिक्ष व्यापार करता है। जबकि अब subterm का मूल्यांकन कई बार नहीं किया जाता है, इसे तब तक स्मृति में रखा जाना चाहिए जब तक इसे फिर से जरूरी हो। इसलिए, जीएचसी बेहद सावधान है और आमतौर पर इस प्रकार के परिवर्तन को स्वचालित रूप से नहीं करता है। अंगूठे का सामान्य नियम यह है: यदि आप यह सुनिश्चित करना चाहते हैं कि एक शब्द का मूल्यांकन एक बार किया जाता है, तो इसे एक नाम दें। – kosmikus