क्या किसी शर्त की अनावश्यक रूप से जाँच करना बुरा है?


10

मुझे अक्सर अपने कोड में स्थान मिलता है जहां मैं खुद को एक विशिष्ट स्थिति की बार-बार जांच करवाता हूं।

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

# ...
clear_lines() # removes every other line than those starting with "a" or "b"
for line in lines:
    if (line.startsWith("a")):
        # do stuff
    elif (line.startsWith("b")):
        # magic
    else:
        # this else is redundant, I already made sure there is no else-case
        # by using clear_lines()
# ...

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

क्या आप इसे शोर के रूप में मानते हैं या यह मेरे कोड में कुछ मूल्य जोड़ता है?


5
यह मूल रूप से इस बारे में है कि आप रक्षात्मक तरीके से कोडिंग कर रहे हैं या नहीं। क्या आप देखते हैं कि इस कोड को बहुत संपादित किया जा रहा है? क्या यह संभावना है कि यह एक ऐसी प्रणाली का हिस्सा बनने जा रहा है जिसे अत्यंत विश्वसनीय होने की आवश्यकता है? मुझे assert()परीक्षण में मदद करने के लिए वहां शौच करने में ज्यादा नुकसान नहीं दिखता है , लेकिन इसके अलावा शायद बहुत ज्यादा है। यह स्थिति के आधार पर अलग-अलग होगा।
1

आपका 'और' मामला अनिवार्य रूप से मृत / पहुंच योग्य कोड है। जाँच करें कि कोई सिस्टम वाइड आवश्यकताएँ नहीं हैं जो इस पर रोक लगाती हैं।
NWS

@NWS: क्या आप कह रहे हैं कि मुझे दूसरा मामला रखना चाहिए? क्षमा करें, मैं आपको पूरी तरह से नहीं समझता।
निशान

2
विशेष रूप से प्रश्न से संबंधित नहीं है - लेकिन मैं उस 'अभिकथन' को एक विलक्षण में बनाऊंगा - जिसके लिए एक नई "लाइन" वर्ग (शायद ए एंड बी के लिए व्युत्पन्न वर्गों के साथ) की आवश्यकता होगी, न कि रेखाओं को तार के रूप में मानने और उन्हें बताने के लिए वे बाहर से प्रतिनिधित्व करते हैं। मैं
मैटडेवी

क्या आपका मतलब था elif (line.startsWith("b"))? वैसे, आप शर्तों पर उन बढ़ते हुए कोष्ठक को सुरक्षित रूप से हटा सकते हैं, वे पायथन में मुहावरेदार नहीं हैं।
टोकलैंड

जवाबों:


14

यह एक बहुत ही सामान्य अभ्यास है और इससे निपटने का तरीका उच्च-क्रम के फिल्टर के माध्यम से है ।

अनिवार्य रूप से, आप फ़िल्टर विधि के लिए एक फ़ंक्शन पास करते हैं, उस सूची / अनुक्रम के साथ जिसे आप फ़िल्टर करना चाहते हैं और परिणामी सूची / अनुक्रम में केवल वे तत्व शामिल हैं जो आप चाहते हैं।

मैं अजगर वाक्यविन्यास से अपरिचित हूं (हालांकि, इसमें ऐसा फ़ंक्शन शामिल है जैसा कि ऊपर लिंक में देखा गया है), लेकिन c # / f # में यह इस तरह दिखता है:

सी#:

var linesWithAB = lines.Where(l => l.StartsWith("a") || l.StartsWith("b"));
foreach (var line in linesWithAB)
{
    /* line is guaranteed to ONLY start with a or b */
}

f # (मान लिया गया है, अन्यथा List.filter का उपयोग किया जाएगा):

let linesWithAB = lines
    |> Seq.filter (fun l -> l.StartsWith("a") || l.StartsWith("b"))

for line in linesWithAB do
    /* line is guaranteed to ONLY start with a or b */

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


3
एक नोट के रूप में, इसके लिए अजगर वाक्यविन्यास एक जनरेटर अभिव्यक्ति होगा (line for line in lines if line.startswith("a") or line.startswith("b")):।
लैट्टी

1
+1 इंगित करने के लिए कि (अनावश्यक) अनिवार्य कार्यान्वयन clear_linesवास्तव में एक बुरा विचार है। पाइथन में आप शायद मेमोरी में पूरी फाइल लोड करने से बचने के लिए जनरेटर का उपयोग करेंगे।
टोकलैंड

क्या होता है जब इनपुट फ़ाइल उपलब्ध मेमोरी से बड़ी होती है?
ब्लरफ्ल

@ ब्लरफ्ल: यदि जेनरेटर शब्द c # / f # / python के बीच सुसंगत है, तो @tokland और @Lattyware क्या c # / f # यील्ड और / या यील्ड में अनुवाद करता है! बयान। यह मेरे f # उदाहरण में थोड़ा अधिक स्पष्ट है क्योंकि Seq.filter केवल IEnumerable <T> के संग्रह पर लागू किया जा सकता है, लेकिन दोनों कोड उदाहरण काम करेंगे यदि linesएक उत्पन्न संग्रह है।
स्टीवन एवर्स

@mcwise: जब आप इस तरह से काम करने वाले अन्य उपलब्ध कार्यों को देखना शुरू करते हैं तो यह वास्तव में सेक्सी और अविश्वसनीय रूप से अभिव्यंजक होने लगते हैं क्योंकि वे सभी एक साथ जंजीर और रचना कर सकते हैं। को देखो skip, take, reduce( aggregate.NET में), map( select.NET में), और वहाँ और भी है, लेकिन यह एक सच में ठोस शुरुआत है।
स्टीवन एवर्स

14

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

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

इसे सही तरीके से करने में मुझे शायद दो घंटे अतिरिक्त लगे, लेकिन अन्य लोगों के समय के साथ-साथ समस्या निवारण में भी एक दिन बर्बाद हो गया। यह बहुत दुर्लभ है कि कुछ प्रोसेसर चक्र व्यर्थ समस्या निवारण के एक दिन के लायक हैं।

यह कहा जा रहा है, जहां पढ़ने वाली फ़ाइलों का संबंध है, यह अक्सर आपके सॉफ़्टवेयर को डिज़ाइन करने के लिए इसे पढ़ने और इसे एक पंक्ति में संसाधित करने के बजाय एक बार में पूरी फ़ाइल को मेमोरी में संसाधित करने और स्मृति में संसाधित करने के लिए डिज़ाइन करना फायदेमंद होता है। इस तरह यह अभी भी बहुत बड़ी फ़ाइलों पर काम करेगा।


"यह बहुत दुर्लभ है कि कुछ प्रोसेसर चक्र व्यर्थ समस्या निवारण के एक दिन के लायक हैं।" उत्तर के लिए धन्यवाद, आपके पास एक अच्छा बिंदु है।
मार्कटनी

5

आप elseमामले में अपवाद उठा सकते हैं । इस तरह यह बेमानी नहीं है। अपवाद ऐसी चीजें हैं जो होने वाली नहीं हैं, लेकिन वैसे भी जाँच की जाती हैं।

clear_lines() # removes every other line than those starting with "a" or "b"
for line in lines:
    if (line.startsWith("a)):
        # do stuff
    if (line.startsWith("b")):
        # magic
    else:
        throw BadLineException
# ...

मेरा तर्क है कि उत्तरार्द्ध एक बुरा विचार है, क्योंकि यह कम स्पष्ट है - यदि आप बाद में जोड़ने का फैसला करते हैं "c", तो यह कम स्पष्ट हो सकता है।
लैट्टी

पहले सुझाव में योग्यता है ... दूसरा ("बी" मान लें) एक बुरा विचार है
एंड्रयू

@ लेटवेयर मैंने जवाब सुधारा। आपकी टिप्पणियों के लिए आभार।
ट्यूलेंस कोरडोवा

1
@ और मैंने जवाब में सुधार किया। आपकी टिप्पणियों के लिए आभार।
ट्यूलेंस कोरडोवा

3

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

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

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

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

यदि आप अनुबंधों का शोषण करते हैं, तो आप अनावश्यक रूप से जाँच की गई स्थिति से बच सकते हैं क्योंकि या तो फ़ंक्शन पूरी तरह से काम करता है और आपको डबल चेक की आवश्यकता नहीं है, या कॉल किया गया फ़ंक्शन शिथिल है और कॉलिंग फ़ंक्शन व्यवहार कर सकता है जैसा वह चाहता है।

कठिन हिस्सा तो यह परिभाषित करने के लिए है कि कौन सा फ़ंक्शन क्या जिम्मेदार है, और इन भूमिकाओं को कड़ाई से दस्तावेज करना है।


1

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

यह केवल खराब शैली नहीं है, यह कम्प्यूटेशनल रूप से खराब है।


2
यह हो सकता है कि उन पंक्तियों का उपयोग किसी और चीज़ के लिए किया जा रहा हो, और "a"/ "b"पंक्तियों से निपटने से पहले उनसे निपटा जाना चाहिए । यह नहीं कह रहा है कि यह संभव है ( स्पष्ट नाम का अर्थ है कि उन्हें छोड़ दिया जा रहा है), बस यह है कि इसकी संभावना है। यदि भविष्य में बार-बार लाइनों के सेट को पुनरावृत्त किया जाता है, तो बहुत सारे व्यर्थ के पुनरावृत्ति से बचने के लिए उन्हें पहले से निकालना भी सार्थक हो सकता है।
लैट्टी

0

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

यदि, हालांकि, इसे अनदेखा करना सुरक्षित है, या आप जानते हैं कि आपको कभी भी एक अमान्य स्ट्रिंग नहीं मिलेगा, तो अतिरिक्त शाखा की कोई आवश्यकता नहीं है।

व्यक्तिगत रूप से मैं हमेशा किसी भी अप्रत्याशित स्थिति के लिए कम से कम एक ट्रेस आउटपुट में डालने के लिए हूं - यह जीवन को बहुत आसान बनाता है जब आपके पास आउटपुट के साथ एक बग होता है जो आपको बताता है कि क्या गलत हुआ।


0

... मान लीजिए कि एक टेक्स्ट फाइल है जिसमें "a" से शुरू होने वाली लाइनें, "b" और अन्य लाइनों से शुरू होने वाली लाइनें हैं और मैं वास्तव में केवल पहली दो तरह की लाइनों के साथ काम करना चाहता हूं। मेरा कोड कुछ इस तरह दिखाई देगा (अजगर का उपयोग करते हुए, लेकिन इसे स्यूडोकोड के रूप में पढ़ें))

# ...
clear_lines() # removes every other line than those starting with "a" or "b"
for line in lines:
    if ...

मुझे if...then...elseकंस्ट्रक्शन से नफरत है । मैं पूरे मामले को टालूंगा:

process_lines_by_first_character (lines,  
                                  'a' => { |line| ... a code ... },
                                  'b' => { |line| ... b code ... } )
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.