ब्रेविटी बनाम पठनीयता: एक मध्य मैदान
जैसा कि आपने देखा है, यह समस्या उन समाधानों को स्वीकार करती है जो मध्यम लंबे और कुछ हद तक दोहराए जाने वाले हैं, लेकिन बहुत पठनीय हैं ( टेर्डन और एबी के बाश उत्तर), साथ ही साथ वे जो बहुत कम लेकिन गैर-सहज और बहुत कम स्व-दस्तावेजीकरण (टिम पायथन) हैं और बैश जवाब और ग्लेन जैकमैन के पर्ल जवाब )। ये सभी दृष्टिकोण मूल्यवान हैं।
आप इस समस्या को कॉम्पैक्टनेस और पठनीयता के बीच निरंतरता के बीच में कोड के साथ भी हल कर सकते हैं। यह दृष्टिकोण लगभग लंबे समाधानों के रूप में पठनीय है, छोटे, गूढ़ समाधानों की लंबाई के साथ।
#!/usr/bin/env bash
read -erp 'Enter numeric grade (q to quit): '
case $REPLY in [qQ]) exit;; esac
declare -A cutoffs
cutoffs[F]=59 cutoffs[D]=69 cutoffs[C]=79 cutoffs[B]=89 cutoffs[A]=100
for letter in F D C B A; do
((REPLY <= cutoffs[$letter])) && { echo $letter; exit; }
done
echo "Grade out of range."
इस बैश समाधान में, मैंने पठनीयता बढ़ाने के लिए कुछ रिक्त लाइनें शामिल की हैं, लेकिन यदि आप इसे और भी कम चाहते हैं तो आप उन्हें हटा सकते हैं।
खाली लाइनों को शामिल किया गया, यह वास्तव में एबी के बैश समाधान के एक कॉम्पैक्ट, अभी भी बहुत पठनीय संस्करण की तुलना में थोड़ा कम है । उस पद्धति पर इसके मुख्य लाभ हैं:
- यह अधिक सहज है।
- ग्रेड के बीच की सीमाओं को बदलना आसान है (या अतिरिक्त ग्रेड जोड़ें)।
- यह स्वचालित रूप से प्रमुख और अनुगामी रिक्त स्थान के साथ इनपुट को स्वीकार करता है (कैसे
(( ))काम करता है की व्याख्या के लिए नीचे देखें )।
ये सभी तीन फायदे हैं क्योंकि यह विधि उपयोगकर्ता के इनपुट को संख्यात्मक डेटा के रूप में उपयोग करती है बजाय इसके घटक अंकों को मैन्युअल रूप से जांचने के लिए।
यह काम किस प्रकार करता है
- उपयोगकर्ता से इनपुट पढ़ें। उन्हें तीर कुंजी का उपयोग उनके द्वारा दर्ज किए गए पाठ में घूमने के लिए करें (
-e) और \एक एस्केप चरित्र के रूप में व्याख्या न करें ( -r)।
यह स्क्रिप्ट एक सुविधा संपन्न समाधान नहीं है - एक शोधन के लिए नीचे देखें - लेकिन वे उपयोगी विशेषताएं केवल इसे दो अक्षर लंबे समय तक बनाती हैं। मैं हमेशा के -rसाथ उपयोग करने की सलाह देता हूं read, जब तक कि आपको नहीं पता कि आपको उपयोगकर्ता की आपूर्ति से \बचने की आवश्यकता है।
- यदि उपयोगकर्ता ने लिखा है
qया Qछोड़ दिया है।
- एक साहचर्य सरणी बनाएँ (
declare -A)। प्रत्येक अक्षर ग्रेड के साथ जुड़े उच्चतम संख्यात्मक ग्रेड के साथ इसे आबाद करें।
- पत्र ग्रेड के माध्यम से निम्नतम से उच्चतम तक की जाँच करें, अगर उपयोगकर्ता द्वारा प्रदान की गई संख्या प्रत्येक अक्षर ग्रेड की संख्यात्मक सीमा में गिरने के लिए पर्याप्त है। अंकगणितीय मूल्यांकन के
साथ (( )), चर नामों के साथ विस्तार करने की आवश्यकता नहीं है $। (अधिकांश अन्य स्थितियों में, यदि आप इसके नाम के स्थान पर किसी चर के मान का उपयोग करना चाहते हैं, तो आपको यह अवश्य करना चाहिए ।)
- यदि यह सीमा में आता है, तो ग्रेड प्रिंट करें और बाहर निकलें ।
संक्षिप्तता के लिए, मैं शॉर्ट सर्किट का उपयोग और ऑपरेटर ( &&बल्कि एक से) if- then।
- यदि लूप खत्म हो जाता है और कोई रेंज मैच नहीं हुई है, तो मान लें कि दर्ज की गई संख्या बहुत अधिक है (100 से अधिक) और उपयोगकर्ता को बताएं कि यह सीमा से बाहर है।
यह कैसे अजीब है, अजीब इनपुट के साथ
पोस्ट किए गए अन्य छोटे समाधानों की तरह , यह स्क्रिप्ट एक संख्या मानने से पहले इनपुट की जांच नहीं करती है। अंकगणित मूल्यांकन ( (( ))) स्वचालित रूप से प्रमुख और रिक्त स्थान को अनुगामी स्ट्रिप्स, इसलिए है कि कोई समस्या नहीं है, लेकिन:
- इनपुट जो किसी संख्या की तरह नहीं दिखता है उसे 0 के रूप में व्याख्या किया गया है।
- इनपुट के साथ जो एक संख्या की तरह दिखता है (यानी, यदि यह एक अंक से शुरू होता है) लेकिन इसमें अमान्य वर्ण हैं, तो स्क्रिप्ट त्रुटियों का उत्सर्जन करती है।
- मल्टी अंकों इनपुट के साथ शुरू
0किया गया है किया जा रहा है के रूप में व्याख्या में अष्टाधारी । उदाहरण के लिए, स्क्रिप्ट आपको बताएगी 77 एक सी है, जबकि 077 एक डी है। हालांकि कुछ उपयोगकर्ता यह चाहते हैं, सबसे अधिक शायद नहीं और यह भ्रम पैदा कर सकता है।
- प्लस साइड पर, जब एक अंकगणितीय अभिव्यक्ति दी जाती है, तो यह स्क्रिप्ट स्वचालित रूप से इसे सरल बनाती है और संबंधित पत्र ग्रेड को निर्धारित करती है। उदाहरण के लिए, यह आपको बताएगा कि 320/4 एक बी है।
एक विस्तारित, पूरी तरह से चित्रित संस्करण
उन कारणों के लिए, आप इस विस्तारित स्क्रिप्ट की तरह कुछ का उपयोग करना चाह सकते हैं, जो यह सुनिश्चित करने के लिए जांचता है कि इनपुट अच्छा है, और कुछ अन्य संवर्द्धन भी शामिल हैं।
#!/usr/bin/env bash
shopt -s extglob
declare -A cutoffs
cutoffs[F]=59 cutoffs[D]=69 cutoffs[C]=79 cutoffs[B]=89 cutoffs[A]=100
while read -erp 'Enter numeric grade (q to quit): '; do
case $REPLY in # allow leading/trailing spaces, but not octal (e.g. "03")
*( )@([1-9]*([0-9])|+(0))*( )) ;;
*( )[qQ]?([uU][iI][tT])*( )) exit;;
*) echo "I don't understand that number."; continue;;
esac
for letter in F D C B A; do
((REPLY <= cutoffs[$letter])) && { echo $letter; continue 2; }
done
echo "Grade out of range."
done
यह अभी भी एक सुंदर कॉम्पैक्ट समाधान है।
यह क्या सुविधाएँ जोड़ता है?
इस विस्तारित स्क्रिप्ट के मुख्य बिंदु हैं:
- इनपुट सत्यापन टेरडॉन की स्क्रिप्ट इनपुट की जांच करती है , इसलिए मैं एक और तरीका दिखाता हूं, जो कुछ संक्षिप्तता का त्याग करता है, लेकिन अधिक मजबूत होता है, जिससे उपयोगकर्ता अग्रणी और अनुगामी स्थानों में प्रवेश कर सकता है और एक अभिव्यक्ति की अनुमति देने से इनकार कर सकता है जो ऑक्टल के रूप में अभिप्रेत नहीं हो सकती है (जब तक कि शून्य नहीं है) ।
if [[ ! $response =~ ^[0-9]*$ ]] ...
- मैं का उपयोग किया है
caseके साथ ग्लोबिंग विस्तारित बजाय [[साथ =~ नियमित अभिव्यक्ति मिलान (के रूप में ऑपरेटर terdon का जवाब )। मैंने ऐसा करने के लिए (और कैसे) यह भी किया जा सकता है। ग्लोब और रेगेक्स, पाठ से मेल खाने वाले पैटर्न को निर्दिष्ट करने के दो तरीके हैं, और या तो विधि इस एप्लिकेशन के लिए ठीक है।
- जैसा अटल बिहारी के bash स्क्रिप्ट , मैं एक बाहरी पाश में पूरी बात (के प्रारंभिक निर्माण को छोड़कर संलग्न किया है
cutoffsसरणी)। यह संख्याओं का अनुरोध करता है और टर्मिनल इनपुट उपलब्ध होने तक संबंधित पत्र ग्रेड देता है और उपयोगकर्ता ने इसे छोड़ने के लिए नहीं कहा है। आपके प्रश्न में कोड के आसपास do... देखकर done, ऐसा लगता है कि आप ऐसा चाहते हैं।
- छोड़ने को आसान बनाने के लिए, मैं किसी भी मामले को असंवेदनशील
qया स्वीकार करता हूं quit।
यह स्क्रिप्ट कुछ निर्माणों का उपयोग करती है जो नौसिखियों के लिए अपरिचित हो सकते हैं; वे नीचे विस्तृत हैं।
स्पष्टीकरण: का उपयोग continue
जब मैं बाहरी whileलूप के शेष शरीर को छोड़ना चाहता हूं , तो मैं continueकमांड का उपयोग करता हूं । यह इसे लूप के शीर्ष पर वापस लाता है, अधिक इनपुट पढ़ने और एक और पुनरावृत्ति चलाने के लिए।
पहली बार जब मैं ऐसा करता हूं, तो मैं जो एकमात्र लूप होता हूं वह बाहरी whileलूप होता है, इसलिए मैं continueबिना किसी तर्क के कॉल कर सकता हूं । (मैं एक में हूँ caseनिर्माण, लेकिन उस के संचालन को प्रभावित नहीं करता है breakया continue।)
*) echo "I don't understand that number."; continue;;
हालांकि, दूसरी बार, मैं एक आंतरिक forलूप में हूं जो खुद बाहरी whileलूप के अंदर नेस्टेड है । अगर मैं continueबिना किसी तर्क के उपयोग करता हूं , तो यह बराबर continue 1होगा और forबाहरी whileलूप के बजाय आंतरिक लूप जारी रखेगा ।
((REPLY <= cutoffs[$letter])) && { echo $letter; continue 2; }
तो उस स्थिति में, मैं continue 2बैश खोजने और दूसरे लूप को जारी रखने के लिए उपयोग करता हूं ।
स्पष्टीकरण: caseग्लब्स के साथ लेबल
मैं यह caseपता लगाने के लिए उपयोग नहीं करता हूं कि कौन से अक्षर ग्रेड बिन एक संख्या में गिरता है (जैसे एबी के बैश उत्तर में )। लेकिन मैं यह caseतय करने के लिए उपयोग करता हूं कि क्या उपयोगकर्ता के इनपुट पर विचार किया जाना चाहिए:
- एक वैध संख्या,
*( )@([1-9]*([0-9])|+(0))*( )
- पद छोड़ना,
*( )[qQ]?([uU][iI][tT])*( )
- कुछ और (और इस तरह अमान्य इनपुट),
*
ये शेल ग्लब्स हैं ।
- प्रत्येक के बाद एक है
)कि किसी भी उद्घाटन से मेल नहीं खाता है (, जो caseकि जब यह मिलान होता है तब चलने वाले आदेशों से एक पैटर्न को अलग करने के लिए वाक्यविन्यास है।
;;है caseएक paticular मामले मैच के लिए चलाने के लिए (और है कि बाद में कोई मामलों उन्हें चलाने के बाद परीक्षण किया जाना चाहिए) आदेशों के अंत का संकेत के लिए की वाक्य रचना।
साधारण गोलाकार गोलाकार *शून्य या अधिक वर्णों का मिलान करने के लिए प्रदान करता है, ?बिलकुल एक वर्ण और [ ]कोष्ठक में वर्ण वर्गों / श्रेणियों से मेल खाने के लिए । लेकिन मैं विस्तारित ग्लोबिंग का उपयोग कर रहा हूं , जो उससे आगे जाता है। bashअंतःक्रियात्मक रूप से उपयोग करते समय विस्तारित ग्लोबिंग डिफ़ॉल्ट रूप से सक्षम होता है , लेकिन स्क्रिप्ट चलाते समय डिफ़ॉल्ट रूप से अक्षम होता है। shopt -s extglobस्क्रिप्ट के शीर्ष पर आदेश उस पर बदल जाता है।
स्पष्टीकरण: विस्तारित ग्लोबिंग
*( )@([1-9]*([0-9])|+(0))*( ), जो संख्यात्मक इनपुट के लिए जाँच करता है , के अनुक्रम से मेल खाता है:
- शून्य या अधिक रिक्त स्थान (
*( ))। *( )निर्माण मैचों शून्य या कोष्ठक में पैटर्न, सिर्फ एक जगह नहीं है यहाँ जिनमें से अधिक।
वास्तव में दो प्रकार के क्षैतिज व्हाट्सएप, स्पेस और टैब हैं, और अक्सर यह टैब से मिलान करने के लिए भी वांछनीय है। लेकिन मैं यहाँ उस बारे में चिंता नहीं कर रहा हूँ, क्योंकि यह स्क्रिप्ट मैन्युअल, इंटरेक्टिव इनपुट और -eझंडे को readGNU रीडलाइन को सक्षम करने के लिए लिखा गया है। ऐसा इसलिए है कि उपयोगकर्ता अपने पाठ में बाएं और दाएं तीर कुंजियों के साथ आगे-पीछे हो सकता है, लेकिन इसमें आम तौर पर टैब को शाब्दिक रूप से प्रवेश करने से रोकने का दुष्प्रभाव है।
- दोनों में से एक घटना
@( )( |):
- एक अंकीय अंक (
[1-9]) शून्य या अधिक ( *( )) किसी भी अंक ( [0-9]) के बाद।
- एक या अधिक (
+( )) का 0।
- शून्य या अधिक रिक्त स्थान (
*( )), फिर से।
*( )[qQ]?([uU][iI][tT])*( ), जो छोड़ दिया आदेश के लिए जाँच करता है , के एक क्रम से मेल खाता है:
- शून्य या अधिक रिक्त स्थान (
*( ))।
qया Q( [qQ])।
- वैकल्पिक रूप से - अर्थात, शून्य या एक घटना (
?( )) का:
uया U( [uU]) के बाद iया I( [iI]) के बाद tया T( [tT])।
- शून्य या अधिक रिक्त स्थान (
*( )), फिर से।
वेरिएंट: एक विस्तारित नियमित अभिव्यक्ति के साथ वैधता इनपुट
आप एक खोल ग्लोब के बजाय एक नियमित अभिव्यक्ति के खिलाफ उपयोगकर्ता के इनपुट परीक्षण करने के लिए पसंद करते हैं, तो आप इस संस्करण है, जो एक ही है, लेकिन का उपयोग करता है काम करता है का उपयोग करना पसंद कर सकते हैं [[और =~(जैसे में terdon का जवाब ) के बजाय caseऔर ग्लोबिंग बढ़ाया।
#!/usr/bin/env bash
shopt -s nocasematch
declare -A cutoffs
cutoffs[F]=59 cutoffs[D]=69 cutoffs[C]=79 cutoffs[B]=89 cutoffs[A]=100
while read -erp 'Enter numeric grade (q to quit): '; do
# allow leading/trailing spaces, but not octal (e.g., "03")
if [[ ! $REPLY =~ ^\ *([1-9][0-9]*|0+)\ *$ ]]; then
[[ $REPLY =~ ^\ *q(uit)?\ *$ ]] && exit
echo "I don't understand that number."; continue
fi
for letter in F D C B A; do
((REPLY <= cutoffs[$letter])) && { echo $letter; continue 2; }
done
echo "Grade out of range."
done
इस दृष्टिकोण के संभावित लाभ इस प्रकार हैं:
इस विशेष मामले में, वाक्यविन्यास थोड़ा सरल है, कम से कम दूसरे पैटर्न में, जहां मैं छोड़ दिया कमांड के लिए जांच करता हूं। इसका कारण यह है कि मैं सेट करने में सक्षम था nocasematchखोल विकल्प, और तब सभी मामले की वेरिएंट qऔर quitस्वचालित रूप से कवर किया गया।
वही shopt -s nocasematchकाम करता है। shopt -s extglobआदेश ग्लोबिंग इस संस्करण में नहीं किया जाता है के रूप में छोड़ दिया जाता है।
बैश के एक्सग्लोब में प्रवीणता की तुलना में नियमित अभिव्यक्ति कौशल अधिक सामान्य हैं।
स्पष्टीकरण: नियमित अभिव्यक्तियाँ
=~ऑपरेटर के दाईं ओर निर्दिष्ट किए गए पैटर्न के लिए , यहां बताया गया है कि उन नियमित अभिव्यक्तियाँ कैसे काम करती हैं।
^\ *([1-9][0-9]*|0+)\ *$, जो संख्यात्मक इनपुट के लिए जाँच करता है , के अनुक्रम से मेल खाता है:
- शुरुआत - यानी, बाएं किनारे - लाइन की (
^)।
- शून्य या अधिक (
*, लागू पोस्टफ़िक्स) रिक्त स्थान। एक स्थान को \नियमित रूप से अभिव्यक्ति में नियमित रूप से होने की आवश्यकता नहीं है , लेकिन [[एक सिंटैक्स त्रुटि को रोकने के लिए इसके साथ की आवश्यकता है ।
- एक विकल्प (
( )) जो एक या दूसरे का है ( |):
[1-9][0-9]*: किसी अंक का [1-9]शून्य या उससे अधिक ( *, अनुप्रयुक्त पोस्टफिक्स) के बाद एक नोनोजेरो अंक ( [0-9])।
0+: एक या अधिक ( +के, लागू पोस्टफ़िक्स) 0।
- पहले की तरह शून्य या अधिक स्थान (
\ *)।
- अंत - यानी, दाहिने किनारे - लाइन का (
$)।
caseलेबल्स के विपरीत , जो पूरी अभिव्यक्ति के परीक्षण के विरुद्ध मेल खाता है, यह =~सही है अगर इसके बाएं हाथ के किसी भी हिस्से को उसके दाहिने हाथ की अभिव्यक्ति के रूप में दिए गए पैटर्न से मेल खाता है। यही कारण है कि ^और $एंकर, लाइन की शुरुआत और अंत को निर्दिष्ट करते हैं, यहां आवश्यक हैं, और इसके साथ caseऔर एक्सग्लब्स के साथ विधि में प्रकट होने वाली किसी भी चीज़ के लिए वाक्यात्मक रूप से मेल नहीं खाते हैं।
कोष्ठक बनाने के लिए ^और $के विच्छेदन के लिए बाध्य करने के लिए आवश्यक हैं [1-9][0-9]*और 0+। अन्यथा यह की अलगाव हो जाएगा ^[1-9][0-9]*और 0+$, और किसी भी इनपुट से मेल एक अशून्य अंकों के साथ शुरू या एक के साथ समाप्त 0(या दोनों, जो अभी भी बीच में गैर अंक शामिल हो सकता है)।
^\ *q(uit)?\ *$, जो छोड़ दिया आदेश के लिए जाँच करता है , के एक क्रम से मेल खाता है:
- लाइन की शुरुआत (
^)।
- शून्य या अधिक रिक्त स्थान (
\ *, स्पष्टीकरण के ऊपर देखें)।
- अक्षर
q। या Q, चूंकि shopt nocasematchसक्षम है।
- वैकल्पिक रूप से - यानी, शून्य या एक घटना (उपसर्ग
?) - सबस्ट्रिंग ( ( )) की:
u, उसके बाद i, उसके बाद t। या, shopt nocasematchसक्षम uहोने के बाद से हो सकता है U; स्वतंत्र रूप से, iहो सकता है I; और स्वतंत्र रूप से, tहो सकता है T। (अर्थात्, संभावनाएँ सीमित नहीं हैं uitऔर UIT)
- शून्य या अधिक रिक्त स्थान फिर से (
\ *)।
- पंक्ति का अंत (
$)।