C के टर्नीरी ऑपरेटर के मुहावरेदार गो क्या है?


295

C / C ++ (और उस परिवार की कई भाषाएं) में, एक शर्त के आधार पर एक वैरिएबल को घोषित करने और आरंभ करने के लिए एक सामान्य मुहावरा, टर्नरी सशर्त ऑपरेटर का उपयोग करता है:

int index = val > 0 ? val : -val

गो के पास सशर्त ऑपरेटर नहीं है। उपरोक्त के समान कोड को लागू करने का सबसे मुहावरेदार तरीका क्या है? मैं निम्नलिखित समाधान के लिए आया था, लेकिन यह काफी क्रियात्मक लगता है

var index int

if val > 0 {
    index = val
} else {
    index = -val
}

क्या कुछ बेहतर है?


आप किसी अन्य भाग के साथ मान को इनिशियलाइज़ कर सकते हैं और केवल अपनी स्थिति को बदलने के लिए जाँच कर सकते हैं, यह सुनिश्चित नहीं करते कि यह बेहतर है
x29a

बहुत सारे if / thens को वैसे भी खत्म कर दिया जाना चाहिए था। हम उन दिनों से यह सब करते थे जब मैंने 35 साल पहले अपना पहला BASIC कार्यक्रम लिखा था। आपका उदाहरण यह हो सकता है: int index = -val + 2 * val * (val > 0);
hyc

8
@ आपका उदाहरण गो के मुहावरेदार कोड के रूप में पठनीय होने से, या यहां तक ​​कि सी के संस्करण के रूप में पढ़ने योग्य ऑपरेटर का उपयोग करने से दूर है। वैसे भी, AFAIK, गो में इस समाधान को लागू करना संभव नहीं है क्योंकि बूलियन को संख्यात्मक मान के रूप में उपयोग नहीं किया जा सकता है।
फाबिएन

आश्चर्य है कि जाने क्यों इस तरह के एक ऑपरेटर प्रदान नहीं किया?
एरिक वांग

@ EricWang दो कारण, AFAIK: 1- आपको इसकी आवश्यकता नहीं है, और वे भाषा को यथासंभव छोटा रखना चाहते थे। 2- इसका दुरुपयोग किया जाता है, अर्थात विभिन्न बहु-पंक्ति अभिव्यक्तियों में इसका उपयोग किया जाता है, और भाषा डिजाइनर इसे पसंद नहीं करते हैं।
फबिएन

जवाबों:


242

जैसा कि बताया गया है (और उम्मीद है कि अनजाने में), गो में सशर्त करने के लिए if+elseवास्तव में मुहावरेदार तरीका है।

var+if+elseकोड के पूर्ण विकसित ब्लॉक के अलावा , हालांकि, इस वर्तनी का उपयोग अक्सर किया जाता है:

index := val
if val <= 0 {
    index = -val
}

और यदि आपके पास कोड का एक ब्लॉक है जो दोहराव के लिए पर्याप्त है, जैसे कि बराबर है int value = a <= b ? a : b, तो आप इसे रखने के लिए एक फ़ंक्शन बना सकते हैं:

func min(a, b int) int {
    if a <= b {
        return a
    }
    return b
}

...

value := min(a, b)

कंपाइलर ऐसे सरल कार्यों को इनलाइन करेगा, इसलिए यह तेज़, अधिक स्पष्ट और छोटा है।


183
हे दोस्तों, देखो! मैंने गॉलंग्स को टर्नारिटी ऑपरेटर को चित्रित किया! play.golang.org/p/ZgLwC_DHm0इतना कुशल!
thwd

28
@tomwilde आपका समाधान काफी दिलचस्प लग रहा है, लेकिन इसमें टर्नरी ऑपरेटर की मुख्य विशेषताओं में से एक का अभाव है - सशर्त मूल्यांकन।
व्लादिमीर मतावेव

12
@VladimirMatveev बंद मूल्यों में लपेट;)
nemo

55
c := (map[bool]int{true: a, false: a - 1})[a > b]भले ही यह काम करता है, आईएमएफएचओ का उदाहरण है।
रिक -777

34
यदि if/elseमुहावरेदार दृष्टिकोण है, तो शायद गोलंग if/elseएक मान को लौटाने देने पर विचार कर सकता है x = if a {1} else {0}:। गो इस तरह से काम करने वाली एकमात्र भाषा नहीं होगी। एक मुख्यधारा का उदाहरण स्काला है। देखें: alvinalexander.com/scala/scala-ternary-operator-syntax
अधिकतम मर्फी

80

कोई जाओ एक त्रिगुट ऑपरेटर नहीं है, अगर / बाकी सिंटैक्स का उपयोग है मुहावरेदार रास्ता।

गो के पास क्यों नहीं है ?: ऑपरेटर?

गो में कोई टेनेरी टेस्टिंग ऑपरेशन नहीं है। आप समान परिणाम प्राप्त करने के लिए निम्नलिखित का उपयोग कर सकते हैं:

if expr {
    n = trueVal
} else {
    n = falseVal
}

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

- अक्सर पूछे जाने वाले प्रश्न (एफएक्यू) - गो प्रोग्रामिंग भाषा


1
तो सिर्फ इसलिए कि भाषा डिजाइनरों ने जो देखा है, उन्होंने पूरे if-elseब्लॉक के लिए एक-लाइनर को छोड़ दिया है ? और कौन कहता if-elseहै कि इस तरह से दुर्व्यवहार नहीं किया जाता है? मैं आप पर हमला नहीं कर रहा हूं, मुझे सिर्फ यह लगता है कि डिजाइनरों द्वारा किया गया बहाना पर्याप्त वैध नहीं है
अल्फ मोह

58

मान लें कि आपके पास निम्नलिखित त्रैमासिक अभिव्यक्ति है (C में):

int a = test ? 1 : 2;

गो में मुहावरेदार दृष्टिकोण केवल एक ifब्लॉक का उपयोग करना होगा :

var a int

if test {
  a = 1
} else {
  a = 2
}

हालाँकि, यह आपकी आवश्यकताओं के अनुरूप नहीं हो सकता है। मेरे मामले में, मुझे एक कोड पीढ़ी टेम्पलेट के लिए इनलाइन अभिव्यक्ति की आवश्यकता थी।

मैंने तुरंत मूल्यांकन किए गए अनाम फ़ंक्शन का उपयोग किया:

a := func() int { if test { return 1 } else { return 2 } }()

यह सुनिश्चित करता है कि दोनों शाखाओं का मूल्यांकन नहीं किया गया है।


यह जानने के लिए अच्छा है कि इनलाइन फ़ंक्शन के केवल एक शाखा का मूल्यांकन किया जाता है। लेकिन ध्यान दें कि इस तरह के मामले सी के टर्नेरी ऑपरेटर के दायरे से परे हैं।
वुल्फ

1
सी सशर्त अभिव्यक्ति (आमतौर पर टर्नरी ऑपरेटर के रूप में जाना जाता है) में तीन ऑपरेंड होते हैं expr1 ? expr2 : expr3:। यदि expr1मूल्यांकन किया जाता है true, मूल्यांकन किया expr2जाता है और अभिव्यक्ति का परिणाम है। अन्यथा, expr3परिणाम के रूप में मूल्यांकन और प्रदान किया जाता है। यह KSI द्वारा ANSI C प्रोग्रामिंग लैंग्वेज सेक्शन 2.11 से है। मेरा गो समाधान इन विशिष्ट शब्दार्थों को संरक्षित करता है। क्या आप सुझाव दे रहे हैं?
पीटर बोयर

मुझे यकीन नहीं है कि मेरे मन में क्या था, हो सकता है कि एनोन फ़ंक्शंस एक गुंजाइश (स्थानीय नाम स्थान) प्रदान करे जो कि सी / सी ++ में टर्नरी ऑपरेटर के साथ नहीं है। इस दायरे का उपयोग करने के लिए
वुल्फ

39

कोष्ठक के बिना मैप टर्नेरी को पढ़ना आसान है:

c := map[bool]int{true: 1, false: 0} [5 > 4]

पूरी तरह से निश्चित नहीं है कि इसे -2 क्यों मिला है ... हां, यह वर्कअराउंड है लेकिन यह काम करता है और टाइप-सेफ है।
एलेसेंड्रो शांतििनी

30
हां, यह काम करता है, टाइप-सेफ है, और रचनात्मक भी है; हालाँकि, अन्य मेट्रिक्स हैं। टर्नरी ऑप्स रनटाइम समतुल्य हैं यदि / / (उदाहरण के लिए इस एस / ओ पोस्ट देखें )। यह प्रतिक्रिया नहीं है क्योंकि 1) दोनों शाखाओं को निष्पादित किया जाता है, 2) एक नक्शा बनाता है 3) एक हैश कहता है। ये सभी "तेज़" हैं, लेकिन उपवास के रूप में अगर / नहीं तो। इसके अलावा, मेरा तर्क है कि यह var r T से अधिक पठनीय नहीं है अगर हालत {r = foo ()} वरना {r = bar ()}
नाइट

अन्य भाषाओं में जब मैं एकाधिक चर और क्लोजर या फ़ंक्शन पॉइंटर्स या जंप के साथ इस दृष्टिकोण का उपयोग करता हूं। नेस्टेड लिखना यदि वैरिएबल की संख्या बढ़ने पर एरर प्रोन हो जाता है, जबकि {{0,0,0) => {कोड 1}, (0,0,1) => {कोड 2} ...} [(x> 1) , y> 1, z> 1)] (pseudocode) अधिक से अधिक आकर्षक हो जाता है, क्योंकि चर की संख्या बढ़ जाती है। क्लोजर इस मॉडल को तेज रखते हैं। मुझे उम्मीद है कि इसी तरह के ट्रेडऑफ लागू होते हैं।
मैक्स मर्फी

मुझे लगता है कि आप उस मॉडल के लिए एक स्विच का उपयोग करेंगे। मैं प्यार करता हूँ जिस तरह से स्विच स्वतः ही टूट जाता है, भले ही यह कभी-कभी असुविधाजनक हो।
मैक्स मर्फी

8
जैसा कि कैसी फ़ॉस्च ने बताया: simple and clear code is better than creative code.
वुल्फ

11
func Ternary(statement bool, a, b interface{}) interface{} {
    if statement {
        return a
    }
    return b
}

func Abs(n int) int {
    return Ternary(n >= 0, n, -n).(int)
}

यह आउटफ़ॉर्म नहीं होगा यदि / अन्यथा और कलाकारों की आवश्यकता है लेकिन काम करता है। जानकारी के लिए:

बेंचमार्कएब्सटर्नरी -8 100000000 18.8 ns / op

बेंचमार्कअफ्सइफ्लेसी -8 2000000000 0.27 ns / op


यह सबसे अच्छा समाधान है, बधाई! एक लाइन जो सभी संभावित मामलों को संभालती है
अलेक्जेंड्रो डी ओलिवेरा

2
मुझे नहीं लगता कि यह सशर्त मूल्यांकन को संभालता है, या करता है? साइड-इफेक्ट फ्री शाखाओं के साथ यह कोई फर्क नहीं पड़ता (जैसे आपके उदाहरण में), लेकिन यदि यह साइड-इफेक्ट के साथ कुछ है तो आप समस्याओं में भाग लेंगे।
एश्टन वाइर्सडॉर्फ

7

यदि आपकी सभी शाखाएँ साइड-इफ़ेक्ट करती हैं या कम्प्यूटेशनल रूप से महँगी हैं तो निम्नलिखित एक शब्दार्थ-संरक्षणकारी रिफैक्टरिंग होगा:

index := func() int {
    if val > 0 {
        return printPositiveAndReturn(val)
    } else {
        return slowlyReturn(-val)  // or slowlyNegate(val)
    }
}();  # exactly one branch will be evaluated

आम तौर पर कोई ओवरहेड (इनबिल्ड) और, सबसे महत्वपूर्ण बात, अपने नाम स्थान को एक सहायक कार्यों के साथ अव्यवस्थित किए बिना, जो केवल एक बार उपयोग किया जाता है (जो पठनीयता और रखरखाव को बाधित करता है)। लाइव उदाहरण

ध्यान दें कि क्या आप गुस्तावो के दृष्टिकोण को भली-भांति लागू करने के लिए थे :

    index := printPositiveAndReturn(val);
    if val <= 0 {
        index = slowlyReturn(-val);  // or slowlyNegate(val)
    }

आपको एक अलग व्यवहार के साथ एक कार्यक्रम मिलेगा ; यदि val <= 0ऐसा नहीं होना चाहिए तो मामले में एक गैर-सकारात्मक मूल्य मुद्रित होगा! (आमतौर पर, यदि आप शाखाओं को उलट देते हैं, तो आप अनावश्यक रूप से धीमी गति से कॉल करके ओवरहेड का परिचय देंगे।)


1
दिलचस्प पढ़ा, लेकिन मैं वास्तव में गुस्तावो के दृष्टिकोण की आपकी आलोचना के बिंदु को नहीं समझ रहा हूं। मैं एक (की तरह) को देखने के absमूल कोड (अच्छी तरह से, मैं बदल देंगे में समारोह <=के लिए <)। आपके उदाहरण में मुझे एक आरंभिकता दिखाई देती है, जो किसी मामले में निरर्थक है और इसका विस्तार हो सकता है। क्या आप कृपया स्पष्ट कर सकते हैं: अपने विचार को थोड़ा और स्पष्ट करें?
वुल्फ

मुख्य अंतर यह है कि किसी भी शाखा के बाहर एक फ़ंक्शन को कॉल करने से साइड इफेक्ट्स होंगे, भले ही उस शाखा को नहीं लिया गया हो। मेरे मामले में, केवल सकारात्मक संख्याएं ही प्रिंट की जाएंगी क्योंकि फ़ंक्शन printPositiveAndReturnको केवल सकारात्मक संख्याओं के लिए कहा जाता है। इसके विपरीत, हमेशा एक शाखा को निष्पादित करना, फिर एक अलग शाखा को निष्पादित करने के साथ मूल्य को "ठीक करना" पहली शाखा के दुष्प्रभावों को पूर्ववत नहीं करता है
eold

मैं देख रहा हूं, लेकिन अनुभव प्रोग्रामर आम तौर पर दुष्प्रभावों के बारे में जानते हैं। उस मामले में, मैं एक एम्बेडेड फ़ंक्शन में Cassy Foesch का स्पष्ट समाधान पसंद करूंगा , भले ही संकलित कोड समान हो सकता है: यह छोटा है और अधिकांश प्रोग्रामर को स्पष्ट दिखता है। मुझे गलत मत समझो: मुझे वास्तव में गो के बंद होने से प्यार है ;)
वुल्फ

1
" अनुभव प्रोग्रामर आमतौर पर साइड इफेक्ट्स के बारे में जानते हैं " - नहीं। शर्तों के मूल्यांकन से बचना एक टर्नरी ऑपरेटर की प्राथमिक विशेषताओं में से एक है।
जोनाथन हार्टले

6

प्राक्कथन: यह तर्क दिए बिना कि if elseजाने का तरीका है, हम अभी भी भाषा-सक्षम निर्माणों में आनंद ले सकते हैं।

निम्न Ifनिर्माण मेरे github.com/icza/goxपुस्तकालय में कई अन्य विधियों के साथ उपलब्ध है , builtinx.Ifप्रकार है।


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

कुछ इस तरह:

type If bool

func (c If) Int(a, b int) int {
    if c {
        return a
    }
    return b
}

हम इसका उपयोग कैसे कर सकते हैं?

i := If(condition).Int(val1, val2)  // Short variable declaration, i is of type int
     |-----------|  \
   type conversion   \---method call

उदाहरण के लिए एक टर्नरी कर max():

i := If(a > b).Int(a, b)

एक टर्नरी कर abs():

i := If(a >= 0).Int(a, -a)

यह अच्छा लग रहा है, यह सरल, सुरुचिपूर्ण और कुशल है (यह इनलाइनिंग के लिए भी योग्य है )।

एक "वास्तविक" टर्नरी ऑपरेटर की तुलना में एक नकारात्मक पहलू: यह हमेशा सभी ऑपरेंड का मूल्यांकन करता है।

आस्थगित और केवल-अगर-आवश्यक मूल्यांकन प्राप्त करने के लिए, एकमात्र विकल्प फ़ंक्शंस का उपयोग करना है (या तो घोषित फ़ंक्शंस या विधियाँ, या फ़ंक्शन शाब्दिक ), जिन्हें केवल जब / यदि आवश्यक हो तो कहा जाता है:

func (c If) Fint(fa, fb func() int) int {
    if c {
        return fa()
    }
    return fb()
}

इसका उपयोग करना: मान लें कि गणना करने के लिए हमारे पास ये कार्य हैं aऔर b:

func calca() int { return 3 }
func calcb() int { return 4 }

फिर:

i := If(someCondition).Fint(calca, calcb)

उदाहरण के लिए, स्थिति वर्तमान वर्ष> 2020:

i := If(time.Now().Year() > 2020).Fint(calca, calcb)

अगर हम फंक्शन शाब्दिक का उपयोग करना चाहते हैं:

i := If(time.Now().Year() > 2020).Fint(
    func() int { return 3 },
    func() int { return 4 },
)

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

उदाहरण के लिए अगर calca()और calcb()भी पैरामीटर (वापसी मूल्य के अलावा) होगा:

func calca2(x int) int { return 3 }
func calcb2(x int) int { return 4 }

इस तरह आप उनका उपयोग कर सकते हैं:

i := If(time.Now().Year() > 2020).Fint(
    func() int { return calca2(0) },
    func() int { return calcb2(0) },
)

गो प्लेग्राउंड पर इन उदाहरणों को आज़माएं ।


4

ईल्ड का जवाब दिलचस्प और रचनात्मक है, शायद चतुर भी।

हालांकि, इसके बजाय करने की सिफारिश की जाएगी:

var index int
if val > 0 {
    index = printPositiveAndReturn(val)
} else {
    index = slowlyReturn(-val)  // or slowlyNegate(val)
}

हां, वे दोनों अनिवार्य रूप से एक ही विधानसभा के लिए संकलित करते हैं, हालांकि यह कोड केवल एक मूल्य को वापस करने के लिए एक अनाम फ़ंक्शन को कॉल करने की तुलना में अधिक सुपाठ्य है जो पहले स्थान पर चर को लिखा जा सकता था।

मूल रूप से, सरल और स्पष्ट कोड रचनात्मक कोड से बेहतर है।

इसके अतिरिक्त, मैप शाब्दिक का उपयोग करने वाला कोई भी कोड एक अच्छा विचार नहीं है, क्योंकि मैप्स गो में बिल्कुल भी हल्के नहीं हैं। 1.3 के बाद से, छोटे नक्शे के लिए यादृच्छिक पुनरावृत्ति क्रम की गारंटी है, और इसे लागू करने के लिए, यह छोटे नक्शे के लिए काफी कम कुशल मेमोरी-वार हो गया है।

परिणामस्वरूप, कई छोटे मानचित्र बनाना और हटाना अंतरिक्ष-खपत और समय लेने वाली दोनों हैं। मेरे पास कोड का एक टुकड़ा था जो एक छोटे नक्शे (दो या तीन कुंजी का उपयोग करता था, संभावना है, लेकिन सामान्य उपयोग का मामला केवल एक प्रविष्टि था) लेकिन कोड कुत्ते का धीमा था। हम दोहरी कोड कुंजी [इंडेक्स] => डेटा [इंडेक्स] मैप का उपयोग करने के लिए दोबारा लिखे गए कोड की तुलना में कम से कम ३ ऑर्डर की बात कर रहे हैं। और संभावना अधिक थी। जैसा कि कुछ ऑपरेशनों को चलाने में पहले कुछ मिनट लगते थे, मिलीसेकंड में पूरा होने लगा। \ _


1
simple and clear code is better than creative code- यह मुझे बहुत पसंद है, लेकिन मैं आखिरी खंड में थोड़ा भ्रमित हो रहा हूं dog slow, शायद यह दूसरों को भी भ्रमित कर सकता है?
वुल्फ

1
तो, मूल रूप से ... मेरे पास कुछ कोड थे जो एक, दो, या तीन प्रविष्टियों के साथ छोटे नक्शे बना रहे थे, लेकिन कोड बहुत धीमी गति से चल रहा था। तो, एक बहुत कुछ है m := map[string]interface{} { a: 42, b: "stuff" }, और फिर इसके माध्यम से चलने वाले दूसरे फ़ंक्शन में: for key, val := range m { code here } दो स्लाइस सिस्टम पर स्विच करने के बाद: keys = []string{ "a", "b" }, data = []interface{}{ 42, "stuff" }और फिर for i, key := range keys { val := data[i] ; code here }1000 गुना ऊपर की चीजों की तरह पुनरावृति ।
कैसी फ़ॉस्च

मैं देखता हूं, स्पष्टीकरण के लिए धन्यवाद। (हो सकता है कि इस उत्तर में ही उत्तर को बेहतर बनाया जा सके ।)
वुल्फ

1
-.- ... टच, लॉजिक ... टच ... मैं आखिरकार उस पर
मिलूंगा

3

एक-लाइनर्स, हालांकि रचनाकारों द्वारा चौंक गए, उनका स्थान है।

यह आपको आलसी मूल्यांकन समस्या हल करने देता है जिससे आप, वैकल्पिक रूप से, यदि आवश्यक हो तो मूल्यांकन किए जाने वाले कार्यों को पास कर सकते हैं:

func FullTernary(e bool, a, b interface{}) interface{} {
    if e {
        if reflect.TypeOf(a).Kind() == reflect.Func {
            return a.(func() interface{})()
        }
        return a
    }
    if reflect.TypeOf(b).Kind() == reflect.Func {
        return b.(func() interface{})()
    }
    return b
}

func demo() {
    a := "hello"
    b := func() interface{} { return a + " world" }
    c := func() interface{} { return func() string { return "bye" } }
    fmt.Println(FullTernary(true, a, b).(string)) // cast shown, but not required
    fmt.Println(FullTernary(false, a, b))
    fmt.Println(FullTernary(true, b, a))
    fmt.Println(FullTernary(false, b, a))
    fmt.Println(FullTernary(true, c, nil).(func() string)())
}

उत्पादन

hello
hello world
hello world
hello
bye
  • पारित किए गए फ़ंक्शंस को वापस करना होगा interface{}आंतरिक कास्ट ऑपरेशन को संतुष्ट करने के लिए ।
  • संदर्भ के आधार पर, आप आउटपुट को एक विशिष्ट प्रकार में डालना चुन सकते हैं।
  • यदि आप इसमें से कोई फ़ंक्शन वापस करना चाहते हैं, तो आपको इसे उसी तरह से लपेटना होगा जैसा कि दिखाया गया है c

स्टैंडअलोन समाधान यहाँ भी अच्छा है, लेकिन कुछ उपयोगों के लिए कम स्पष्ट हो सकता है।


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