ब्रेविटी बनाम पठनीयता: एक मध्य मैदान
जैसा कि आपने देखा है, यह समस्या उन समाधानों को स्वीकार करती है जो मध्यम लंबे और कुछ हद तक दोहराए जाने वाले हैं, लेकिन बहुत पठनीय हैं ( टेर्डन और एबी के बाश उत्तर), साथ ही साथ वे जो बहुत कम लेकिन गैर-सहज और बहुत कम स्व-दस्तावेजीकरण (टिम पायथन) हैं और बैश जवाब और ग्लेन जैकमैन के पर्ल जवाब )। ये सभी दृष्टिकोण मूल्यवान हैं।
आप इस समस्या को कॉम्पैक्टनेस और पठनीयता के बीच निरंतरता के बीच में कोड के साथ भी हल कर सकते हैं। यह दृष्टिकोण लगभग लंबे समाधानों के रूप में पठनीय है, छोटे, गूढ़ समाधानों की लंबाई के साथ।
#!/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
झंडे को read
GNU रीडलाइन को सक्षम करने के लिए लिखा गया है। ऐसा इसलिए है कि उपयोगकर्ता अपने पाठ में बाएं और दाएं तीर कुंजियों के साथ आगे-पीछे हो सकता है, लेकिन इसमें आम तौर पर टैब को शाब्दिक रूप से प्रवेश करने से रोकने का दुष्प्रभाव है।
- दोनों में से एक घटना
@(
)
( |
):
- एक अंकीय अंक (
[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
)
- शून्य या अधिक रिक्त स्थान फिर से (
\ *
)।
- पंक्ति का अंत (
$
)।