अगर-और स्टेटमेंट पर स्विच का लाभ


168

एक switchकथन का उपयोग करने के लिए सबसे अच्छा अभ्यास क्या है बनाम if30 unsignedगणनाओं के लिए एक बयान का उपयोग करना जहां लगभग 10 में एक अपेक्षित कार्रवाई होती है (वर्तमान में वही कार्रवाई है)। प्रदर्शन और स्थान पर विचार करने की आवश्यकता है, लेकिन महत्वपूर्ण नहीं हैं। मैंने स्निपेट को अमूर्त कर दिया है ताकि नामकरण सम्मेलनों के लिए मुझसे नफरत न करें।

switch बयान:

// numError is an error enumeration type, with 0 being the non-error case
// fire_special_event() is a stub method for the shared processing

switch (numError)
{  
  case ERROR_01 :  // intentional fall-through
  case ERROR_07 :  // intentional fall-through
  case ERROR_0A :  // intentional fall-through
  case ERROR_10 :  // intentional fall-through
  case ERROR_15 :  // intentional fall-through
  case ERROR_16 :  // intentional fall-through
  case ERROR_20 :
  {
     fire_special_event();
  }
  break;

  default:
  {
    // error codes that require no additional action
  }
  break;       
}

if बयान:

if ((ERROR_01 == numError)  ||
    (ERROR_07 == numError)  ||
    (ERROR_0A == numError)  || 
    (ERROR_10 == numError)  ||
    (ERROR_15 == numError)  ||
    (ERROR_16 == numError)  ||
    (ERROR_20 == numError))
{
  fire_special_event();
}

26
इसे 'व्यक्तिपरक' के रूप में संपादित किया गया है? वास्तव में? निश्चित रूप से 'व्यक्तिपरक' उन चीजों के लिए है जिन्हें एक तरह से या किसी अन्य तरीके से साबित नहीं किया जा सकता है?
एलेक्जेंड्रा फ्रैंक्स

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

8
मैं असहमत हूं, मुझे नहीं लगता कि यह व्यक्तिपरक है। एक साधारण ASM अंतर मायने रखता है, आप कई मामलों में अनुकूलन के कुछ सेकंड की उपेक्षा नहीं कर सकते। और इस सवाल में, यह एक धार्मिक युद्ध या बहस नहीं है, इस बात का तर्कसंगत स्पष्टीकरण है कि कोई व्यक्ति क्यों तेज होगा, बस स्वीकृत उत्तर पढ़ें।
18


@RichardFranks offtopic: grats! आप एसओ पर मॉडरेशन पर ले जाने वाले पहले मानव हैं जो मैंने कभी देखा है
जंगल_मोल

जवाबों:


162

स्विच का उपयोग करें।

सबसे खराब स्थिति में कंपाइलर एक-और-चेन के समान कोड उत्पन्न करेगा, इसलिए आप कुछ भी नहीं खोते हैं। यदि संदेह में सबसे आम मामलों को स्विच स्टेटमेंट में पहले रखा जाए।

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


2
तकनीकी रूप से अभी भी एक तुलना होगी, यह सुनिश्चित करने के लिए कि जंप टेबल के भीतर एनम का मूल्य निहित है।
0124816

Jep। यह सच है। एनम पर स्विच करना और सभी मामलों को संभालना अंतिम तुलना से छुटकारा पा सकता है, हालांकि।
Nils Pipenbrinck

4
ध्यान दें कि यदि एक संकलक द्वारा स्विच के समान होने के लिए सैद्धांतिक रूप से ifs की एक श्रृंखला का विश्लेषण किया जा सकता है, लेकिन मौका क्यों लेते हैं? एक स्विच का उपयोग करके, आप वास्तव में वही चाहते हैं जो आप चाहते हैं, जिससे कोड पीढ़ी आसान हो जाए।
जाकोबेंग्ब्लोम २

5
jakoben: कि किया जा सकता है, लेकिन केवल स्विच की तरह अगर / और चेन। व्यवहार में ये नहीं होते हैं क्योंकि प्रोग्रामर स्विच का उपयोग करते हैं। मैंने संकलक तकनीक में खुदाई की और मुझ पर भरोसा किया: ऐसे "बेकार" निर्माणों को खोजने में बहुत समय लगता है। कंपाइलर लोगों के लिए इस तरह के अनुकूलन से कोई मतलब नहीं है।
Nils Pipenbrinck

5
@NilsPipenbrinck छद्म-पुनरावर्ती के निर्माण में आसानी के साथ if- elseटेम्प्लेट मेटा प्रोग्रामिंग में चेन, और switch caseचेन बनाने में कठिनाई , कि मैपिंग अधिक महत्वपूर्ण हो सकती है। (और हाँ, प्राचीन टिप्पणी, लेकिन वेब हमेशा के लिए है, या कम से कम अगले
दशहरे

45

आपके द्वारा अपने उदाहरण में प्रदान किए गए विशेष मामले के लिए, स्पष्ट कोड शायद है:

if (RequiresSpecialEvent(numError))
    fire_special_event();

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

bool RequiresSpecialEvent(int numError)
{
    return specialSet.find(numError) != specialSet.end();
}

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


5
ये बिल्कुल सही है। पठनीयता स्विच और इफ-स्टेटमेंट दोनों की तुलना में बहुत बेहतर है। मैं वास्तव में कुछ इस तरह का जवाब देने वाला था, लेकिन आपने मुझे हरा दिया। :-)
२०:०६ पर mlarsen

यदि आपके enum मान सभी छोटे हैं, तो आपको एक हैश की जरूरत नहीं है, बस एक मेज है। जैसे const std::bitset<MAXERR> specialerror(initializer); इसके साथ प्रयोग करें if (specialerror[numError]) { fire_special_event(); }। यदि आप सीमा-जाँच चाहते हैं, bitset::test(size_t)तो बाहर के मानों पर एक अपवाद फेंक देंगे। ( bitset::operator[]रेंज-चेक नहीं करता है)। cplusplus.com/reference/bitset/bitset/test । यह संभवतः एक कंपाइलर-जनरेटेड जंप टेबल कार्यान्वयन switch, एस्प को बेहतर बनाएगा । नॉट-स्पेशल केस में जहां यह एक सिंगल नॉट-ब्रांच होगी।
पीटर कॉर्ड्स

@PeterCordes मैं अभी भी तर्क देता हूं कि तालिका को अपने कार्य में लगाना बेहतर है। जैसा कि मैंने कहा, जब आप ऐसा करते हैं तो बहुत सारे विकल्प खुलते हैं, मैंने उन सभी को समझने की कोशिश नहीं की।
मार्क रैनसम

@MarkRansom: मैं इसे अमूर्त करने से असहमत नहीं था। बस जब से आपने एक नमूना क्रियान्वयन का उपयोग किया std::set, मैंने सोचा कि मैं इंगित करता हूं कि यह शायद एक खराब विकल्प है। यह पता चला है कि जीसीसी पहले से ही एक 32 बिट में बिटमैप का परीक्षण करने के लिए ओपी कोड को पहले से ही संकलित करता है। godbolt: goo.gl/qjjv0e । gcc 5.2 ifसंस्करण के लिए भी ऐसा करेगा । इसके अलावा, अधिक हाल ही में जीसीसी सही जगह पर थोड़ा सा btडालने 1और उपयोग करने के लिए बिट-टेस्ट निर्देश का उपयोग करेगा test reg, imm32
पीटर कॉर्ड्स

यह तत्काल-निरंतर बिटमैप एक बड़ी जीत है, क्योंकि बिटमैप पर कोई कैश मिस नहीं है। यह काम करता है अगर "विशेष" त्रुटि कोड सभी 64 या उससे कम की सीमा में हैं। (या विरासत 32 बिट कोड के लिए 32।) संकलक सबसे छोटा मामला मान घटाता है, अगर यह गैर-शून्य है। Takeaway यह है कि हाल ही में संकलक इतने स्मार्ट हैं कि आप जो भी तर्क का उपयोग करते हैं उससे अच्छा कोड प्राप्त करने जा रहे हैं, जब तक कि आप इसे एक भारी डेटा संरचना का उपयोग करने के लिए नहीं कहते हैं।
पीटर कॉर्डेस

24

स्विच है तेजी से।

बस कोशिश करें यदि / पाश के अंदर 30 अलग-अलग मूल्य हैं, और स्विच की तुलना में समान कोड की तुलना करके देखें कि स्विच कितना तेज है।

अब, स्विच में एक वास्तविक समस्या है : स्विच को प्रत्येक मामले के मूल्यों के संकलन समय पर पता होना चाहिए। इसका मतलब है कि निम्नलिखित कोड:

// WON'T COMPILE
extern const int MY_VALUE ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

संकलन नहीं होगा।

तब अधिकांश लोग परिभाषित (आरग!) का उपयोग करेंगे, और अन्य एक ही संकलन इकाई में निरंतर चर को घोषित और परिभाषित करेंगे। उदाहरण के लिए:

// WILL COMPILE
const int MY_VALUE = 25 ;

void doSomething(const int p_iValue)
{
    switch(p_iValue)
    {
       case MY_VALUE : /* do something */ ; break ;
       default : /* do something else */ ; break ;
    }
}

तो, अंत में, डेवलपर को "गति + स्पष्टता" बनाम "कोड युग्मन" के बीच चयन करना होगा।

(ऐसा नहीं है कि एक स्विच को नरक के रूप में भ्रमित करने के लिए नहीं लिखा जा सकता है ... अधिकांश स्विच जो मैं वर्तमान में देख रहा हूं वह इस "भ्रामक" श्रेणी "के हैं ... लेकिन यह एक और कहानी है ...)

2008-09-21 को संपादित करें:

bk1e ने निम्न टिप्पणी जोड़ी: " हेडर फ़ाइल में एनम के रूप में स्थिरांक को परिभाषित करना इसे संभालने का एक और तरीका है"।

निश्चित रूप से यह है।

एक बाहरी प्रकार का बिंदु स्रोत से मूल्य को कम करना था। इस मूल्य को एक स्थूल के रूप में परिभाषित करते हुए, एक साधारण कास्ट इंट डिक्लेरेशन के रूप में, या यहां तक ​​कि एक एनम के रूप में मूल्य को रेखांकित करने का दुष्प्रभाव है। इस प्रकार, परिभाषित करना चाहिए, enum मान, या const int मान परिवर्तन, एक recompilation की आवश्यकता होगी। एक्सटर्नल डिक्लेरेशन का मतलब है कि वैल्यू चेंज के मामले में रीकॉइल करने की जरूरत नहीं है, लेकिन दूसरी ओर, स्विच का इस्तेमाल करना असंभव बना देता है। समापन स्विच का उपयोग करते हुए निष्कर्ष स्विच कोड और मामलों के रूप में उपयोग किए जाने वाले चर के बीच युग्मन को बढ़ाएगा । जब यह ठीक हो, तो स्विच का उपयोग करें। जब यह नहीं है, तब, कोई आश्चर्य की बात नहीं है।

2013-01-15 संपादित करें:

व्लाद लजारेंको ने मेरे जवाब पर टिप्पणी की, एक स्विच द्वारा उत्पन्न विधानसभा कोड के उनके गहन अध्ययन के लिए एक लिंक दिया। बहुत ज्ञानवर्धक: http://lazarenko.me/switch/


एक हेडर फाइल में एनम को परिभाषित करते हुए इसे संभालने का एक और तरीका है।
bk1e 15


1
@ व्लाद लजारेंको: लिंक के लिए धन्यवाद! यह एक बहुत ही रोचक पाठ था।
पियरसबल

1
@AhmedHussein user404725 की लिंक डेड है। शुक्र है, मैंने इसे वेकबैक मशीन में पाया: web.archive.org/web/20131111091431/http://lazarenko.me/2013/01/… । दरअसल, वेबैक मशीन काफी आशीर्वाद हो सकती है।
जैक गिफिन

बहुत बहुत धन्यवाद, यह बहुत मददगार है
अहमद हुसैन

20

कंपाइलर इसे वैसे भी ऑप्टिमाइज़ करेगा - स्विच के लिए जाएं क्योंकि यह सबसे पठनीय है।


3
संभावना है कि संकलक स्पर्श नहीं करेगा यदि-अन्यथा। वास्तव में, gccयह सुनिश्चित करने के लिए नहीं होगा (इसके लिए एक अच्छा कारण है)। क्लैंग दोनों मामलों को एक द्विआधारी खोज में अनुकूलित करेगा। उदाहरण के लिए, को देखने के लिए इस

7

स्विच, केवल पठनीयता के लिए। विशाल अगर बयानों को बनाए रखना कठिन है और मेरी राय में पढ़ने के लिए कठिन है।

ERROR_01 : // जानबूझकर गिरना

या

(ERROR_01 == संख्या)

बाद में त्रुटि अधिक होती है और पहले की तुलना में अधिक टाइपिंग और फॉर्मेटिंग की आवश्यकता होती है।


6

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


6

स्विच का उपयोग करें, यह वही है जो इसके लिए है और प्रोग्रामर क्या उम्मीद करते हैं।

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


4

कंपाइलर अनुकूलन करने में वास्तव में अच्छे हैं switch। हाल की gcc भी a में स्थितियों का एक गुच्छा अनुकूलित करने में अच्छा है if

मैंने गॉडबोल्ट पर कुछ परीक्षण मामले बनाए

जब caseमानों को एक साथ रखा जाता है, तो gcc, clang और icc सभी एक बिटमैप का उपयोग करने के लिए पर्याप्त स्मार्ट होते हैं ताकि यह जांचा जा सके कि क्या मान विशेष में से एक है।

उदाहरण के लिए gcc 5.2 -O3 switch(और ifकुछ इसी तरह) के लिए संकलित करता है :

errhandler_switch(errtype):  # gcc 5.2 -O3
    cmpl    $32, %edi
    ja  .L5
    movabsq $4301325442, %rax   # highest set bit is bit 32 (the 33rd bit)
    btq %rdi, %rax
    jc  .L10
.L5:
    rep ret
.L10:
    jmp fire_special_event()

ध्यान दें कि बिटमैप तत्काल डेटा है, इसलिए इसे एक्सेस करने की कोई संभावित डेटा-कैश मिस या जंप टेबल नहीं है।

gcc 4.9.2 -O3 switchएक बिटमैप के लिए संकलित करता है, लेकिन 1U<<errNumbermov / Shift के साथ करता है । यह ifसंस्करण को शाखाओं की श्रृंखला के लिए संकलित करता है ।

errhandler_switch(errtype):  # gcc 4.9.2 -O3
    leal    -1(%rdi), %ecx
    cmpl    $31, %ecx    # cmpl $32, %edi  wouldn't have to wait an extra cycle for lea's output.
              # However, register read ports are limited on pre-SnB Intel
    ja  .L5
    movl    $1, %eax
    salq    %cl, %rax   # with -march=haswell, it will use BMI's shlx to avoid moving the shift count into ecx
    testl   $2150662721, %eax
    jne .L10
.L5:
    rep ret
.L10:
    jmp fire_special_event()

ध्यान दें कि यह कैसे 1 से घटाता है errNumber( leaचाल के साथ उस ऑपरेशन को संयोजित करने के लिए)। यह 64 बिट तत्काल से बचने के लिए, एक 32 बिट तत्काल में बिटमैप फिट करने देता हैmovabsq जो अधिक निर्देश बाइट लेता है ।

एक छोटा (मशीन कोड में) अनुक्रम होगा:

    cmpl    $32, %edi
    ja  .L5
    mov     $2150662721, %eax
    dec     %edi   # movabsq and btq is fewer instructions / fewer Intel uops, but this saves several bytes
    bt     %edi, %eax
    jc  fire_special_event
.L5:
    ret

(उपयोग करने में विफलता jc fire_special_eventसर्वव्यापी है, और संकलक बग है ।)

rep retपुराने AMD K8 और K10 (पूर्व-बुलडोजर) के लाभ के लिए, शाखा लक्ष्यों में और सशर्त शाखाओं का अनुसरण करने के लिए उपयोग किया जाता है: `rep ret` का क्या अर्थ है? । इसके बिना, शाखा भविष्यवाणी उन अप्रचलित सीपीयू पर भी काम नहीं करती है।

bt(बिट परीक्षण) एक रजिस्टर arg के साथ तेज है। यह errNumberबिट्स द्वारा ए-लेफ्ट-शिफ्टिंग के काम को जोड़ती है और ए कर रही है test, लेकिन अभी भी 1 चक्र विलंबता और केवल एक इंटेल यूओपी है। यह अपने तरीके से बहुत अधिक CISC शब्दार्थ के कारण मेमोरी अरग के साथ धीमा है: "बिट स्ट्रिंग" के लिए मेमोरी ऑपरेंड के साथ, परीक्षण की जाने वाली बाइट का पता अन्य arg (8 से विभाजित) और isn के आधार पर गणना की जाती है '1, 2, 4 या 8byte chunk तक सीमित है, जिसे मेमोरी ऑपरेंड द्वारा इंगित किया गया है।

से Agner कोहरा के निर्देश टेबल , एक चर गिनती पारी अनुदेश एक की तुलना में धीमी है btहाल ही में इंटेल (2 के बजाय UOPs 1, और पारी और सब कुछ है कि के लिए आवश्यक नहीं है) पर।


4

स्विच का लाभ () यदि किसी अन्य मामले में हैं: - 1. यदि स्विच की तुलना में अधिक कुशल है, क्योंकि प्रत्येक मामला पिछले मामले पर निर्भर नहीं करता है, तो इसके विपरीत यदि व्यक्तिगत कथन को सही या गलत स्थिति के लिए जाँचने की आवश्यकता है।

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

  2. स्विच में मानों को उपयोगकर्ता परिभाषित किया गया है जबकि मान यदि किसी अन्य मामले में बाधाओं पर आधारित है।

  3. त्रुटि के मामले में, स्विच में बयानों को आसानी से जांचा जा सकता है और ठीक किया जा सकता है जो कि यदि किसी अन्य कथन के मामले में जांचना तुलनात्मक रूप से कठिन है।

  4. स्विच मामला बहुत अधिक कॉम्पैक्ट और पढ़ने और समझने में आसान है।


2

IMO यह एक आदर्श उदाहरण है कि स्विच फॉल-थ्रू किस लिए बनाया गया था।


c # में यह एकमात्र ऐसा मामला है जहां गिर विचार होता है। अच्छा तर्क वहीं।
BCS

2

यदि आपके मामलों को भविष्य में समूहीकृत किए जाने की संभावना है - यदि एक से अधिक मामले एक परिणाम से मेल खाते हैं - तो स्विच को पढ़ने और बनाए रखने में आसान साबित हो सकता है।


2

वे समान रूप से अच्छी तरह से काम करते हैं। प्रदर्शन उसी के बारे में है जिसे एक आधुनिक संकलक दिया गया है।

मैं पसंद करता हूं अगर केस स्टेटमेंट पर बयान दिया जाता है क्योंकि वे अधिक पठनीय हैं, और अधिक लचीले हैं - आप अन्य शर्तों को जोड़ सकते हैं जो संख्यात्मक बराबर पर आधारित नहीं हैं, जैसे "" अधिकतम <मिनट। लेकिन आपके द्वारा यहां पोस्ट किए गए साधारण मामले के लिए, यह वास्तव में मायने नहीं रखता है, बस वही करें जो आपके लिए सबसे अधिक पठनीय है।


2

स्विच निश्चित रूप से पसंद किया जाता है। स्विच की मामलों की सूची को देखना आसान है और यह सुनिश्चित करने के लिए जानते हैं कि लंबे समय तक स्थिति को पढ़ने की तुलना में यह क्या कर रहा है।

ifस्थिति में दोहराव आंखों पर कठिन है। मान लीजिए कि एक ==लिखा गया था!= ; क्या आप ध्यान देंगे? या अगर 'अंक' का एक उदाहरण 'nmuError' लिखा गया, जो सिर्फ संकलन के लिए हुआ?

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

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


2

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


2
मैं असहमत हूं, उसी कारण से कि मैं पतन-हालांकि मामले से असहमत हूं। मैंने स्विच को "मामलों में 01,07,0A, 10,15,16 और 20 आग विशेष घटना के रूप में पढ़ा।" किसी अन्य खंड में कोई गिरावट नहीं है। हालांकि, यह C ++ सिंटैक्स की एक कलाकृति है जहां आप प्रत्येक मान के लिए 'केस' कीवर्ड दोहराते हैं।
MSalters

1

यदि मैं स्पष्टता और सम्मेलन के लिए इफ स्टेटमेंट को चुनूंगा, हालांकि मुझे यकीन है कि कुछ असहमत होंगे। सब के बाद, आप कुछ करना चाहते हैं ifकुछ शर्त सच है! एक कार्रवाई के साथ एक स्विच होने से थोड़ा सा लगता है ... अनावश्यक।


1

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

switch(numerror){
    ERROR_20 : { fire_special_event(); } break;
    default : { null; } break;
}

शायद संभावनाओं की एक सूची के खिलाफ भी अपनी स्थिति (इस मामले में अंक में) का परीक्षण करें, एक सरणी शायद इसलिए आपके SWITCH का भी उपयोग नहीं किया जाता है जब तक कि निश्चित रूप से एक परिणाम नहीं होगा।


कुल 30 त्रुटियां हैं। 10 को विशेष कार्रवाई की आवश्यकता है, इसलिए मैं ~ 20 त्रुटियों के लिए डिफ़ॉल्ट का उपयोग कर रहा हूं जिन्हें कार्रवाई की आवश्यकता नहीं है ...
Zing-

1

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


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

एक तालिका शायद ही जटिल है - वास्तव में यह कोड के लिए एक स्विच की तुलना में संभवतः सरल है। और बयान ने उल्लेख किया प्रदर्शन एक कारक था।
ग्रेग व्हिटफील्ड

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

इसके अलावा, यदि आप कुछ भी अपने आप को लागू करने जा रहे हैं std::bitset<MAXERR> specialerror;, तो, तब करें if (specialerror[err]) { special_handler(); }। यह जंप टेबल, एस्प की तुलना में तेज होगा। नहीं लिया मामले में।
पीटर कॉर्ड्स

1

मुझे सर्वश्रेष्ठ-अभ्यास के बारे में निश्चित नहीं है, लेकिन मैं स्विच का उपयोग करूंगा - और फिर 'डिफॉल्ट' के माध्यम से जानबूझकर गिरना


1

एस्थेटिकली मैं इस दृष्टिकोण का पक्ष लेता हूं।

unsigned int special_events[] = {
    ERROR_01,
    ERROR_07,
    ERROR_0A,
    ERROR_10,
    ERROR_15,
    ERROR_16,
    ERROR_20
 };
 int special_events_length = sizeof (special_events) / sizeof (unsigned int);

 void process_event(unsigned int numError) {
     for (int i = 0; i < special_events_length; i++) {
         if (numError == special_events[i]) {
             fire_special_event();
             break;
          }
     }
  }

डेटा को थोड़ा स्मार्ट बनाएं ताकि हम तर्क को थोड़ा कम कर सकें।

मुझे लगता है कि यह अजीब लग रहा है। यहाँ प्रेरणा है (मैं इसे पायथन में कैसे करूँगा) से प्रेरणा मिली:

special_events = [
    ERROR_01,
    ERROR_07,
    ERROR_0A,
    ERROR_10,
    ERROR_15,
    ERROR_16,
    ERROR_20,
    ]
def process_event(numError):
    if numError in special_events:
         fire_special_event()

4
किसी भाषा के वाक्य-विन्यास का इस बात पर प्रभाव पड़ता है कि हम किसी समाधान को कैसे लागू करते हैं ... => यह C में कुरूप दिखता है और पायथन में अच्छा है। :)
rlerallut

बिटमैप का उपयोग करें? अगर Error_0a 0x0a आदि है तो आप उन्हें लंबे समय तक बिट्स के रूप में रख सकते हैं। long long special_events = 1LL << 1 | 1LL << 7 | 1LL << 0xa ... तब उपयोग करें यदि (special_events & (1LL << numError) fire_special_event ()
कागज़ात

1
छी। आपने O (N) में ओ (1) सबसे खराब स्थिति वाला ऑपरेशन (यदि जंप टेबल उत्पन्न होता है) सबसे खराब स्थिति (जहाँ N संभाले मामलों की संख्या है) को बदल दिया है, और आपने breakबाहर का उपयोग किया है case(हाँ, एक मामूली पाप, लेकिन फिर भी एक पाप)। :)
मैक

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

1
while (true) != while (loop)

संभवतः पहले एक को संकलक द्वारा अनुकूलित किया गया है, जो यह बताता है कि लूप काउंट को बढ़ाते समय दूसरा लूप धीमा क्यों है।


यह McAnix के उत्तर के लिए एक टिप्पणी प्रतीत होती है। यही कारण है कि केवल समय पर कि प्रयास के साथ समस्याओं में से एक है ifबनाम switchजावा में एक पाश के अंत शर्त के रूप में।
पीटर कॉर्ड्स

1

जब कार्यक्रम को संकलित करने की बात आती है, तो मुझे नहीं पता कि क्या कोई अंतर है। लेकिन कार्यक्रम के लिए और कोड को यथासंभव सरल रखने के लिए, मुझे व्यक्तिगत रूप से लगता है कि यह इस पर निर्भर करता है कि आप क्या करना चाहते हैं। अगर और अगर बयानों के अपने फायदे हैं, जो मुझे लगता है कि हैं:

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

(उदाहरण:

`int a;
 cout<<"enter value:\n";
 cin>>a;

 if( a > 0 && a < 5)
   {
     cout<<"a is between 0, 5\n";

   }else if(a > 5 && a < 10)

     cout<<"a is between 5,10\n";

   }else{

       "a is not an integer, or is not in range 0,10\n";

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

`int a;
 cout<<"enter value:\n";
 cin>>a;

 switch(a)
 {
    case 0:
    case 1:
    case 2: 
    case 3:
    case 4:
    case 5:
        cout<<"a is between 0,5 and equals: "<<a<<"\n";
        break;
    //other case statements
    default:
        cout<<"a is not between the range or is not a good value\n"
        break;

मैं पसंद करता हूँ अगर - और अगर - और बयान, लेकिन यह वास्तव में आप पर निर्भर है। यदि आप फ़ंक्शंस का उपयोग शर्तों के रूप में करना चाहते हैं, या आप किसी श्रेणी, सरणी, या वेक्टर और / या कुछ के खिलाफ परीक्षण करना चाहते हैं, तो आपको जटिल घोंसले के शिकार से निपटने में कोई आपत्ति नहीं है, मैं अनुशंसा करता हूं कि यदि कोई और ब्लॉक करता है। यदि आप एकल मानों के खिलाफ परीक्षण करना चाहते हैं या आप ब्लॉक को पढ़ना चाहते हैं, तो मैं आपको स्विच () केस ब्लॉक का उपयोग करने की सलाह दूंगा।


0

Im आपको गति और स्मृति उपयोग के बारे में बताने वाला व्यक्ति नहीं है, लेकिन एक स्विच स्टेटमेंट को देखना बहुत आसान नरक है, तो एक बड़ा यदि कथन (विशेष रूप से 2-3 महीने लाइन के नीचे)


0

मुझे इसका पुराना पता है लेकिन

public class SwitchTest {
static final int max = 100000;

public static void main(String[] args) {

int counter1 = 0;
long start1 = 0l;
long total1 = 0l;

int counter2 = 0;
long start2 = 0l;
long total2 = 0l;
boolean loop = true;

start1 = System.currentTimeMillis();
while (true) {
  if (counter1 == max) {
    break;
  } else {
    counter1++;
  }
}
total1 = System.currentTimeMillis() - start1;

start2 = System.currentTimeMillis();
while (loop) {
  switch (counter2) {
    case max:
      loop = false;
      break;
    default:
      counter2++;
  }
}
total2 = System.currentTimeMillis() - start2;

System.out.println("While if/else: " + total1 + "ms");
System.out.println("Switch: " + total2 + "ms");
System.out.println("Max Loops: " + max);

System.exit(0);
}
}

लूप काउंट को बदलने से बहुत परिवर्तन होते हैं:

यदि / अन्यथा: 5ms स्विच: 1ms अधिकतम लूप: 100000

यदि / अन्यथा: 5ms स्विच: 3ms अधिकतम लूप: 1000000

यदि / अन्यथा: 5ms स्विच: 14ms अधिकतम लूप: 10000000

यदि / अन्यथा: 5ms स्विच: 149ms अधिकतम लूप: 100000000

(यदि आप चाहें तो और बयान जोड़ें)


3
अच्छी बात है, लेकिन आप गलत भाषा में हैं। भाषा बदलने से बहुत कुछ बदल जाता है;)
गेब्रियल श्राइबर

2
if(max) breakपाश पाश गिनती की परवाह किए बिना निरंतर समय में चलाता है? JIT- संकलक की तरह लगता है कि लूप को दूर करने के लिए काफी स्मार्ट है counter2=max। और शायद यह स्विच की तुलना में धीमा है यदि पहले कॉल currentTimeMillisमें अधिक ओवरहेड है, क्योंकि अभी तक सब कुछ JIT- संकलित नहीं है? दूसरे क्रम में छोरों को रखना शायद अलग परिणाम देगा।
पीटर कॉर्ड्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.