Answers:
如您所见,此问题允许使用长度适中,有些重复但可读性强的解决方案(Terdon和AB的 bash答案),以及解决方案很短但不直观且自我记录少的解决方案(Tim的python)和bash答案以及glenn jackman的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."
在此bash解决方案中,我加入了一些空白行以增强可读性,但是如果您希望更短一些,可以将其删除。
空行在内,这比实际只略短一个紧凑化,仍然是相当可读变种的AB的bash的解决方案。与该方法相比,它的主要优点是:
((
))
工作原理的说明,请参见下文)。之所以出现这三个优点,是因为此方法使用用户的输入作为数字数据,而不是通过手动检查其组成数字。
-e
)中移动,不要将其解释\
为转义字符(-r
)。-r
与一起使用read
,除非您知道需要让用户供应\
转义。q
或Q
,请退出。declare -A
)。用与每个字母等级相关的最高数字等级填充它。((
))
算术评估,不需要使用扩展变量名$
。(在大多数其他情况下,如果要使用变量的值代替其名称,则必须执行此操作。)&&
)而不是if
- then
。像发布的其他简短解决方案一样,该脚本在假定输入数字之前不会检查输入。算术评估(((
))
)自动删除开头和结尾的空格,所以这没问题,但是:
0
被解释为在八进制。例如,脚本将告诉您77是C,而077是D。尽管有些用户可能想要这样做,但很可能不需要,这可能会引起混乱。由于这些原因,您可能想要使用类似于此扩展脚本的内容,该脚本进行检查以确保输入正确,并包括其他一些增强功能。
#!/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
了扩展的globing,而不是[[
使用了=~
正则表达式匹配运算符(如terdon的answer)。我这样做是为了表明(以及如何)也可以这样做。globs和regexps是指定与文本匹配的模式的两种方法,对于该应用程序,这两种方法都适用。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
带有标签的标签我不使用case
找出哪些字母等级斌一批落入(如AB的bash的答案)。但我确实case
决定是否应考虑用户的输入:
*( )@([1-9]*([0-9])|+(0))*( )
*( )[qQ]?([uU][iI][tT])*( )
*
这些是贝壳球。
)
没有任何开头的(
,这是case
的语法,用于将模式与匹配时运行的命令分开。;;
是case
的语法,用于指示要针对特定的大小写匹配运行的命令的结尾(并且在运行它们之后不应测试任何后续的大小写)。普通shell *
匹配提供了零个或多个字符的?
匹配,一个字符的精确匹配以及[
]
括号中的字符类/范围。但是我正在使用扩展的globlob,这还不止于此。bash
交互式使用时,默认情况下会启用扩展的glob ,但是运行脚本时,默认情况下会禁用扩展的glob 。shopt -s extglob
脚本顶部的命令将其打开。
*( )@([1-9]*([0-9])|+(0))*( )
,它检查数字输入,与以下序列匹配:
*( )
)。该*(
)
结构匹配括号中的零个或多个模式,这里仅是一个空格。-e
标志read
。这样一来,用户可以使用左右箭头键在其文本中来回移动,但是这样做的副作用是,通常会阻止按实际输入选项卡。@(
)
)之一出现(|
):
[1-9]
),后接零个或多个(*(
)
)任意数字([0-9]
)。+(
)
)0
。*( )
再次零个或多个空格()。*( )[qQ]?([uU][iI][tT])*( )
,它检查quit命令,匹配以下序列:
*( )
)。q
或Q
([qQ]
)。?(
)
):
u
或U
([uU]
)后跟i
或I
([iI]
)后跟t
或T
([tT]
)。*( )
再次零个或多个空格()。如果您希望针对正则表达式而不是Shell Glob测试用户的输入,则您可能更喜欢使用此版本,该版本的工作原理相同,但是使用[[
和=~
(例如,在Terdon的答案中)代替case
和扩展的Glob。
#!/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
shell选项,然后将所有案件的变种q
,并quit
会自动覆盖。
这就是shopt -s nocasematch
命令的作用。该shopt -s extglob
命令被省略,因为此版本未使用通配符。
在bash的extglob中,正则表达技能比熟练程度更普遍。
至于在=~
运算符右侧指定的模式,以下是这些正则表达式的工作方式。
^\ *([1-9][0-9]*|0+)\ *$
,它检查数字输入,与以下序列匹配:
^
)的起点,即左边缘。*
,应用后缀)空格。通常不需要\
在正则表达式中转义空格,但这是[[
防止语法错误所必需的。(
)
),它是以下其中一个或另一个(|
):
[1-9][0-9]*
:一个非零数字([1-9]
),后跟*
任意一个数字()的零个或多个(,应用后缀[0-9]
)。0+
:的一个或多个(+
应用后缀)0
。\ *
与以前一样,零个或多个空格()。$
)的末端,即右边缘。与case
标签不同,标签与要测试的整个表达式匹配,=~
如果其左侧表达式的任何部分与作为其右侧表达式给出的模式匹配,则返回true。这就是为什么在这里需要^
and $
锚点(指定行的开始和结尾),并且在语法上不对应于with case
和extglobs 方法中出现的任何内容。
需要括号,使^
和$
结合的脱节[1-9][0-9]*
和0+
。否则会使的析取^[1-9][0-9]*
和0+$
,并匹配任何输入开始以非零的数字或与结束0
(或两者,这可能仍然包括之间的非数字)。
^\ *q(uit)?\ *$
,它检查quit命令,匹配以下序列:
^
)。\ *
,请参见上面的说明)。q
。或Q
,因为shopt nocasematch
已启用。?
)子字符串((
)
)出现零次或一次(后缀):
u
,i
其次是t
。或者,由于shopt nocasematch
启用u
可能是U
;独立地,i
可能是I
;独立地t
可能是T
。(即,可能性并不限于uit
和UIT
)。\ *
)。$
)。您已经有了基本的想法。如果要使用此代码bash
(这是一个合理的选择,因为它是Ubuntu和大多数其他Linux上的默认shell),则不能使用,case
因为它不了解范围。相反,您可以使用if
/ else
:
#!/usr/bin/env bash
read -p "Please enter your choice: " response
## If the response given did not consist entirely of digits
if [[ ! $response =~ ^[0-9]*$ ]]
then
## If it was Quit or quit, exit
[[ $response =~ [Qq]uit ]] && exit
## If it wasn't quit or Quit but wasn't a number either,
## print an error message and quit.
echo "Please enter a number between 0 and 100 or \"quit\" to exit" && exit
fi
## Process the other choices
if [ $response -le 59 ]
then
echo "F"
elif [ $response -le 69 ]
then
echo "D"
elif [ $response -le 79 ]
then
echo "C"
elif [ $response -le 89 ]
then
echo "B"
elif [ $response -le 100 ]
then
echo "A"
elif [ $response -gt 100 ]
then
echo "Please enter a number between 0 and 100"
exit
fi
-ge
测试可以消除,据推测,由于您使用elif
。没有爱(( $response < X ))
吗?
(( $response < X ))
,当然,但是我发现它更清晰了,OP显然是bash脚本的新成员。
#!/bin/bash
while true
do
read -p "Please enter your choice: " choice
case "$choice"
in
[0-9]|[1-5][0-9])
echo "F"
;;
6[0-9])
echo "D"
;;
7[0-9])
echo "C"
;;
8[0-9])
echo "B"
;;
9[0-9]|100)
echo "A"
;;
[Qq])
exit 0
;;
*) echo "Only numbers between 0..100, q for quit"
;;
esac
done
以及更紧凑的版本(Thx @EliahKagan):
#!/usr/bin/env bash
while read -erp 'Enter numeric grade (q to quit): '; do
case $REPLY in
[0-9]|[1-5][0-9]) echo F ;;
6[0-9]) echo D ;;
7[0-9]) echo C ;;
8[0-9]) echo B ;;
9[0-9]|100) echo A ;;
[Qq]) exit ;;
*) echo 'Only numbers between 0..100, q for quit' ;;
esac
done
[0-59]
表示0、1、2、3、4、5或9中的任何字符,依此类推。我看不到它如何适用于数值。
Ubuntu的所有安装都有Python,因此这里是一个python 脚本。如果您需要将它放在bash中,我也将等效代码编写为shell脚本。
print (chr(75-max(5,int('0'+raw_input('Enter the number: ')[:-1]))))
要运行,请将其保存在文件中(例如grade.py
),然后在终端中运行以下命令:
python grade.py
这是您将看到的:
Enter the number: 65
E
这是如何运作的?
65
。065
。06
。70
。E
。E
。我的代词是他/他
input()
,它会调用eval()
,而是使用.. raw_input()
90+
B
chr(74 - max(4, num))
input()
来raw_input()
进行python2..thats它..
print chr(75-max(5,int('0'+raw_input('Enter the number: ')[:-1])))
python3
没有raw_input()
。.我建议raw_input()
您使用第一个初始代码,因为您告诉过要使用python2
..
这是我的半深奥bash解决方案,它用101个条目填充一个数组,然后根据它们检查用户输入。即使是在现实世界中使用,这也是合理的-如果您需要出色的性能,那么您就不会使用bash,并且一百个(或大约)任务仍然很快速。但是,如果扩展到更大的范围(例如一百万),它将不再是合理的。
#!/usr/bin/env bash
p(){ for i in `seq $2 $3`; do g[$i]=$1; done; }
p A 90 100; p B 80 89; p C 70 79; p D 60 69; p F 0 59
while read -r n && [[ ! $n =~ ^[qQ] ]]; do echo ${g[$n]}; done
优点:
q
,quit
或任何开始q
/ Q
。缺点:
g
它是一维索引数组)。俗话说:“这不是错误,而是功能!” 也许。有点酷吧?(嗯,我是这样认为的。)
p
函数p opulates一个数字索引阵列g
的克拉迪斯,在索引范围从它的第一个参数的第二,在其第三个参数给出的(字母)值。p
每个字母等级都会调用,以定义其数字范围。q
(或Q
)开头,请检查g
字母等级与输入数字相对应的数组,然后打印该字母。[[ $n =~ ^(0|[1-9]+[0-9]*)$ ]]
在Python 2中制作之后,我决定在bash中制作它。
#! /bin/bash
read -p "Enter the number: " i
i=0$i
x=$((10#${i::-1}))
printf "\x$(printf %x $((11-($x>5?$x:5)+64)))\n"
要运行,请将其保存在文件(例如grade.sh)中,使其使用可执行,chmod +x grade.sh
然后使用运行./grade.sh
。
这是您将看到的:
Enter the number: 65
E
这是如何运作的?
65
。065
(并10#
使其以10为底)。06
。70
。E
。E
。我的代词是他/他
这是我的awk版本:
awk '{
if($_ <= 100 && $_ >= 0) {
sub(/^([0-9]|[1-5][0-9])$/, "F", $_);
sub(/^(6[0-9])$/, "D", $_);
sub(/^(7[0-9])$/, "C", $_);
sub(/^(8[0-9])$/, "B", $_);
sub(/^(9[0-9]|100)$/, "A", $_);
print
}
else {
print "Only numbers between 0..100"
}
}' -
或单线:
awk '{if($_ <= 100 && $_ >= 0) { sub(/^([0-9]|[1-5][0-9])$/, "F", $_); sub(/^(6[0-9])$/, "D", $_); sub(/^(7[0-9])$/, "C", $_); sub(/^(8[0-9])$/, "B", $_);sub(/^(9[0-9]|100)$/, "A", $_); print} else { print "Only numbers between 0..100"}}' -
这是另一个“神秘的”答案
perl -E '
print "number: ";
$n = <>;
say qw/A A B C D E F F F F F/[11-($n+1)/10]
if $n=~/^\s*\d/ and 0<=$n and $n<=100
'
perl -E
:和-E
一样-e
,允许将脚本作为命令行参数传递。这是运行perl单行代码的一种方法。与不同-e
,-E
它还启用所有可选功能(例如say
,基本上是print
带有尾随换行符的)。print "number: ";
:提示用户输入数字。$n = <>;
:将该号码另存为$n
。接下来的一点需要分解。qw/string/
求值是通过string
在空格处断开而制成的列表。因此,qw/A A B C D E F F F F F/
实际上是此列表:
0 : A
1 : A
2 : B
3 : C
4 : D
5 : E
6 : F
7 : F
8 : F
9 : F
10 : F
因此,say qw/A A B C D E F F F F F/[11-($n+1)/10]
相当于
my @F=("A","A","B","C","D","E","F","F","F","F","F");
print "$F[11-($n+1)/10]\n"
现在,Perl允许使用负索引来检索从数组末尾开始计数的元素。例如,$arrray[-1]
将打印数组的最后一个元素。此外,浮点数组索引(例如10.7)会自动截断为下一个较低的整数(10.7或10.3或所有等于10的整数)。
所有这些的结果是索引11-($n+1)/10
总是求值到数组的适当元素(等级)。
尽管您要求使用bash解决方案,但我认为在python中,这可以通过简洁的方式完成。涵盖了在输入错误的情况下处理错误以及将0到100之间的数字“转换”为A到F(或其他任何字母)的情况:
#!/usr/bin/env python3
try:
n = int(input("number: ")); n = n if n>0 else ""
print("FEDCBA"[[n>=f for f in [50,60,70,80,90,101]].count(True)])
except:
print("invalid input")
首先,我们需要从用户那里获取电话号码:
n = int(input("number: "))
我们测试此号码在多种情况下是否有效:
n>=50, n>=60, n>=70, n>=80, n>=90
对于这些测试中的每一个,结果将为False
或True
。因此(稍微压缩一下代码):
[n>=f for f in [50,60,70,80,90]].count(True)]
将产生一个从0
到5
随后,我们可以将此图用作字符串的索引,以产生一个字符作为输出,例如
"ABCDEF"[3]
将输出“ D”(因为第一个字符=“ A”)
该101
列表的附加功能是在数量超出时生成(Index-)错误100
,因为该数量"ABCDEF"[6]
不存在。同样适用于n = n if n>=0 else ""
,如果输入的数字小于0,则将产生(Value-)错误。
在这些情况下,以及如果输入的不是数字,结果将是:
invalid input
测试:
number: 10
F
number: 50
E
number: 60
D
number: 70
C
number: 80
B
number: 90
A
number: 110
invalid input
number: -10
invalid input
number: Monkey
invalid input