ความกะทัดรัดเทียบกับการอ่านได้: พื้นกลาง
ในขณะที่คุณได้เห็นปัญหานี้ยอมรับว่าการแก้ปัญหาที่มีในระดับปานกลางยาวและค่อนข้างซ้ำ แต่สามารถอ่านได้สูง ( terdon ของและAB ของคำตอบทุบตี) เช่นเดียวกับผู้ที่มีความสั้นมาก แต่ไม่ใช่งานง่ายและมากน้อยตนเอง documenting (ทิมหลามและทุบตีคำตอบและคำตอบของperlเกล็นแจ็คแมน) วิธีการทั้งหมดนี้มีค่า
นอกจากนี้คุณยังสามารถแก้ปัญหานี้ด้วยรหัสที่อยู่ตรงกลางระหว่างความกะทัดรัดและความสามารถในการอ่าน วิธีนี้เกือบจะอ่านได้เหมือนโซลูชันที่ยาวกว่าและมีความยาวใกล้กับโซลูชันลึกลับที่มีขนาดเล็ก
#!/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."
ในโซลูชันทุบตีนี้ฉันได้รวมบรรทัดว่างไว้เพื่อปรับปรุงความสามารถในการอ่าน แต่คุณสามารถลบออกได้หากคุณต้องการให้สั้นลง
บรรทัดที่ว่างเปล่ารวมนี้เป็นจริงเพียงเล็กน้อยสั้นกว่าcompactified แตกต่างยังคงสามารถอ่านได้สวยของวิธีการแก้ปัญหาของทุบตี AB ข้อได้เปรียบหลักเหนือวิธีการคือ:
- มันใช้งานง่ายมากขึ้น
- ง่ายกว่าในการเปลี่ยนขอบเขตระหว่างคะแนน (หรือเพิ่มเกรดเพิ่มเติม)
- จะรับอินพุตโดยอัตโนมัติด้วยช่องว่างนำหน้าและต่อท้าย (ดูด้านล่างสำหรับคำอธิบายวิธีการ
((
))
ทำงาน)
ข้อดีทั้งสามข้อนี้เกิดขึ้นเนื่องจากวิธีนี้ใช้อินพุตของผู้ใช้เป็นข้อมูลตัวเลขมากกว่าโดยการตรวจสอบตัวเลขส่วนประกอบด้วยตนเอง
มันทำงานอย่างไร
- อ่านอินพุตจากผู้ใช้ ปล่อยให้พวกเขาใช้ปุ่มลูกศรเพื่อเลื่อนไปมาในข้อความที่พวกเขาป้อน (
-e
) และอย่าตีความ\
ว่าเป็นอักขระยกเว้น ( -r
)
สคริปต์นี้ไม่ใช่โซลูชันที่มีคุณสมบัติหลากหลาย - ดูด้านล่างสำหรับการปรับแต่ง - แต่คุณสมบัติที่มีประโยชน์เหล่านั้นทำให้มีความยาวได้สองอักขระเท่านั้น ฉันแนะนำให้ใช้เสมอ-r
กับread
เว้นแต่คุณจะรู้ว่าคุณต้องปล่อยให้ผู้ใช้\
หลบหนี
- หากผู้ใช้เขียน
q
หรือQ
ออกจาก
- สร้างอาเรย์แบบเชื่อมโยง ( ) เติมด้วยระดับตัวเลขสูงสุดที่เกี่ยวข้องกับแต่ละเกรดตัวอักษร
declare -A
- วนรอบคะแนนตัวอักษรจากต่ำสุดไปหาสูงสุดตรวจสอบว่าหมายเลขที่ผู้ใช้ให้นั้นต่ำพอที่จะตกอยู่ในช่วงตัวเลขของแต่ละเกรดของตัวอักษร
ด้วยการประเมินผลคณิตศาสตร์ชื่อตัวแปรไม่จำเป็นต้องขยายได้ด้วย((
))
$
(ในสถานการณ์อื่น ๆ ส่วนใหญ่หากคุณต้องการใช้ค่าของตัวแปรแทนชื่อคุณต้องทำสิ่งนี้ )
- ถ้ามันอยู่ในช่วงที่พิมพ์เกรดและทางออก
เพื่อความกระชับผมใช้ลัดวงจรและผู้ประกอบการ ( &&
) มากกว่า-if
then
- หากลูปเสร็จสิ้นและไม่ได้จับคู่ช่วงให้ถือว่าจำนวนที่ป้อนนั้นสูงเกินไป (มากกว่า 100) และบอกผู้ใช้ว่าอยู่นอกช่วง
วิธีนี้ทำงานด้วยการป้อนข้อมูลที่แปลก
เช่นเดียวกับโซลูชันระยะสั้นอื่นๆ ที่โพสต์สคริปต์นั้นไม่ตรวจสอบอินพุตก่อนสมมติว่าเป็นตัวเลข การประเมินผลเลขคณิต ( ((
))
) โดยอัตโนมัติแถบชั้นนำและต่อท้ายช่องว่างเพื่อที่ว่าไม่มีปัญหา แต่:
- ข้อมูลที่ไม่เหมือนกับตัวเลขจะถูกตีความว่าเป็น 0
- ด้วยอินพุตที่ดูเหมือนหมายเลข (เช่นถ้าขึ้นต้นด้วยหลัก) แต่มีอักขระที่ไม่ถูกต้องสคริปต์จะปล่อยข้อผิดพลาด
- การป้อนข้อมูล Multi-หลักเริ่มต้นด้วยการ
0
ถูกตีความว่าเป็นในฐานแปด ตัวอย่างเช่นสคริปต์จะบอกคุณ 77 คือ C ในขณะที่ 077 เป็น D แม้ว่าผู้ใช้บางรายอาจต้องการสิ่งนี้ส่วนใหญ่อาจไม่และอาจทำให้เกิดความสับสน
- ในด้านบวกเมื่อได้รับนิพจน์ทางคณิตศาสตร์สคริปต์นี้จะลดความซับซ้อนของมันโดยอัตโนมัติและกำหนดเกรดตัวอักษรที่เกี่ยวข้อง ตัวอย่างเช่นมันจะบอกคุณ 320/4 เป็น B
เวอร์ชันที่ขยายและโดดเด่นอย่างสมบูรณ์
ด้วยเหตุผลเหล่านั้นคุณอาจต้องการใช้บางสิ่งบางอย่างเช่นสคริปต์ที่ขยายนี้ซึ่งตรวจสอบเพื่อให้แน่ใจว่าอินพุตนั้นดีและรวมถึงการปรับปรุงอื่น ๆ
#!/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
นี่ยังเป็นโซลูชันที่กะทัดรัด
คุณลักษณะนี้เพิ่มอะไรบ้าง
จุดสำคัญของสคริปต์ที่ขยายนี้คือ:
- การตรวจสอบอินพุต สคริปต์ของ terdon ตรวจสอบอินพุตด้วยดังนั้นฉันจึงแสดงอีกวิธีหนึ่งซึ่งเสียสละบางอย่างสั้น ๆ แต่มีประสิทธิภาพมากขึ้นทำให้ผู้ใช้ป้อนช่องว่างนำหน้าและต่อท้ายและปฏิเสธที่จะอนุญาตให้มีการแสดงออกที่อาจหรือไม่ตั้งใจให้เป็นฐานแปด (เว้นแต่เป็นศูนย์) .
if [[ ! $response =~ ^[0-9]*$ ]] ...
- ฉันใช้
case
กับการขยายแบบวงกลมแทนการ[[
ใช้ตัวดำเนินการ=~
จับคู่นิพจน์ทั่วไป (ตามคำตอบของ terdon ) ฉันทำอย่างนั้นเพื่อแสดงว่า (และวิธี) มันก็สามารถทำได้เช่นกัน Globs และ regexps เป็นสองวิธีในการระบุรูปแบบที่ตรงกับข้อความและทั้งสองวิธีนี้ใช้ได้สำหรับแอปพลิเคชันนี้
- เช่นเดียวกับสคริปต์ทุบตีของ ABฉันได้ล้อมรอบทั้งหมดไว้ในลูปด้านนอก (ยกเว้นการสร้าง
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
เพื่อค้นหา bash และวนลูปที่สองต่อไปแทน
คำอธิบาย: case
ป้ายกำกับที่มี Globs
ฉันไม่ได้ใช้case
เพื่อหาตัวเลขตัวอักษรเกรดถังขยะที่ตกอยู่ใน (เช่นในคำตอบ bash ของ AB ) แต่ฉันจะใช้case
เพื่อตัดสินใจว่าควรพิจารณาการป้อนข้อมูลของผู้ใช้หรือไม่:
- หมายเลขที่ถูกต้อง
*( )@([1-9]*([0-9])|+(0))*( )
- คำสั่งเลิก
*( )[qQ]?([uU][iI][tT])*( )
- สิ่งอื่นใด (และการป้อนข้อมูลที่ไม่ถูกต้อง)
*
เหล่านี้เป็นglobs เปลือก
- แต่ละคนตามมาด้วยสิ่ง
)
ที่ไม่ตรงกันโดยการเปิดใด ๆ(
ซึ่งก็คือcase
ไวยากรณ์ของการแยกรูปแบบจากคำสั่งที่ทำงานเมื่อมีการจับคู่
;;
เป็นcase
ไวยากรณ์ของการระบุจุดสิ้นสุดของคำสั่งที่จะเรียกใช้สำหรับการจับคู่กรณี paticular (และไม่ควรทดสอบกรณีที่ตามมาหลังจากเรียกใช้)
เชลล์แบบปกติจะจัดให้มี*
อักขระเท่ากับศูนย์หรือมากกว่า?
เพื่อจับคู่อักขระหนึ่งตัวและคลาส / ช่วงอักขระใน[
]
วงเล็บ แต่ฉันกำลังใช้การขยายแบบวงกลมซึ่งไปไกลกว่านั้น การขยายแบบวงกลมจะถูกเปิดใช้งานตามค่าเริ่มต้นเมื่อใช้bash
แบบโต้ตอบ แต่จะถูกปิดใช้งานตามค่าเริ่มต้นเมื่อเรียกใช้สคริปต์ shopt -s extglob
คำสั่งที่ด้านบนของสคริปต์เปลี่ยนมันใน
คำอธิบาย: ขยาย Globbing
*( )@([1-9]*([0-9])|+(0))*( )
ซึ่งจะตรวจสอบอินพุตตัวเลขตรงกับลำดับของ:
- เป็นศูนย์หรือมากกว่าช่องว่าง (
*( )
) *(
)
การแข่งขันสร้างศูนย์หรือมากกว่าของรูปแบบในวงเล็บซึ่งนี่เป็นเพียงพื้นที่
จริง ๆ แล้วมีช่องว่างแนวนอนช่องว่างและแท็บสองประเภทและบ่อยครั้งที่ต้องการจับคู่แท็บด้วยเช่นกัน แต่ฉันไม่ต้องกังวลเกี่ยวกับเรื่องนี้ที่นี่เพราะสคริปต์นี้เขียนขึ้นสำหรับการป้อนข้อมูลด้วยตนเองและการ-e
ตั้งค่าสถานะเพื่อread
เปิดใช้งาน GNU readline นี่คือเพื่อให้ผู้ใช้สามารถย้ายไปมาในข้อความของพวกเขาด้วยปุ่มลูกศรซ้ายและขวา แต่มันมีผลข้างเคียงของการป้องกันโดยทั่วไปแท็บจากการป้อนอย่างแท้จริง
- หนึ่งเหตุการณ์ (
@(
)
) ของทั้ง ( |
):
- ตัวเลขที่ไม่ใช่ศูนย์ (
[1-9]
) ตามด้วยศูนย์หรือมากกว่า (*(
)
) ของหลักใด ๆ ( [0-9]
)
- หนึ่งหรือมากกว่า (
+(
)
) 0
ของ
- เป็นศูนย์หรือมากกว่าเว้นวรรค (
*( )
) อีกครั้ง
*( )[qQ]?([uU][iI][tT])*( )
ซึ่งตรวจสอบคำสั่ง quitตรงกับลำดับของ:
- เป็นศูนย์หรือมากกว่าช่องว่าง (
*( )
)
q
หรือQ
( [qQ]
)
- เลือกได้ - เช่นศูนย์หรือหนึ่งเหตุการณ์ (
?(
)
) - จาก:
u
หรือU
( [uU]
) ตามด้วยi
หรือI
( [iI]
) ตามด้วยt
หรือT
( [tT]
)
- เป็นศูนย์หรือมากกว่าเว้นวรรค (
*( )
) อีกครั้ง
Variant: การตรวจสอบความถูกต้องของอินพุตด้วยนิพจน์ปกติเพิ่มเติม
หากคุณต้องการที่จะทดสอบการป้อนข้อมูลของผู้ใช้กับการแสดงออกปกติมากกว่า shell glob คุณอาจต้องการใช้รุ่นนี้ซึ่งใช้งานได้เหมือนกัน แต่ใช้[[
และ=~
(เช่นในคำตอบของ 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
ข้อดีที่เป็นไปได้ของวิธีนี้คือ:
ในกรณีพิเศษนี้ไวยากรณ์นั้นง่ายกว่าเล็กน้อยอย่างน้อยก็ในรูปแบบที่สองซึ่งฉันจะตรวจสอบคำสั่ง quit นี่เป็นเพราะฉันสามารถตั้งค่าnocasematch
ตัวเลือกของเชลล์แล้วทุกกรณีของq
และquit
ครอบคลุมโดยอัตโนมัติ
นั่นคือสิ่งที่shopt -s nocasematch
คำสั่งทำ shopt -s extglob
คำสั่งที่ถูกละไว้เป็น globbing ไม่ได้ใช้ในรุ่นนี้
ทักษะการแสดงออกปกติเป็นเรื่องธรรมดามากกว่าความสามารถใน extglobs ของ bash
คำอธิบาย: นิพจน์ปกติ
สำหรับรูปแบบที่ระบุทางด้านขวาของ=~
โอเปอเรเตอร์นี่คือวิธีการที่นิพจน์ทั่วไปทำงาน
^\ *([1-9][0-9]*|0+)\ *$
ซึ่งจะตรวจสอบอินพุตตัวเลขตรงกับลำดับของ:
- จุดเริ่มต้น - คือขอบซ้าย - ของบรรทัด (
^
)
*
ช่องว่างเป็นศูนย์หรือมากกว่า ( , postfix ที่ใช้) ตามปกติแล้วพื้นที่ไม่จำเป็นต้องมีการ\
หลีกเลี่ยงในนิพจน์ปกติ แต่จำเป็นต้องมี[[
เพื่อป้องกันข้อผิดพลาดทางไวยากรณ์
- สตริงย่อย (
(
)
) ที่เป็นหนึ่งหรืออื่น ๆ ( |
) ของ:
[1-9][0-9]*
: ตัวเลขที่ไม่ใช่ศูนย์ ( [1-9]
) ตามด้วยศูนย์หรือมากกว่า ( *
, postfix ที่ใช้) ของตัวเลขใด ๆ ( [0-9]
)
0+
หนึ่งหรือมากกว่า ( +
, postfix ประยุกต์) 0
ของ
- ช่องว่างเป็นศูนย์หรือมากกว่า (
\ *
) เช่นก่อน
- จุดสิ้นสุด - เช่น, ขอบด้านขวา - ของเส้น (
$
)
ต่างจากcase
ป้ายกำกับที่ตรงกับนิพจน์ทั้งหมดที่ถูกทดสอบ=~
ส่งคืนค่าจริงหากส่วนใดส่วนหนึ่งของนิพจน์ทางซ้ายตรงกับรูปแบบที่กำหนดให้เป็นนิพจน์ทางขวา นี่คือสาเหตุที่^
และ$
anchors ระบุจุดเริ่มต้นและจุดสิ้นสุดของบรรทัดที่นี่และไม่สอดคล้อง syntactically กับสิ่งที่ปรากฏในวิธีการที่มีcase
และ extglobs
วงเล็บที่มีความจำเป็นเพื่อให้^
และ$
ผูกไว้กับความร้าวฉานของและ[1-9][0-9]*
0+
มิฉะนั้นจะเป็นการแยก^[1-9][0-9]*
และ0+$
และจับคู่อินพุตใด ๆ ที่เริ่มต้นด้วยตัวเลขที่ไม่ใช่ศูนย์หรือลงท้ายด้วย0
(หรือทั้งสองอย่างซึ่งอาจยังคงมีตัวเลขที่ไม่ใช่ตัวเลขอยู่)
^\ *q(uit)?\ *$
ซึ่งตรวจสอบคำสั่ง quitตรงกับลำดับของ:
- จุดเริ่มต้นของบรรทัด (
^
)
- ช่องว่างเป็นศูนย์หรือมากกว่า (
\ *
ดูคำอธิบายข้างต้น)
q
ตัวอักษร หรือQ
ตั้งแต่shopt nocasematch
เปิดใช้งาน
- เลือกได้ - เช่นศูนย์หรือหนึ่งเหตุการณ์ (postfix
?
) - ของสตริงย่อย ( (
)
):
u
ตามด้วยตามด้วยi
t
หรือเนื่องจากshopt nocasematch
เปิดใช้งานu
อาจเป็นU
; อิสระi
อาจจะI
; และเป็นอิสระ, อาจจะt
T
(นั่นคือความเป็นไปได้ไม่จำกัดuit
และUIT
.)
- ศูนย์หรือมากกว่าช่องว่างอีกครั้ง (
\ *
)
- จุดสิ้นสุดของบรรทัด (
$
)