2010-06-23 9 views
20

यह प्रश्न मिल सकता है, लेकिन मैं यहां एक स्थिति में हूं।गोटो का उपयोग करने के लिए या नहीं?

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

मानक ब्रेक और ध्वज चर का उपयोग करना इस मामले में काफी बोझिल है और राज्य का ट्रैक रखना मुश्किल है।

क्या दृष्टिकोण बेहतर है? किसी और चीज से ज्यादा मुझे चिंता है कि यह मेरे मालिक पर एक बुरा प्रभाव डाल सकता है, क्योंकि मैं इंटर्नशिप पर हूं।

+19

ए को लिंक करना होगा: http://xkcd.com/292/ –

+3

मैं उत्सुक हूं। क्या आप पहले गोटो/कोड के गोटो स्नैपशॉट के बाद पोस्ट कर सकते हैं? –

+3

हम आपके मालिक को नहीं जानते हैं और उसके पास क्या पूर्वाग्रह हैं, इसलिए यदि आप चालाक देखना चाहते हैं, तो फ़ंक्शन पॉइंटर्स का उपयोग करें (यहां अन्य प्रश्नों का मेरा उत्तर देखें: http://stackoverflow.com/questions/1371460/state-machines- ट्यूटोरियल/1371654 # 1371654) – qrdl

उत्तर

19

एक राज्य मशीन को लागू करने के लिए goto का उपयोग करना अक्सर अच्छी समझ में आता है।

typedef enum {s0,s1,s2,s3,s4,...,sn,sexit} state; 

state nextstate; 
int done = 0; 

nextstate = s0; /* set up to start with the first state */ 
while(!done) 
    switch(nextstate) 
     { 
     case s0: 
      nextstate = do_state_0(); 
      break; 
     case s1: 
      nextstate = do_state_1(); 
      break; 
     case s2: 
      nextstate = do_state_2(); 
      break; 
     case s3: 
      . 
      . 
      . 
      . 
     case sn: 
      nextstate = do_state_n(); 
      break; 
     case sexit: 
      done = TRUE; 
      break; 
     default: 
      /* some sort of unknown state */ 
      break; 
     } 
15

यदि मैं अपने मालिक पर एक अच्छा प्रभाव छोड़ना चाहता हूं, तो मैं Ragel जैसे एफएसएम जनरेटर का उपयोग करूंगा।

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

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

+10

+1 - पहिया को पुन: पेश न करें। –

+3

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

+3

मैं गोटो का समर्थन नहीं करता क्योंकि एफएसएम जेनरेटर इसे करते हैं। कोड जनरेटर (जो अनुमानतः परीक्षण किया गया है) के बीच एक अंतर है जो गोटो और प्रोग्रामर कर रहा है। मुझे यकीन नहीं है कि यहां जाने का कौन सा तरीका है। लेकिन मैं इस तथ्य को नहीं दूँगा कि जनरेटर गोटो का उपयोग किसी भी तरह से निर्णय को प्रभावित करते हैं (बेशक अगर मैं इसे करने के लिए जनरेटर लिख रहा था ...)। – Cervo

10

मैं एक चर और एक स्विच पटरियों है कि क्या राज्य में कर रहे हैं का उपयोग उन्हें संभालने के लिए होगा:।

fsm_ctx_t ctx = ...; 
state_t state = INITIAL_STATE; 

while (state != DONE) 
{ 
    switch (state) 
    { 
    case INITIAL_STATE: 
    case SOME_STATE: 
     state = handle_some_state(ctx) 
     break; 

    case OTHER_STATE: 
     state = handle_other_state(ctx); 
     break; 
    } 
} 
4

मैं अपने विशिष्ट कोड पता नहीं है, लेकिन वहाँ एक कारण कुछ है इस तरह:

typedef enum { 
    STATE1, STATE2, STATE3 
} myState_e; 

void myFsm(void) 
{ 
    myState_e State = STATE1; 

    while(1) 
    { 
     switch(State) 
     { 
      case STATE1: 
       State = STATE2; 
       break; 
      case STATE2: 
       State = STATE3; 
       break; 
      case STATE3: 
       State = STATE1; 
       break; 
     } 
    } 
} 

आपके लिए काम नहीं करेगा? यह goto का उपयोग नहीं करता है, और यह अपेक्षाकृत आसान है।

संपादित करें: सभी उन State = टुकड़े सूखी उल्लंघन करते हैं, तो मैं बजाय की तरह कुछ कर सकता:

typedef int (*myStateFn_t)(int OldState); 

int myStateFn_Reset(int OldState, void *ObjP); 
int myStateFn_Start(int OldState, void *ObjP); 
int myStateFn_Process(int OldState, void *ObjP); 

myStateFn_t myStateFns[] = { 
#define MY_STATE_RESET 0 
    myStateFn_Reset, 
#define MY_STATE_START 1 
    myStateFn_Start, 
#define MY_STATE_PROCESS 2 
    myStateFn_Process 
} 

int myStateFn_Reset(int OldState, void *ObjP) 
{ 
    return shouldStart(ObjP) ? MY_STATE_START : MY_STATE_RESET; 
} 

int myStateFn_Start(int OldState, void *ObjP) 
{ 
    resetState(ObjP); 
    return MY_STATE_PROCESS; 
} 

int myStateFn_Process(int OldState, void *ObjP) 
{ 
    return (process(ObjP) == DONE) ? MY_STATE_RESET : MY_STATE_PROCESS; 
} 

int stateValid(int StateFnSize, int State) 
{ 
    return (State >= 0 && State < StateFnSize); 
} 

int stateFnRunOne(myStateFn_t StateFns, int StateFnSize, int State, void *ObjP) 
{ 
    return StateFns[OldState])(State, ObjP); 
} 

void stateFnRun(myStateFn_t StateFns, int StateFnSize, int CurState, void *ObjP) 
{ 
    int NextState; 

    while(stateValid(CurState)) 
    { 
     NextState = stateFnRunOne(StateFns, StateFnSize, CurState, ObjP); 
     if(! stateValid(NextState)) 
      LOG_THIS(CurState, NextState); 
     CurState = NextState; 
    } 
} 

जो निश्चित रूप से है, बहुत लंबे समय तक पहला प्रयास (सूखी के बारे में अजीब बात) की तुलना में,। लेकिन यह भी अधिक मजबूत है - राज्य कार्यों में से किसी एक से राज्य को वापस करने में विफलता के परिणामस्वरूप पहले कोड में गायब State = को चुपचाप अनदेखा करने के बजाय संकलक चेतावनी होगी।

+0

... क्या यह अभी प्रभाव में नहीं है? – naiad

+1

नहीं, क्योंकि संकलक लेबल, कूद और स्टैक को संभालता है। यह सुझाव देने जैसा है कि लिस्प या पायथन जैसे अभिव्यक्तिपूर्ण भाषा की दस लाख लाइनें असेंबलर हो सकती हैं। निश्चित रूप से, वे सब कुछ कहा और किया जाने के बाद भी वही कार्य कर सकते हैं, लेकिन वे दूरस्थ रूप से वही नहीं हैं। – Puppy

+0

+1 धन्यवाद। वह कोड वास्तव में मेरे लिए काम करेगा। –

1

मैं तुम्हें सिफारिश करेंगे "Dragon book":: यदि आप एक गोटो का उपयोग कर के बारे में बहुत चिंतित हैं, तो एक उचित विकल्प एक state चर जिसे आप संशोधित, और एक switch बयान है कि के आधार पर करने के लिए अक्सर है Compilers, Principles-Techniques-Tools अहो, सेठी और उलमैन से। (यह खरीदने के लिए महंगा है लेकिन आप निश्चित रूप से इसे पुस्तकालय में पाएंगे)। वहां आपको कुछ भी मिलेगा जो आपको तारों को पार्स करने और सीमित automatons बनाने के लिए आवश्यक होगा। goto के साथ मुझे कोई जगह नहीं मिल सकती है।आम तौर पर राज्य एक डेटा टेबल और संक्रमण होते हैं जैसे accept_space()

8

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

8

बचें goto जब तक जटिलता जोड़ा (से बचने के लिए) और अधिक भ्रामक है।

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

एक सही ढंग से काम समाधान आमतौर पर प्राथमिक उद्देश्य है। इसे सही बनाने के लिए और बनाए रखने योग्य (जटिलता को कम करके) कई जीवन चक्र लाभ हैं। इसे पहले काम करें, और फिर इसे धीरे-धीरे साफ करें, अधिमानतः सरलता और कुरूपता को हटाकर।

36

goto साथ स्वाभाविक गलत कुछ भी नहीं है। उन्हें अक्सर "वर्जित" माना जाने वाला कारण यह है कि कुछ प्रोग्रामर (अक्सर असेंबली दुनिया से आते हैं) उन्हें "स्पेगेटी" कोड बनाने के लिए उपयोग करते हैं जो समझने के लिए लगभग असंभव है। यदि आप अपने कोड को साफ, पठनीय और बग-फ्री रखते हुए goto कथन का उपयोग कर सकते हैं, तो आपके लिए अधिक शक्ति।

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

हकीकत में, दो तरीकों बहुत समान हैं। आप राज्य चर विधि का उपयोग कर एक राज्य मशीन लिख सकते हैं और यह संकलन हैं, तो जनरेट विधानसभा सकता है बहुत अच्छी तरह से goto विधि (अनुकूलन के अपने संकलक के स्तर के आधार पर) का उपयोग कर लिखा कोड के समान है। goto विधि को राज्य परिवर्तनीय विधि से अतिरिक्त चर और लूप को अनुकूलित करने के रूप में देखा जा सकता है। आप जिस विधि का उपयोग करते हैं वह व्यक्तिगत पसंद का मामला है, और जब तक आप काम कर रहे हैं, पठनीय कोड मैं आशा करता हूं कि आपके बॉस दूसरे पर एक विधि का उपयोग करने के लिए आप में से कोई भी अलग नहीं सोचेंगे।

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

+3

जो भी सम्मेलन पहले से ही उपयोग में है, निम्नलिखित में ज्ञान है – Cervo

+3

+1: स्वच्छता और ज्ञान –

+0

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

1

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

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

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

संबंधित मुद्दे