शुद्ध कार्य: क्या "कोई साइड इफेक्ट" नहीं है "हमेशा एक ही आउटपुट, एक ही इनपुट दिया"?


84

एक फ़ंक्शन को परिभाषित करने वाली दो स्थितियां pureनिम्नानुसार हैं:

  1. कोई साइड इफेक्ट नहीं (यानी केवल स्थानीय क्षेत्र में परिवर्तन की अनुमति है)
  2. हमेशा उसी आउटपुट को वापस करें, उसी इनपुट को देखते हुए

यदि पहली शर्त हमेशा सच होती है, तो क्या किसी भी समय दूसरी स्थिति सच नहीं होती है?

Ie यह वास्तव में केवल पहली शर्त के साथ आवश्यक है?


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

4
@Alexander: "शुद्ध फ़ंक्शन" के संदर्भ में, "इनपुट" का अर्थ आमतौर पर उन मापदंडों / तर्कों से समझा जाता है जो स्पष्ट रूप से पारित किए जाते हैं (जो भी तंत्र प्रोग्रामिंग भाषा का उपयोग करता है)। यह "शुद्ध कार्य" की परिभाषा का हिस्सा है। लेकिन आप सही हैं, परिभाषा को ध्यान में रखना महत्वपूर्ण है।
sleske

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

2
आपको "साइड इफेक्ट्स" की अपनी परिभाषा का विस्तार करने की आवश्यकता है; आप कहते हैं कि एक शुद्ध विधि साइड इफेक्ट्स का उत्पादन नहीं करती है, लेकिन आपको यह भी ध्यान देने की आवश्यकता है कि एक शुद्ध विधि कहीं और उत्पादित साइड इफेक्ट्स का उपभोग नहीं करती है।
एरिक लिपर्ट

2
@ स्लेस्क शायद आमतौर पर समझा जाता है, लेकिन उस भेद की कमी ओपी के भ्रम का सटीक कारण है।
अलेक्जेंडर -

जवाबों:


114

यहाँ कुछ प्रतिकृतियां दी गई हैं जो बाहरी दायरे को नहीं बदलती हैं लेकिन फिर भी अशुद्ध मानी जाती हैं:

  • function a() { return Date.now(); }
  • function b() { return window.globalMutableVar; }
  • function c() { return document.getElementById("myInput").value; }
  • function d() { return Math.random(); } (जो माना जाता है कि PRNG बदल जाता है, लेकिन इसे देखने योग्य नहीं माना जाता है)

गैर-स्थिर गैर-स्थानीय चर तक पहुँचना दूसरी स्थिति का उल्लंघन करने में सक्षम होने के लिए पर्याप्त है।

मैं हमेशा पूरक के रूप में शुद्धता के लिए दो स्थितियों के बारे में सोचता हूं:

  • परिणाम मूल्यांकन में साइड स्टेट पर प्रभाव नहीं होना चाहिए
  • मूल्यांकन परिणाम पक्ष की स्थिति से प्रभावित नहीं होना चाहिए

शब्द साइड इफेक्ट केवल पहले को संदर्भित करता है, फ़ंक्शन गैर-स्थानीय स्थिति को संशोधित करता है। हालांकि, कभी-कभी रीड ऑपरेशंस को साइड इफेक्ट के रूप में भी माना जाता है: जब वे ऑपरेशन होते हैं और साथ ही लेखन को शामिल करते हैं , भले ही उनका प्राथमिक उद्देश्य किसी मूल्य तक पहुंच हो। इसके लिए उदाहरण एक छद्म यादृच्छिक संख्या उत्पन्न कर रहे हैं जो जनरेटर की आंतरिक स्थिति को संशोधित करता है, एक इनपुट स्ट्रीम से पढ़ना जो पढ़ने की स्थिति को आगे बढ़ाता है, या एक बाहरी सेंसर से पढ़ना है जिसमें "टेक माप" कमांड शामिल है।


1
धन्यवाद बरगी। किसी कारण से मुझे लगा कि साइड इफेक्ट्स में स्थानीय दायरे के बाहर पढ़ने वाले चर शामिल हैं , लेकिन मुझे लगता है कि यह केवल एक बाहरी प्रभाव है अगर यह ऐसे बाहरी चर लिखता है
मैग्नस

17
यदि prompt("you choose")साइड इफेक्ट्स नहीं हैं, तो हमें एक कदम वापस लेना चाहिए और साइड इफेक्ट्स का मतलब स्पष्ट करना चाहिए।
होल्गर

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

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

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

30

"सामान्य" तरीका, जो एक शुद्ध कार्य है, को फिर से दर्ज करने का तरीका संदर्भात्मक पारदर्शिता के संदर्भ में है । एक फ़ंक्शन शुद्ध है यदि यह संदर्भात्मक रूप से पारदर्शी है

प्रासंगिक ट्रांसपेरेंसी , मोटे तौर पर, का अर्थ है कि आप प्रोग्राम के अर्थ को बदले बिना, प्रोग्राम के किसी भी बिंदु पर कॉल को इसके रिटर्न मान या इसके विपरीत से बदल सकते हैं।

इसलिए, उदाहरण के लिए, यदि C printfसंदर्भित रूप से पारदर्शी थे , तो इन दो कार्यक्रमों का एक ही अर्थ होना चाहिए:

printf("Hello");

तथा

5;

और निम्नलिखित सभी कार्यक्रमों का एक ही अर्थ होना चाहिए:

5 + 5;

printf("Hello") + 5;

printf("Hello") + printf("Hello");

क्योंकि printfलिखे गए वर्णों की संख्या, इस मामले में 5।

यह voidकार्यों के साथ और भी स्पष्ट हो जाता है । यदि मेरे पास कोई फ़ंक्शन है void foo, तो

foo(bar, baz, quux);

जैसा होना चाहिए वैसा ही होना चाहिए

;

यानी चूंकि fooरिटर्न कुछ भी नहीं है, मुझे कार्यक्रम का अर्थ बदले बिना इसे कुछ भी नहीं बदलने में सक्षम होना चाहिए।

यह स्पष्ट है, तो, कि न तो printfहै और न ही fooreferentially पारदर्शी होते हैं, और इस तरह उनमें से कोई भी शुद्ध कर रहे हैं। वास्तव में, एक voidफ़ंक्शन कभी भी प्रासंगिक रूप से पारदर्शी नहीं हो सकता है, जब तक कि यह एक नो-ऑप न हो।

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

func fib(n):
    return memo[n] if memo.has_key?(n)
    return 1 if n <= 1
    return memo[n] = fib(n-1) + fib(n-2)

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

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


2
जब आप सम्‍मिलित होते हैं, तो यह अशुद्धता पूरे कार्यक्रम को प्रभावित करती है।
आर .. गिटहब स्टॉप हेल्पिंग ICE

@R .. क्या आप इस तरह से सोच सकते हैं कि संगणित वर्णित फाइबोनैचि फ़ंक्शन को बाहरी रूप से अशुद्ध बना सकता है? मैं नहीं कर सकता। लिखने के लिए memo[n]बेकार है, और इसे पढ़ने में विफल होने से केवल सीपीयू चक्र बर्बाद होता है।
Brilliand

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

@R .. एक संगामिति-जागरूक संस्करण की कल्पना करना मुश्किल नहीं है।
user253751

1
@ ब्रिलियन उदाहरण के लिए, memo[n] = ...पहले एक शब्दकोश प्रविष्टि बना सकते हैं, और फिर उसमें मूल्य को संग्रहीत कर सकते हैं। यह एक खिड़की छोड़ता है, जिसके दौरान एक और धागा एक असिंचित प्रविष्टि देख सकता है।
user253751

12

मुझे ऐसा लगता है कि आपने जो दूसरी स्थिति बताई है, वह पहले की तुलना में कमजोर है।

मैं आपको एक उदाहरण देता हूं, मान लीजिए कि आपके पास कंसोल में लॉग इन करने के लिए एक फ़ंक्शन है:

function addOneAndLog(x) {
  console.log(x);
  return x + 1;
}

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

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

मान लीजिए कि हमारे पास एक फ़ंक्शन है जो बस जोड़ता है:

function addOne(x) {
  return x + 1;
}

हम जगह ले सकता है addOne(5)के साथ 6हमारे कार्यक्रम और कुछ भी नहीं बदल जाएगा में कहीं भी।

इसके विपरीत, हम व्यवहार में बदलाव के बिना हमारे कार्यक्रम में कहीं भी addOneAndLog(x)मूल्य के साथ प्रतिस्थापित नहीं कर सकते 6हैं क्योंकि पहली अभिव्यक्ति में कंसोल को लिखा जाता है जबकि दूसरा नहीं होता है।

हम इस अतिरिक्त व्यवहार पर विचार करते हैं, जो addOneAndLog(x)आउटपुट के साइड-इफ़ेक्ट के अलावा प्रदर्शन करता है


"मुझे ऐसा लगता है कि आपने जो दूसरी स्थिति बताई है, वह पहले की तुलना में एक कमजोर बाधा है।" नहीं, दो स्थितियाँ तार्किक रूप से स्वतंत्र हैं।
sleske

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

छोटा टाइपो: ऐसा कुछ भी नहीं है जो बिना किसी साइड इफेक्ट के कोई फ़ंक्शन दिए गए इनपुट के लिए समान आउटपुट वापस करने के अलावा कर सकता है
इननरलाईट

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

2
ऐसा लगता है कि आप "साइड इफेक्ट" की एक अलग परिभाषा का उपयोग कर रहे हैं जो आमतौर पर उपयोग किया जाता है। एक साइड इफेक्ट को आमतौर पर "मान के वापस आने के अलावा एक अवलोकन प्रभाव" या "राज्य में अवलोकन योग्य परिवर्तन" के रूप में परिभाषित किया जाता है - उदाहरण के लिए देखें विकिपीडिया , सॉफ्टवेयर पोस्टिंग पर यह पोस्ट । आप पूरी तरह से सही हैं जो Date.now()शुद्ध / संदर्भित रूप से पारदर्शी नहीं है, लेकिन इसलिए नहीं कि इसके साइड-इफेक्ट हैं, बल्कि इसलिए कि इसका परिणाम सिर्फ इसके इनपुट से अधिक पर निर्भर करता है।
sleske

7

सिस्टम के बाहर से यादृच्छिकता का स्रोत हो सकता है। मान लीजिए कि आपकी गणना के हिस्से में कमरे का तापमान शामिल है। फिर फ़ंक्शन को निष्पादित करने से कमरे के तापमान के यादृच्छिक बाहरी तत्व के आधार पर हर बार अलग-अलग परिणाम मिलेंगे। कार्यक्रम को निष्पादित करने से राज्य नहीं बदला जाता है।

सभी मैं वैसे भी सोच सकते हैं।


3
मेरे अनुसार, ये "सिस्टम के बाहर से यादृच्छिकता" साइड इफेक्ट का एक रूप है। इन व्यवहारों के साथ कार्य "pures" नहीं हैं।
जोसेफ एम। डायोन

2

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

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

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

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


2

यदि पहली शर्त हमेशा सच होती है, तो क्या किसी भी समय दूसरी स्थिति सच नहीं होती है?

हाँ

नीचे सरल कोड स्निपेट पर विचार करें

public int Sum(int a, int b) {
    Random rnd = new Random();
    return rnd.Next(1, 10);
}

यह कोड इनपुट के एक ही सेट के लिए यादृच्छिक आउटपुट लौटाएगा - हालांकि इसका कोई साइड इफेक्ट नहीं है।

संयुक्त किए जाने पर दोनों बिंदुओं # 1 & # 2 के समग्र प्रभाव का अर्थ है: किसी भी समय यदि Sumसमान i / p के साथ कार्य किया जाता है तो इसके परिणाम को एक कार्यक्रम में बदल दिया जाता है, कार्यक्रम का समग्र अर्थ नहीं बदलता है । यह और कुछ नहीं बल्कि रेफ़रेंशियल ट्रांसपेरेंसी है


लेकिन इस मामले में, पहली स्थिति सत्यापित नहीं है: कंसोल पर लिखना एक साइड इफेक्ट माना जाता है, क्योंकि यह मशीन की स्थिति को ही बदल देता है।
राइट लेग

@Rightleg इंगित करने के लिए thx। किसी तरह मैं ओपी को पूरी तरह से गलत समझा। सही उत्तर।
रहलगा_देव

2
क्या यह यादृच्छिक जनरेटर की स्थिति को नहीं बदलता है?
एरिक डुमिनील

1
यादृच्छिक संख्या उत्पन्न करना अपने आप में एक साइड इफेक्ट है, जब तक कि यादृच्छिक संख्या जनरेटर की स्थिति स्पष्ट रूप से आपूर्ति नहीं की जाती है जो फ़ंक्शन को संतुष्ट करने की स्थिति को 2 बना देगा।
TheInnerLight

1
rndफ़ंक्शन से बच नहीं जाता है, इसलिए तथ्य यह है कि इसका राज्य परिवर्तन फ़ंक्शन की शुद्धता के लिए कोई फर्क नहीं पड़ता है, लेकिन तथ्य यह है कि Randomनिर्माता वर्तमान समय का उपयोग बीज मूल्य के रूप में करता है इसका मतलब है कि इसके अलावा aऔर "इनपुट" हैं b
स्नेफेल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.