अल्पविराम के साथ टर्नरी ऑपरेटर सच्चे मामले में केवल एक अभिव्यक्ति का मूल्यांकन क्यों करता है?


119

मैं वर्तमान में C ++ प्राइमर पुस्तक के साथ C ++ सीख रहा हूं और पुस्तक में एक अभ्यास है:

बताएं कि निम्नलिखित अभिव्यक्ति क्या करती है: someValue ? ++x, ++y : --x, --y

हम क्या जानते हैं? हम जानते हैं कि टर्नरी ऑपरेटर की कॉमा ऑपरेटर की तुलना में अधिक पूर्वता है। बाइनरी ऑपरेटरों के साथ यह समझने में काफी आसान था, लेकिन टर्नरी ऑपरेटर के साथ मैं थोड़ा संघर्ष कर रहा हूं। बाइनरी ऑपरेटरों के साथ "उच्च पूर्वता होने" का अर्थ है कि हम अभिव्यक्ति के चारों ओर कोष्ठक का उपयोग उच्च वरीयता के साथ कर सकते हैं और यह निष्पादन को नहीं बदलेगा।

टर्नरी ऑपरेटर के लिए मैं यह करूंगा:

(someValue ? ++x, ++y : --x, --y)

प्रभावी रूप से उसी कोड के परिणामस्वरूप जो मुझे समझने में मदद नहीं करता है कि कंपाइलर कोड को कैसे समूहित करेगा।

हालाँकि, C ++ कंपाइलर के परीक्षण से मुझे पता है कि अभिव्यक्ति संकलित है और मुझे नहीं पता कि एक :ऑपरेटर खुद के लिए क्या खड़ा कर सकता है। तो संकलक को टर्नेरी ऑपरेटर की सही व्याख्या करना प्रतीत होता है।

फिर मैंने कार्यक्रम को दो तरीकों से अंजाम दिया:

#include <iostream>

int main()
{
    bool someValue = true;
    int x = 10, y = 10;

    someValue ? ++x, ++y : --x, --y;

    std::cout << x << " " << y << std::endl;
    return 0;
}

का परिणाम:

11 10

जबकि दूसरी ओर इसके साथ someValue = falseप्रिंट:

9 9

C ++ कंपाइलर कोड क्यों उत्पन्न करेगा, जो कि टर्नरी ऑपरेटर की सच्ची शाखा के लिए केवल वेतन वृद्धि है x, जबकि टर्नरी की झूठी-शाखा के लिए यह दोनों को घटाता है xऔर y?

मैं भी इस तरह से सच्ची शाखा के चारों ओर कोष्ठक डालने के रूप में दूर चला गया:

someValue ? (++x, ++y) : --x, --y;

लेकिन इसका परिणाम अभी भी है 11 10


5
"वरीयता" C ++ में केवल एक आकस्मिक घटना है। यह केवल भाषा व्याकरण को सीधे देखने और अभिव्यक्ति कैसे काम करती है, इसे देखने के लिए सरल हो सकता है।
केरेक एसबी

26
हम परवाह नहीं है कि सिद्धांतों के बारे में ज्यादा। :-) तथ्य यह है कि आपको यह पूछना होगा कि यह संकेत देता है कि कोड आपके साथी प्रोग्रामर द्वारा कोड समीक्षा पास करने वाला नहीं है। यह इस बारे में ज्ञान बनाता है कि यह वास्तव में उपयोगी से कम कैसे काम करता है। जब तक आप बेशक, सी प्रतियोगिता में भाग लेना चाहते हैं ।
बो पर्सन

5
@BoPersson इस तरह के उदाहरणों से सीखते हैं, भविष्य के समीक्षक कभी नहीं सीखेंगे कि उन्हें उत्पादन से इसे क्यों अस्वीकार करना चाहिए।
लेउशेंको

8
@Leushenko - चेतावनी की घंटी वैसे भी बजनी चाहिए। एक ही बयान में कई वेतन वृद्धि और गिरावट (डिंग, डिंग, डिंग!)। जब आप (-डिंग, डिंग, डिंग!) का उपयोग कर सकते हैं तो एक टर्नरी ऑपरेटर रुको, क्या वे अल्पविराम अल्पविराम संचालक हैं? (डिंग, डिंग, डिंग!) उन सभी ऑपरेटरों के साथ, क्या कोई पूर्ववर्ती चीज हो सकती है? (डिंग, डिंग, डिंग!) इसलिए हम कभी भी इसका उपयोग करने में सक्षम नहीं होंगे। तो क्यों समय बर्बाद करने लगा कि यह क्या करता है, अगर कुछ भी हो?
बो पर्सन

4
माइनर निट: के लिए नाम ?है सशर्त ऑपरेटरटर्नेरी ऑपरेटर शब्द का अर्थ है तीन ऑपरेटर वाला ऑपरेटर। सशर्त ऑपरेटर एक टर्नरी ऑपरेटर का एक उदाहरण है , लेकिन एक भाषा (सैद्धांतिक रूप से) में कई टर्नरी ऑपरेटर हो सकते हैं।
बटा

जवाबों:


122

जैसा कि @Rete ने अपने उत्कृष्ट उत्तर में कहा, यह मुश्किल है। मैं उस पर थोड़ा जोड़ना चाहूंगा।

टर्नरी ऑपरेटर के पास फॉर्म होना चाहिए:

तार्किक-या-अभिव्यक्ति ? अभिव्यक्ति : असाइनमेंट-अभिव्यक्ति

तो हमारे पास निम्न मैपिंग हैं:

  • someValue: तार्किक-या-अभिव्यक्ति
  • ++x, ++y: अभिव्यक्ति
  • ??? है काम अभिव्यक्ति --x, --y केवल या --x?

वास्तव में यह केवल --xइसलिए है क्योंकि एक असाइनमेंट एक्सप्रेशन को कॉमा द्वारा अलग किए गए दो एक्सप्रेशन के रूप में पार्स नहीं किया जा सकता है (C ++ के व्याकरण नियमों के अनुसार), इसलिए असाइनमेंट एक्सप्रेशन के--x, --y रूप में नहीं माना जा सकता है ।

इस तरह से देखने के लिए टर्नरी (सशर्त) अभिव्यक्ति भाग का परिणाम होता है:

someValue?++x,++y:--x

यह पठनीयता के लिए मदद करने के लिए विचार ++x,++yकरने के लिए गणना की जा सकती है जैसे कि अगर कोष्ठक (++x,++y); बीच में कुछ भी शामिल है ?और सशर्त के बाद: अनुक्रम किया जाएगा । (मैं उन्हें बाकी पोस्ट के लिए संक्षिप्त करूँगा)।

और इस क्रम में मूल्यांकन किया गया:

  1. someValue?
  2. (++x,++y)या --x( bool1. के परिणाम पर निर्भर करता है )

इस अभिव्यक्ति को तब अल्पविराम ऑपरेटर के रूप में छोड़ दिया जाता है, सही उप-अभिव्यक्ति के साथ --y, जैसे:

(someValue?(++x,++y):--x), --y;

जिसका अर्थ है कि बाईं ओर एक त्याग-मूल्य अभिव्यक्ति है , जिसका अर्थ है कि यह निश्चित रूप से मूल्यांकन किया गया है, लेकिन फिर हम सही पक्ष का मूल्यांकन करते हैं और उसे वापस करते हैं।

तो क्या होता है जब someValueहै true?

  1. (someValue?(++x,++y):--x)निष्पादित और वृद्धि xऔर yहोने के लिए 11और11
  2. बाईं अभिव्यक्ति को छोड़ दिया गया है (हालांकि वेतन वृद्धि के दुष्प्रभाव बने हुए हैं)
  3. हम कॉमा ऑपरेटर के दाहिने हाथ की ओर का मूल्यांकन करते हैं: --yजो तब yवापस घटता है10

"ठीक" करने के लिए व्यवहार में, आप समूह कर सकते हैं --x, --yकोष्ठकों के साथ एक में बदलने के लिए प्राथमिक अभिव्यक्ति जो है एक के लिए एक मान्य प्रविष्टि काम अभिव्यक्ति *:

someValue?++x,++y:(--x, --y);

* यह एक बल्कि अजीब लंबी श्रृंखला है जो एक असाइनमेंट-एक्सप्रेशन को एक प्राथमिक अभिव्यक्ति से जोड़ता है :

असाइनमेंट-एक्सप्रेशन --- (शामिल हो सकता है) -> सशर्त-अभिव्यक्ति -> तार्किक-या-अभिव्यक्ति -> तार्किक-और-अभिव्यक्ति -> समावेशी-या-अभिव्यक्ति -> अनन्य-या-अभिव्यक्ति - -> और अभिव्यक्ति -> समानता-अभिव्यक्ति -> संबंधपरक अभिव्यक्ति -> बदलाव-अभिव्यक्ति -> योगात्मक-अभिव्यक्ति -> गुणन-अभिव्यक्ति -> दोपहर-अभिव्यक्ति -> कास्ट-अभिव्यक्ति -> एकात्मक-अभिव्यक्ति -> उपसर्ग-अभिव्यक्ति -> प्राथमिक-अभिव्यक्ति


10
व्याकरण के नियमों को उजागर करने की परेशानी उठाने के लिए धन्यवाद; ऐसा करने से पता चलता है कि अधिकांश पाठ्यपुस्तकों में C ++ के व्याकरण की तुलना में अधिक है।
श्रीधाम

4
@sdenham: जब लोग पूछते हैं कि "अभिव्यक्ति उन्मुख भाषाएं" अच्छी क्यों हैं (अर्थात जब { ... }एक अभिव्यक्ति के रूप में माना जा सकता है), तो मेरे पास अब एक जवाब है => यह अल्पविराम ऑपरेटर को पेश करने से बचने के लिए है जो इस तरह के मुश्किल तरीकों से व्यवहार करता है।
मैथ्यू एम।

क्या आप मुझे assignment-expressionश्रृंखला के बारे में पढ़ने के लिए एक लिंक दे सकते हैं ?
MiP

@MiP: मैंने इसे स्वयं मानक से उठा लिया, आप इसे gram.expr
AndyG

88

वाह, यह मुश्किल है।

संकलक आपकी अभिव्यक्ति को इस रूप में देखता है:

(someValue ? (++x, ++y) : --x), --y;

टर्नरी ऑपरेटर को एक की आवश्यकता होती है :, यह उस संदर्भ में स्वयं नहीं खड़ा हो सकता है, लेकिन इसके बाद, कोई कारण नहीं है कि अल्पविराम झूठे मामले से संबंधित होना चाहिए।

अब यह अधिक समझ में आता है कि आपको वह आउटपुट क्यों मिलता है। अगर someValueसच है, तो ++x, ++yऔर --yनिष्पादित करें, जो प्रभावी रूप से नहीं बदलता है yलेकिन एक को जोड़ता है x

यदि someValueगलत है, तो --xऔर --yक्रियान्वित कर रहे हैं, उन दोनों को एक-एक करके decrementing।


42

C ++ कंपाइलर कोड क्यों उत्पन्न करेगा जो कि टर्नरी ऑपरेटर की सच्ची शाखा के लिए केवल वेतन वृद्धि है x

आपने गलत समझा है कि क्या हुआ है। सच्चा-शाखा वेतन वृद्धि दोनों xऔर y। हालांकि, yउसके तुरंत बाद बिना शर्त के डिक्रिप्ट किया गया है।

यहाँ यह है कि ऐसा कैसे होता है: चूंकि सशर्त ऑपरेटर C ++ में अल्पविराम ऑपरेटर की तुलना में अधिक पूर्वता रखते हैं , इसलिए संकलक अभिव्यक्ति को इस प्रकार बताता है:

   (someValue ? ++x, ++y : --x), (--y);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^  ^^^^^

--yअल्पविराम के बाद "अनाथ" पर ध्यान दें । यह वही है जो yशुरू में वेतन वृद्धि की ओर जाता है ।

मैं भी इस तरह से सच्ची शाखा के चारों ओर कोष्ठक डालने के रूप में दूर चला गया:

someValue ? (++x, ++y) : --x, --y;

आप सही रास्ते पर थे, लेकिन आपने एक गलत शाखा को छोटा कर दिया: आप इसे अन्य शाखा की तरह ही छोटा करके इसे ठीक कर सकते हैं:

someValue ? ++x, ++y : (--x, --y);

डेमो (प्रिंट 11 11)


5

आपकी समस्या यह है कि टर्नरी अभिव्यक्ति वास्तव में कॉमा की तुलना में अधिक पूर्वता नहीं रखती है। वास्तव में, C ++ को केवल पूर्वता के अनुसार सटीक रूप से वर्णित नहीं किया जा सकता है - और यह त्रैमासिक ऑपरेटर और अल्पविराम के बीच की बातचीत है जहां यह टूट जाता है।

a ? b++, c++ : d++

के रूप में इलाज किया जाता है:

a ? (b++, c++) : d++

(अल्पविराम ऐसा व्यवहार करता है जैसे कि इसकी उच्चता है)। दूसरी ओर,

a ? b++ : c++, d++

के रूप में इलाज किया जाता है:

(a ? b++ : c++), d++

और टर्नरी ऑपरेटर उच्च पूर्वता है।


मुझे लगता है कि यह अभी भी पूर्वता के दायरे में है क्योंकि मध्य रेखा के लिए केवल एक वैध पार्स है, है ना? फिर भी एक उपयोगी उदाहरण हालांकि
sudo rm -rf slash

2

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

तो, बड़ा संदर्भ यह होगा:

whatIreallyWanted = someValue ? ++x, ++y : --x, --y;

जो अपने चेहरे पर बेतुका है, इसलिए अपराध कई गुना हैं:

  • भाषा एक असाइनमेंट में हास्यास्पद दुष्प्रभावों की अनुमति देती है।
  • संकलक ने आपको चेतावनी नहीं दी कि आप विचित्र चीजें कर रहे थे।
  • पुस्तक 'ट्रिक' प्रश्नों पर ध्यान केंद्रित करती प्रतीत होती है। कोई केवल यह आशा कर सकता है कि पीठ में उत्तर था "यह अभिव्यक्ति क्या करती है यह एक आकस्मिक उदाहरण में साइड इफेक्ट्स का उत्पादन करने के लिए एक अजीब उदाहरण के मामलों पर निर्भर करता है जो किसी को उम्मीद नहीं है। कभी भी ऐसा न करें।"

1
दो चर में से एक को सौंपना टर्नरी ऑपरेटर का सामान्य मामला है, लेकिन ऐसे मौके होते हैं जब अभिव्यक्ति के रूप में उपयोगी होता है if(उदाहरण के लिए, लूप के लिए वेतन वृद्धि)। बड़ा संदर्भ अच्छी तरह for (x = 0, y=0; x+y < 100; someValue?(++x, ++y) :( --x, --y))से एक लूप के साथ हो सकता है जो संशोधित xऔर yस्वतंत्र रूप से हो सकता है।
मार्टिन बोनर

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