जावा में मूल्यांकन आदेश के नियम क्या हैं?


86

मैं कुछ जावा पाठ पढ़ रहा हूं और निम्नलिखित कोड मिला है:

int[] a = {4,4};
int b = 1;
a[b] = b = 0;

पाठ में, लेखक ने स्पष्ट स्पष्टीकरण नहीं दिया और अंतिम पंक्ति का प्रभाव है: a[1] = 0;

मुझे इतना यकीन नहीं है कि मैं समझता हूं: मूल्यांकन कैसे हुआ?


19
ऐसा क्यों होता है, इस पर नीचे दिए गए भ्रम का मतलब है कि आपको ऐसा कभी नहीं करना चाहिए, क्योंकि कई लोगों को यह सोचना होगा कि यह वास्तव में क्या करता है, इसके बजाय यह स्पष्ट होना चाहिए।
मार्टिज़न

इस तरह किसी भी सवाल का उचित जवाब है "ऐसा मत करो!" असाइनमेंट को एक बयान के रूप में माना जाना चाहिए; एक संकलक के रूप में असाइनमेंट का उपयोग जो एक संकलक त्रुटि, या बहुत कम से कम चेतावनी देने के लिए एक मूल्य लौटाता है। ऐसा मत करो।
मेसन व्हीलर

जवाबों:


173

मुझे यह स्पष्ट रूप से कहने दें, क्योंकि लोग इसे हर समय गलत समझते हैं:

सबएक्सप्रेस के मूल्यांकन का आदेश दोनों संघ और पूर्वता से स्वतंत्र है । सहकारिता और पूर्वता यह निर्धारित करती है कि ऑपरेटरों को किस क्रम में निष्पादित किया जाता है, लेकिन यह निर्धारित नहीं किया जाता है कि उप - अनुक्रमों का मूल्यांकन किस क्रम में किया जाता है। आपका प्रश्न उस आदेश के बारे में है जिसमें उप - परीक्षाओं का मूल्यांकन किया जाता है।

विचार करें A() + B() + C() * D()। गुणन इसके अलावा की तुलना में अधिक पूर्वता है, और इसके अलावा बायां-सहयोगी है, इसलिए यह बराबर है (A() + B()) + (C() * D()) लेकिन यह जानना कि केवल आपको बताता है कि पहला जोड़ दूसरे जोड़ से पहले होगा, और यह कि गुणा दूसरे जोड़ से पहले होगा। यह आपको नहीं बताता है कि A (), B (), C () और D () किस क्रम में कहा जाएगा! (यह आपको यह भी नहीं बताता है कि गुणा पहले या बाद में होता है या नहीं।) यह इस तरह संकलित करके पूर्वता और सहानुभूति के नियमों का पालन करना पूरी तरह से संभव होगा :

d = D()          // these four computations can happen in any order
b = B()
c = C()
a = A()
sum = a + b      // these two computations can happen in any order
product = c * d
result = sum + product // this has to happen last

पूर्ववर्तीता और सहानुभूति के सभी नियमों का पालन किया जाता है - पहला जोड़ दूसरे जोड़ से पहले होता है, और गुणा दूसरे जोड़ से पहले होता है। स्पष्ट रूप से हम किसी भी क्रम में ए (), बी (), सी () और डी () को कॉल कर सकते हैं और अभी भी पूर्वता और सहानुभूति के नियमों का पालन कर सकते हैं!

हमें उस आदेश की व्याख्या करने के लिए असंबंधित नियम की आवश्यकता होती है , जिसमें उपप्रकारों का मूल्यांकन किया जाता है। जावा (और C #) में प्रासंगिक नियम "सबसेक्स को बाएं से दाएं मूल्यांकन किया जाता है" है। चूँकि A () C के बाईं ओर दिखाई देता है (), A () का पहले मूल्यांकन किया जाता है, इस तथ्य की परवाह किए बिना कि C () एक गुणा में शामिल है और A () केवल एक अतिरिक्त में शामिल है।

तो अब आपके पास अपने प्रश्न का उत्तर देने के लिए पर्याप्त जानकारी है। सहकारिता a[b] = b = 0के नियमों में कहा गया है कि यह है a[b] = (b = 0);लेकिन इसका मतलब यह नहीं है कि b=0पहले रन! पूर्वता के नियम कहते हैं कि अनुक्रमण असाइनमेंट की तुलना में अधिक पूर्वता है, लेकिन इसका मतलब यह नहीं है कि अनुक्रमणिका सबसे सही असाइनमेंट से पहले चलती है

(अद्यतन: इस उत्तर के एक पुराने संस्करण में अनुभाग में कुछ छोटे और व्यावहारिक रूप से महत्वहीन चूक थे, जिन्हें मैंने सही किया है। मैंने एक ब्लॉग लेख भी लिखा है जिसमें बताया गया है कि ये नियम जावा और सी # में समझदार क्यों हैं: https: // ericlippert.com/2019/01/18/indexer-error-cases/ )

वरीयता और सहानुभूति हमें केवल यह बताती है कि शून्य का असाइनमेंट असाइनमेंट से पहलेb होना चाहिए , क्योंकि शून्य का असाइनमेंट उस मान की गणना करता है जिसे अनुक्रमण ऑपरेशन में सौंपा गया है। पहले और बाद में मूल्यांकन किया जाता है या नहीं इसके बारे में अकेलेपन और सहानुभूति कुछ भी नहीं कहती है ।a[b]a[b]b=0

फिर से, यह केवल इस प्रकार है: A()[B()] = C()- हम सभी जानते हैं कि अनुक्रमण को असाइनमेंट से पहले होना है। हम नहीं जानते कि क्या A (), B (), या C () पहले से पूर्वता और सहानुभूति के आधार पर चलता है । हमें यह बताने के लिए एक और नियम की आवश्यकता है।

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

तो क्या होता है?

  • a[b]के बाईं ओर है b=0हां, तो a[b]रन पहले , जिसके परिणामस्वरूप a[1]। हालाँकि, इस अनुक्रमण ऑपरेशन की वैधता की जाँच में देरी हो रही है।
  • तब b=0होता है।
  • फिर सत्यापन जो aमान्य है और a[1]सीमा में है, होता है
  • a[1]अंतिम होने के लिए मान का असाइनमेंट ।

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

लोगों को यह सामान हर समय गलत लगता है , यहां तक ​​कि उन लोगों को भी जिन्हें बेहतर पता होना चाहिए। मैंने अब तक बहुत सी प्रोग्रामिंग किताबें संपादित की हैं जो नियमों को गलत तरीके से बताती हैं, इसलिए यह कोई आश्चर्य की बात नहीं है कि बहुत से लोगों की पूर्ववर्ती / सहानुभूति और मूल्यांकन आदेश के बीच संबंध के बारे में पूरी तरह से गलत धारणाएं हैं - अर्थात्, वास्तव में ऐसा कोई संबंध नहीं है ; वे स्वतंत्र हैं।

यदि यह विषय आपकी रुचि रखता है, तो आगे पढ़ने के लिए इस विषय पर मेरे लेख देखें:

http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/

वे C # के बारे में हैं, लेकिन इनमें से अधिकांश सामान जावा पर समान रूप से लागू होते हैं।


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

क्या मैं सही हूं कि C ++ इसकी गारंटी नहीं देता है? अजगर के बारे में क्या?
नील जी

2
@ नील: सी ++ मूल्यांकन के आदेश के बारे में कुछ भी गारंटी नहीं देता है, और कभी नहीं किया। (और न ही सी।) पायथन इसे पूर्ववर्ती आदेश द्वारा सख्ती से गारंटी देता है; बाकी सब चीजों के विपरीत, असाइनमेंट R2L है।
डोनल फैलो

2
@ मेरे लिए आप सिर्फ उलझन में ध्वनि। और पूर्ववर्ती नियम केवल यह कहते हैं कि बच्चों का मूल्यांकन माता-पिता से पहले किया जाना चाहिए। लेकिन वे कहते हैं कि बच्चों के मूल्यांकन के क्रम के बारे में कुछ नहीं कहा जाता है। जावा और सी # को दाएं से बाएं चुना गया, सी और सी ++ ने अपरिभाषित व्यवहार को चुना।
संहिताओंइंचौस

6
@ रॉबर्ट: ओके, विचार करें: एम (ए) (बी +), सी () * डी (), ई () + एफ ())। आपकी इच्छा किस क्रम में मूल्यांकन किए जाने वाले सबसेक्स के लिए है? क्या C () और D () का मूल्यांकन A (), B (), E () और F () से पहले किया जाना चाहिए क्योंकि गुणन इसके अलावा उच्च पूर्वता है? यह कहना आसान है कि "स्पष्ट रूप से" आदेश अलग होना चाहिए। सभी मामलों को कवर करने वाले वास्तविक नियम के साथ आना अधिक कठिन है। C # और Java के डिजाइनरों ने एक सरल, आसानी से समझा जाने वाला नियम चुना: "बाएं-दाएं जाएं"। इसके लिए आपका प्रस्तावित प्रतिस्थापन क्या है, और आप क्यों मानते हैं कि आपका नियम बेहतर है?
एरिक लिपर्ट

32

एरिक लिपर्ट का उत्कृष्ट उत्तर फिर भी ठीक से सहायक नहीं है क्योंकि यह एक अलग भाषा के बारे में बात कर रहा है। यह जावा है, जहां जावा भाषा विनिर्देश शब्दार्थ का निश्चित विवरण है। विशेष रूप से, particular15.26.1 प्रासंगिक है क्योंकि यह =ऑपरेटर के लिए मूल्यांकन आदेश का वर्णन करता है (हम सभी जानते हैं कि यह सही-सहयोगी है, हाँ?)। इस सवाल के बारे में परवाह करने वाले बिट्स के लिए इसे थोड़ा नीचे काटना:

यदि बाएं हाथ का ऑपरेंड एक्सप्रेशन एक एरे एक्सेस एक्सेस ( 315.13 ) है, तो कई चरणों की आवश्यकता होती है:

  • सबसे पहले, बाएं हाथ के ऑपरेंड ऐरे एक्सेस एक्सप्रेशन का एरे संदर्भ सबमिशन का मूल्यांकन किया जाता है। यदि यह मूल्यांकन अचानक पूरा हो जाता है, तो असाइनमेंट की अभिव्यक्ति उसी कारण से अचानक पूरी हो जाती है; अनुक्रमणिका उपपरिवर्तन (बाएँ हाथ के ऑपरेंड एरे एक्सेस एक्सप्रेशन के) और दाएं हाथ के ऑपरेंड का मूल्यांकन नहीं किया जाता है और कोई असाइनमेंट नहीं होता है।
  • अन्यथा, बाएं हाथ के ऑपरेंड ऐरे एक्सेस एक्सप्रेशन के इंडेक्स सबएक्प्रेशन का मूल्यांकन किया जाता है। यदि यह मूल्यांकन अचानक पूरा हो जाता है, तो असाइनमेंट एक्सप्रेशन उसी कारण से अचानक पूरा हो जाता है और राइट-हैंड ऑपरेंड का मूल्यांकन नहीं होता है और कोई असाइनमेंट नहीं होता है।
  • अन्यथा, दाहिने हाथ के ऑपरेंड का मूल्यांकन किया जाता है। यदि यह मूल्यांकन अचानक पूरा हो जाता है, तो असाइनमेंट एक्सप्रेशन उसी कारण से अचानक पूरा हो जाता है और कोई असाइनमेंट नहीं होता है।

[... यह तब असाइनमेंट के वास्तविक अर्थ का वर्णन करता है, जिसे हम संक्षिप्तता के लिए यहाँ अनदेखा कर सकते हैं ...]

संक्षेप में, जावा में बहुत बारीकी से परिभाषित मूल्यांकन क्रम होता है जो किसी भी ऑपरेटर या विधि कॉल के तर्कों के भीतर बहुत ही बिल्कुल बाएं से दाएं होता है। सरणी असाइनमेंट अधिक जटिल मामलों में से एक है, लेकिन वहां भी यह अभी भी L2R है। (जेएलएस यह अनुशंसा करता है कि आप ऐसे कोड न लिखें जो इस प्रकार के जटिल अर्थ संबंधी अवरोधों की आवश्यकता है , और इसलिए मैं: आप प्रति कथन केवल एक असाइनमेंट के साथ पर्याप्त से अधिक परेशानी में पड़ सकते हैं!)

इस क्षेत्र में सी और सी ++ निश्चित रूप से जावा से भिन्न हैं: उनकी भाषा की परिभाषा अधिक अनुकूलन को सक्षम करने के लिए मूल्यांकन आदेश को जानबूझकर अपरिभाषित छोड़ देती है। सी # जाहिरा तौर पर जावा की तरह है, लेकिन मैं इसके साहित्य को अच्छी तरह से नहीं जानता कि औपचारिक परिभाषा को इंगित करने में सक्षम होना चाहिए। (यह वास्तव में भाषा से भिन्न होता है, हालांकि, रूबी सख्ती से L2R है, जैसा कि Tcl है - हालांकि यहां प्रासंगिक कारणों के लिए प्रति se असाइनमेंट ऑपरेटर का अभाव है - और पायथन L2R है लेकिन असाइनमेंट के संबंध में R2L है , जो मुझे अजीब लगता है, लेकिन आप वहां जाते हैं ।)


11
तो आप जो कह रहे हैं वह एरिक का जवाब गलत है क्योंकि जावा इसे विशेष रूप से परिभाषित करता है जैसा कि उसने कहा था?
विन्यासकर्ता

8
जावा (और C #) में प्रासंगिक नियम "सबसेक्स को बाएं से दाएं मूल्यांकन किया जाता है" - मुझे लगता है जैसे वह दोनों के बारे में बात कर रहा है।
विन्यासकर्ता

2
यहाँ थोड़ा उलझन में है - क्या यह एरिक लिपर्ट द्वारा उपरोक्त उत्तर को कम सच बनाता है, या यह केवल एक विशिष्ट संदर्भ का हवाला दे रहा है कि यह सत्य क्यों है?
ग्रीनमीनी

5
@ग्रीन: एरिक का जवाब सच है, लेकिन जैसा कि मैंने कहा कि आप इस क्षेत्र में एक भाषा से अंतर्दृष्टि नहीं ले सकते हैं और सावधान रहें बिना इसे दूसरे पर लागू कर सकते हैं। इसलिए मैंने निश्चित स्रोत का हवाला दिया।
डोनल फैलो

1
विषम बात है, बाएं हाथ के चर को हल करने से पहले दाहिने हाथ का मूल्यांकन किया जाता है; में a[-1]=c, cमूल्यांकन किया जाता है, इससे पहले -1कि अमान्य के रूप में मान्यता प्राप्त है।
झोंगयू

5
a[b] = b = 0;

1) एरे इंडेक्सिंग ऑपरेटर में उच्चता होती है, फिर असाइनमेंट ऑपरेटर ( यह उत्तर देखें ):

(a[b]) = b = 0;

2) 15.26 के अनुसार। JLS के असाइनमेंट ऑपरेटर

12 असाइनमेंट ऑपरेटर हैं; सभी वाक्यात्मक रूप से दाएं-साहचर्य हैं (वे दाएं-बाएं समूह)। इस प्रकार, a = b = c का अर्थ है a = (b = c), जो c से b का मान प्रदान करता है और फिर b से a का मान प्रदान करता है।

(a[b]) = (b=0);

3) 15.7 के अनुसार। जेएलएस का मूल्यांकन आदेश

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

तथा

बाइनरी ऑपरेटर के बाएं हाथ के ऑपरेंड का मूल्यांकन पूरी तरह से किया जाता है, राइट-हैंड ऑपरेंड के किसी भी भाग का मूल्यांकन करने से पहले।

इसलिए:

a) (a[b])पहले मूल्यांकन किया गयाa[1]

b) फिर (b=0)मूल्यांकन किया गया0

ग) (a[1] = 0)अंतिम मूल्यांकन किया गया


1

आपका कोड इसके बराबर है:

int[] a = {4,4};
int b = 1;
c = b;
b = 0;
a[c] = b;

जो परिणाम की व्याख्या करता है।


7
सवाल यह है कि ऐसा क्यों है।
Mat

@ उत्तर इसका कारण यह है कि प्रश्न में दिए गए कोड को ध्यान में रखते हुए ऐसा होता है। इस तरह मूल्यांकन होता है।
जेरोमे वर्स्ट्राइनेज जुले

1
हाँ मैं जानता हूँ। अभी भी इस सवाल का जवाब नहीं दे रहा है कि आईएमओ, यही वजह है कि मूल्यांकन कैसे होता है।
Mat

1
@ मेत 'ऐसा क्यों होता है मूल्यांकन?' पूछा गया सवाल नहीं है। 'मूल्यांकन कैसे हुआ?' पूछा गया प्रश्न है।
जेरोमे वर्स्ट्रीजेज

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

0

नीचे एक और अधिक गहराई से उदाहरण पर विचार करें।

अँगूठे के सामान्य नियम के रूप में:

इन प्रश्नों को हल करते समय पढ़ने के लिए ऑर्डर ऑफ प्रीएडेंस रूल्स और एसोसिएटिटी की तालिका उपलब्ध होना सबसे अच्छा है जैसे http://introcs.cs.princeton.edu/java/11precedence/

यहाँ एक अच्छा उदाहरण है:

System.out.println(3+100/10*2-13);

प्रश्न: उपरोक्त लाइन का आउटपुट क्या है?

उत्तर: वरीयता और सहकारिता के नियम लागू करें

चरण 1: पूर्वता के नियमों के अनुसार: / और * ऑपरेटर + - ऑपरेटर पर प्राथमिकता लेते हैं। इसलिए इस समीकरण को निष्पादित करने के लिए प्रारंभिक बिंदु निम्न तक सीमित होगा:

100/10*2

चरण 2: नियमों और पूर्वता के अनुसार: / और * पूर्वता में समान हैं।

जैसा कि / और * ऑपरेटर पूर्वता में समान हैं, हमें उन ऑपरेटरों के बीच की संबद्धता को देखने की जरूरत है।

इन दो विशेष परिचालकों के ASSOCIATIVITY RULES के अनुसार, हम LEFT TO RIGHT से समीकरण निष्पादित करना शुरू करते हैं अर्थात 100/10 पहले निष्पादित होता है:

100/10*2
=100/10
=10*2
=20

चरण 3: समीकरण अब निष्पादन की निम्नलिखित स्थिति में है:

=3+20-13

नियमों और पूर्वता के अनुसार: + और - पूर्वता में समान हैं।

अब हमें ऑपरेटर्स + और - ऑपरेटर्स के बीच की तालमेल को देखने की जरूरत है। इन दो विशेष संचालकों की सहानुभूति के अनुसार, हम बाएँ से दाएँ समीकरण को निष्पादित करना शुरू करते हैं अर्थात 3 + 20 को पहले निष्पादित किया जाता है:

=3+20
=23
=23-13
=10

संकलित होने पर 10 सही आउटपुट है

इन प्रश्नों को हल करते समय फिर से, ऑर्डर ऑफ प्रीएडेंस रूल्स और एसोसिएटिविटी की एक टेबल रखना महत्वपूर्ण है जैसे http://introcs.cs.princeton.edu/java/11precedence/


1
आप कह रहे हैं कि "ऑपरेटर्स + और ऑपरेटर्स के बीच की सहानुभूति" "राइट टू लेफ्ट" है। उस तर्क का उपयोग करने और मूल्यांकन करने का प्रयास करें 10 - 4 - 3
Pshemo

1
मुझे संदेह है कि यह गलती इस तथ्य के कारण हो सकती है कि introcs.cs.princeton.edu/java/11precedence के शीर्ष पर एकरी+ ऑपरेटर (जिसमें राइट टू लेफ्ट एसेटिविटी है), लेकिन एडिटिव + और - अभी गुणा * /% बचा है समरसता के लिए।
Pshemo

अच्छी तरह से देखा और तदनुसार संशोधित, आप का धन्यवाद Pshemo
मार्क Burleigh

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