क्या एक ज्ञापन शुद्ध कार्य को ही शुद्ध माना जाता है?


47

मान लीजिए कि fn(x)एक शुद्ध कार्य है जो कुछ महंगा करता है, जैसे कि प्रमुख कारकों की सूची वापस करना x

और हम कहते हैं कि हम उसी फ़ंक्शन का एक ज्ञापन संस्करण बनाते हैं जिसे कहा जाता है memoizedFn(x)। यह हमेशा दिए गए इनपुट के लिए समान परिणाम देता है, लेकिन यह प्रदर्शन को बेहतर बनाने के लिए पिछले परिणामों का एक निजी कैश रखता है।

औपचारिक रूप से, memoizedFn(x)शुद्ध माना जाता है?

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


24
शायद यह शुद्धतावादियों के लिए शुद्ध नहीं है, लेकिन व्यावहारिक लोगों के लिए "शुद्ध पर्याप्त" ;-)
डॉक्टर ब्राउन

2
@DocBrown मैं सहमत हूँ, बस सोच रहा था कि क्या 'शुद्ध पर्याप्त' के लिए एक अधिक औपचारिक शब्द है
callum

13
एक शुद्ध कार्य को निष्पादित करने से प्रोसेसर के इंस्ट्रक्शन कैश, ब्रांच प्रेडिक्टर्स इत्यादि को संशोधित करने की संभावना होगी, लेकिन प्यूरिस्ट्स के लिए "शुद्ध रूप से पर्याप्त" है - या आप पूरी तरह से शुद्ध कार्यों के बारे में भूल सकते हैं।
gnasher729

10
@ कोल्लम नहीं, "शुद्ध पर्याप्त" की कोई औपचारिक परिभाषा नहीं है। जब शुद्धता और दो "संदर्भात्मक पारदर्शी" कॉलों के शब्दार्थ तुल्यता के बारे में बहस करते हैं, तो आपको हमेशा यह बताना होता है कि आप कौन से शब्दार्थ को लागू करने जा रहे हैं। कार्यान्वयन विस्तार के कुछ निम्न स्तर पर यह हमेशा टूट जाएगा और अलग-अलग मेमोरी इफेक्ट या टाइमिंग होगा। इसलिए आपको व्यावहारिक होना चाहिए: आपके कोड के बारे में तर्क करने के लिए विस्तार का कौन सा स्तर उपयोगी है?
बरगी

3
फिर व्यावहारिकता की खातिर, मैं कहूंगा कि शुद्धता इस बात पर निर्भर करती है कि आप गणना समय को आउटपुट का हिस्सा मानते हैं या नहीं। funcx(){sleep(cached_time--); return 0;}हर बार एक ही घाटी लौटाता है, लेकिन अलग
मंगल

जवाबों:


41

हाँ। शुद्ध फ़ंक्शन का संस्मरणित संस्करण भी एक शुद्ध फ़ंक्शन है।

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

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


19

विकिपीडिया एक "शुद्ध कार्य" को एक फ़ंक्शन के रूप में परिभाषित करता है जिसमें निम्नलिखित गुण होते हैं:

  • इसका रिटर्न वैल्यू समान तर्कों के लिए समान है (स्थानीय स्थिर वैरिएबल, गैर-स्थानीय वैरिएबल, म्यूटेबल रेफरेंस तर्क या I / O उपकरणों से इनपुट स्ट्रीम के साथ भिन्नता नहीं)।

  • इसके मूल्यांकन का कोई साइड इफेक्ट नहीं है (स्थानीय स्थैतिक चर, गैर-स्थानीय चर, परस्पर संदर्भ तर्क या I / O स्ट्रीम) का कोई म्यूटेशन नहीं है।

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

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


16
मुझे कुछ याद हो सकता है, लेकिन आप कैश को साइड इफेक्ट के बिना कैसे रखने जा रहे हैं?
वैल

1
इसे फंक्शन के अंदर रखकर।
रॉबर्ट हार्वे

4
"स्थानीय स्थैतिक चर का कोई उत्परिवर्तन" नहीं है, साथ ही साथ कॉल के बीच लगातार स्थानीय चर को बाहर करने के लिए लगता है।
वैल

3
यह वास्तव में इस सवाल का जवाब नहीं देता है, भले ही आपको लगता है कि हां, यह शुद्ध है।
मंगल

6
@ अंतराल आप सही हैं: इस स्थिति को थोड़ा आराम करने की आवश्यकता है। विशुद्ध रूप से कार्यात्मक संस्मरण वह संदर्भित करता है जिसमें किसी भी स्थैतिक डेटा का कोई दृश्य उत्परिवर्तन नहीं होता है। क्या होता है कि परिणाम तब गणना और याद किया जाता है जब पहली बार फ़ंक्शन को बुलाया जाता है, और जब भी यह कहा जाता है, तब उसी मान को वापस करता है। कई भाषाओं में इसके लिए एक मुहावरा है: static constसी ++ (लेकिन सी नहीं) में एक स्थानीय चर या हास्केल में एक आलसी-मूल्यांकन किए गए डेटा संरचना। एक और शर्त है जिसकी आपको आवश्यकता है: आरंभीकरण थ्रेड-सुरक्षित होना चाहिए।
डेविसोर

7

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

एक महत्वपूर्ण चेतावनी है: संस्मरण फ़ंक्शन थ्रेड-सुरक्षित होना चाहिए, अन्यथा आपको एक दौड़ की स्थिति मिल सकती है जब दो थ्रेड्स दोनों इसे कॉल करने का प्रयास करते हैं।

इस तरह से "विशुद्ध रूप से कार्यात्मक" शब्द का उपयोग करने वाले एक कंप्यूटर वैज्ञानिक का एक उदाहरण यह ब्लॉग पोस्ट है, जो कॉनल इलियट द्वारा स्वत: लोड के बारे में है:

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

सहकर्मी की समीक्षा की गई साहित्य में कई उदाहरण हैं, और दशकों से हैं। उदाहरण के लिए, 1995 का यह पत्र, "वास्तविक-विश्व AI सिस्टम में एक सॉफ्टवेयर इंजीनियरिंग उपकरण के रूप में स्वचालित संस्मरण का उपयोग करना" , खंड 5.2 में बहुत ही समान भाषा का उपयोग यह बताने के लिए करता है कि हम आज एक शुद्ध कार्य को क्या कहेंगे:

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

कुछ अनिवार्य भाषाओं में एक समान मुहावरे होते हैं। उदाहरण के लिए, static constC ++ में एक वैरिएबल को केवल एक बार इनिशियलाइज़ किया जाता है, इससे पहले कि इसकी वैल्यू का उपयोग किया जाता है, और कभी भी म्यूट नहीं किया जाता है।


3

यह निर्भर करता है कि आप इसे कैसे करते हैं।

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

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

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

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

यह अंतिम बिंदु इस बात को सामने लाता है कि कभी-कभी लक्ष्य केवल इसे खत्म करने के बजाय म्यूटेशन को एन्कैप्सुलेट कर रहा होता है। अधिकांश कार्यात्मक प्रोग्रामर अशुद्धता को स्पष्ट और समझाया बनाने के लिए इसे "शुद्ध पर्याप्त" मानते हैं।

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


मुझे नहीं लगता कि कोई भी शुद्ध समाधान उपरोक्त समस्याओं को हल करता है: जब आप किसी भी संगामिति चिंताओं को खो देते हैं, तो आप दो समवर्ती शुरू किए गए कॉल जैसे collatz(100)और collatz(200)सहयोग करने के लिए किसी भी अवसर को खो देते हैं । और IIUIC, कैश के बढ़ने की समस्या बहुत बड़ी बनी हुई है (हालांकि हास्केल के पास इसके लिए कुछ अच्छी चालें हो सकती हैं?)।
Maaartinus

नोट: IOशुद्ध है। IOऔर बिल्लियों के सभी अशुद्ध तरीकों का नाम दिया गया है unsafeAsync.memoizeयह भी शुद्ध है, इसलिए हमें "शुद्ध पर्याप्त" के लिए समझौता नहीं करना है :)
शमूएल

2

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

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


0

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

[राज्य भिक्षु] मूल रूप से एक फ़ंक्शन S => (S, A) है, जहां S वह प्रकार है जो आपके राज्य का प्रतिनिधित्व करता है और A वह परिणाम है जो फ़ंक्शन उत्पन्न करता है - Cats State

आपके मामले में राज्य संस्मरण मूल्य या कुछ भी नहीं होगा (यानी हास्केल Maybeया स्काला Option[A])। यदि याद किया गया मूल्य मौजूद है तो इसे वापस लौटा दिया जाता है A, अन्यथा Aगणना की जाती है और परिवर्तित स्थिति और परिणाम दोनों के रूप में वापस किया जाता है।

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