2010-01-27 14 views
12

उत्सुक मूल्यांकन के विरोध में आलसी मूल्यांकन के लिए क्या फायदे हैं?आलसी मूल्यांकन के क्या फायदे हैं?

क्या प्रदर्शन ओवरहेड है? आलसी मूल्यांकन धीमा या तेज होने जा रहा है? क्यों (या यह कार्यान्वयन पर निर्भर करता है?)?

आलसी मूल्यांकन वास्तव में अधिकांश कार्यान्वयन में कैसे काम करता है? मेरे लिए ऐसा लगता है कि यह बहुत धीमा और स्मृति गहन होगा क्योंकि चर को संचालन के साथ-साथ संख्याओं को संग्रहित करना होगा। तो यह हास्केल जैसी भाषा में कैसे काम करता है (ध्यान दें, मैं वास्तव में उस भाषा को नहीं जानता)? आलसीपन को कैसे लागू किया जाता है और किया जाता है ताकि यह ज़्यादा धीमी गति से न हो और अधिक जगह का उपभोग न हो?

+1

डाउन-वोट के लिए एक टिप्पणी? – Earlz

+0

आप यहां कई सारे प्रश्न पूछ रहे हैं: 1) आलसी मूल्यांकन के लाभ, 2) प्रदर्शन, 3) इसका कार्यान्वयन कैसे किया जाता है। मैं इन्हें 3 अलग-अलग प्रश्नों में विभाजित करने की अनुशंसा करता हूं। – Juliet

उत्तर

5

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

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

+0

हास्केल सख्ती से आलसी मूल्यांकन किया गया है। बेशक संकलक अनुकूलित कर सकते हैं। –

1

रूबी में, हम फ़ंक्शन संशोधक का उपयोग करते हैं, जिसे आमतौर पर "एक बार" कहा जाता है, ताकि एक विधि को लपेट सके ताकि यह आलसी मूल्यांकन हो। इस तरह की एक विधि का मूल्यांकन केवल एक बार किया जाएगा, मूल्य कैश किया जाएगा, बाद में कॉल उस मान को वापस कर देगा।

आलसी मूल्यांकन का एक उपयोग (या दुरुपयोग) स्पष्ट रूप से ऑब्जेक्ट प्रारंभिक अंतर्निहित आदेश का आदेश देना है। पहले:

def initialize 
    setup_logging # Must come before setup_database 
    setup_database # Must come before get_addresses 
    setup_address_correction # Must come before get_addresses 
    get_addresses 
end 

def setup_logging 
    @log = Log.new 
end 

def setup_database 
    @db = Db.new(@log) 
end 

def setup_address_correction 
    @address_correction = AddressCorrection.new 
end 

def get_addresses 
    @log.puts ("Querying addresses") 
    @addresses = @address_correction.correct(query_addresses(@db)) 
end 

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

def initialize 
    get_addresses 
end 

def log 
    Log.new 
end 
once :log 

def db 
    Db.new(log) 
end 
once :db 

def address_corrector 
    AddressCorrection.new 
end 
once :address_corrector 

def get_addresses 
    log.puts ("Querying addresses") 
    @addresses = address_corrector.correct(query_addresses(db)) 
end 

विभिन्न अन्योन्याश्रित वस्तुओं के प्रारंभ के आदेश अब (1) निहित है, और (2) स्वचालित है। नकारात्मक तरफ, नियंत्रण की प्रवाह अपारदर्शी हो सकती है अगर यह चाल बहुत भारी पर भरोसा करती है।

+0

क्या यह वास्तव में "आलसी मूल्यांकन" है, या केवल कार्यों को परिभाषित करने के बाद ही कार्य किया जाता है? यदि ऐसा है, तो वास्तव में वास्तव में कार्य कब निष्पादित होते हैं? मैं कुछ अन्य अवधारणाओं के लिए आलसी मूल्यांकन के बारे में सोच रहा हूं। "कैशिंग"! = "आलसी मूल्यांकन" – jsbueno

+1

आप सही हैं। मैंने इस तकनीक को "आलसी मूल्यांकन" के रूप में सीखा है, लेकिन इसे "आलसी प्रारंभिक" कहा जाता है। विधियों को परिभाषित करते समय _not_ मूल्यांकन किया जाता है, लेकिन जब पहली बार कॉल किया जाता है। –

10

आलसी मूल्यांकन कुशल amortized सीमाओं के साथ डेटा संरचनाओं के निर्माण में बहुत उपयोगी हो सकता है।

बस एक उदाहरण देने के लिए, यहाँ एक अपरिवर्तनीय ढेर वर्ग है:

class Stack<T> 
{ 
    public static readonly Stack<T> Empty = new Stack<T>(); 
    public T Head { get; private set; } 
    public Stack<T> Tail { get; private set; } 
    public bool IsEmpty { get; private set; } 

    private Stack(T head, Stack<T> tail) 
    { 
     this.Head = head; 
     this.Tail = tail; 
     this.IsEmpty = false; 
    } 

    private Stack() 
    { 
     this.Head = default(T); 
     this.Tail = null; 
     this.IsEmpty = true; 
    } 

    public Stack<T> AddFront(T value) 
    { 
     return new Stack<T>(value, this); 
    } 

    public Stack<T> AddRear(T value) 
    { 
     return this.IsEmpty 
      ? new Stack<T>(value, this) 
      : new Stack<T>(this.Head, this.Tail.AddRear(value)); 
    } 
} 

आप O(1) समय में ढेर के सामने से एक आइटम जोड़ सकते हैं, लेकिन यह करने के लिए एक आइटम जोड़ने के लिए O(n) समय की आवश्यकता है पीछे। चूंकि हमें अपनी संपूर्ण डेटा संरचना को फिर से बनाना है, AddRearमोनोलिथिक ऑपरेशन है।

यहाँ एक ही अपरिवर्तनीय ढेर है, लेकिन अब इसकी lazily का मूल्यांकन:

class LazyStack<T> 
{ 
    public static readonly LazyStack<T> Empty = new LazyStack<T>(); 

    readonly Lazy<LazyStack<T>> innerTail; 
    public T Head { get; private set; } 
    public LazyStack<T> Tail { get { return innerTail.Value; } } 
    public bool IsEmpty { get; private set; } 

    private LazyStack(T head, Lazy<LazyStack<T>> tail) 
    { 
     this.Head = head; 
     this.innerTail = tail; 
     this.IsEmpty = false; 
    } 

    private LazyStack() 
    { 
     this.Head = default(T); 
     this.innerTail = null; 
     this.IsEmpty = true; 
    } 

    public LazyStack<T> AddFront(T value) 
    { 
     return new LazyStack<T>(value, new Lazy<LazyStack<T>>(() => this, true)); 
    } 

    public LazyStack<T> AddRear(T value) 
    { 
     return this.IsEmpty 
      ? new LazyStack<T>(value, new Lazy<LazyStack<T>>(() => this, true)) 
      : new LazyStack<T>(this.Head, new Lazy<LazyStack<T>>(() => this.Tail.AddRear(value), true)); 
    } 
} 

अब AddRear समारोह स्पष्ट रूप से अब O(1) समय में चलाता है।जब हम Tail प्रॉपर्टी तक पहुंचते हैं, तो यह हेड नोड लौटने के लिए केवल लाइट वैल्यू का मूल्यांकन करेगा, फिर यह बंद हो जाता है, इसलिए यह अब एक मोनोलिथिक फ़ंक्शन नहीं है।

+3

क्या कोई भी डाउनवॉटर कोई टिप्पणी छोड़ने की देखभाल करेगा? क्या कोड गलत है या सिर्फ एक बुरा उदाहरण है?आलस्य का उपयोग करके अमूर्त सीमाएं डेटा संरचना डिजाइन और शोध में एक बहुत ही लोकप्रिय विषय है, इसे स्वयं Google करें: http://www.google.com/search?q=lazy+amortized+data+structure – Juliet

+0

+1 अभी भी 3 वर्षों के बाद;) यह कौनसी भाषा है? सी#? – leemes

+0

@leemes हां, यह सी # है। और यह एक महान उदाहरण है। – liweijian

5

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

if x == 1 then x + 3 else x + 2 

में विचार सख्त (उत्सुक) मूल्यांकन में, अगर एक्स वास्तव में बराबर दो, तो x + 3 का मूल्यांकन किया और दिया जाता है, और एक्स + 2 है, हास्केल में, ऐसी कोई बात नहीं होता है, x + 3 बस, अभिव्यक्ति पर्यत बना है उदाहरण के लिए, कहते हैं कि मेरे पास है:

let x = if x == 1 then x + 3 else x + 2 

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

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

एक और दिलचस्प दुष्प्रभाव यह है कि यदि हास्केल में वास्तव में Bool -> a -> a -> a प्रकार का कार्य है। हास्केल में इसका मतलब है कि यह किसी भी प्रकार का दूसरा, बूलियन प्रकार का एक तर्क लेता है, पहले जैसा ही होता है, और उस प्रकार को फिर से देता है। आप अलग-अलग नियंत्रण शाखाओं के अनंत मूल्यांकन में भाग नहीं लेते हैं क्योंकि मूल्यों का मूल्यांकन केवल तब किया जाता है जब उनकी आवश्यकता होती है, जो आमतौर पर कार्यक्रम के अंत में होती है जब एक बड़ी अभिव्यक्ति की रचना की जाती है और फिर अंतिम परिणाम के लिए मूल्यांकन किया जाता है, अंतिम परिणाम के लिए संकलक सोचने की सभी चीजों की आवश्यकता नहीं है। उदाहरण के लिए यदि मैं स्वयं द्वारा एक विशाल जटिल अभिव्यक्ति को विभाजित करता हूं, तो इसे दोनों भागों के मूल्यांकन के बिना '1' के लिए प्रतिस्थापित किया जा सकता है।

अंतर योजना, जो परंपरागत रूप से सख्ती से मूल्यांकन किया जाता है में दिख रहा है, लेकिन वहाँ एक आलसी संस्करण लेज़ी योजना कहा जाता है, योजना (display (apply if (> x y) "x is larger than y" "x is not larger than y")) में कोई त्रुटि है क्योंकि if एक समारोह नहीं है, यह एक विशेष वाक्य रचना है (कुछ लोगों का कहना है, हालांकि वाक्य रचना है योजना में विशेष नहीं है) क्योंकि यह आवश्यक रूप से अपने सभी तर्कों का मूल्यांकन नहीं करता है, अन्यथा अगर हम उदाहरण के लिए एक फैक्टोरियल की गणना करने की कोशिश करते हैं तो हम स्मृति से बाहर निकल जाएंगे। आलसी योजना में जो ठीक काम करता है क्योंकि उन तर्कों में से कोई भी मूल्यांकन तब तक मूल्यांकन नहीं किया जाता है जब तक किसी फ़ंक्शन को प्रदर्शन जारी रखने के परिणाम की आवश्यकता नहीं होती है, जैसे प्रदर्शन।

+0

बहुत अच्छा नेक्रो जवाब :) – Earlz

संबंधित मुद्दे