Como escrever um shell script para atribuir notas de letra a intervalos numéricos?

19

Quero criar um script que solicite um número entre 0 e 100 e, em seguida, dê a nota com base no número.

Eu gostaria que na festança.

PS3='Please enter your choice: '
(Something here)

do
case $
    "0-59")
        echo "F"
        ;;
    "60-69")
        echo "D"
        ;;
    "70-79")
        echo "C"
        ;;
    "Quit")
        break
        ;;
    *) echo invalid option;;
esac
done
Temple Pate
fonte

Respostas:

20

Brevidade x legibilidade: um meio termo

Como você viu, esse problema admite soluções moderadamente longas e um tanto repetitivas, mas altamente legíveis ( respostas bash de Terdon e AB ), bem como aquelas que são muito curtas, mas não intuitivas e muito menos auto-documentadas ( python de Tim respostas bash e resposta perl de glenn jackman ). Todas essas abordagens são valiosas.

Você também pode resolver esse problema com o código no meio do continuum entre compacidade e legibilidade. Essa abordagem é quase tão legível quanto as soluções mais longas, com um comprimento mais próximo das soluções pequenas e esotéricas.

#!/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."

Nesta solução do bash, incluí algumas linhas em branco para melhorar a legibilidade, mas você pode removê-las se desejar ainda mais.

Linhas em branco incluídas, na verdade, são apenas um pouco mais curtas do que uma variante compactada, ainda bastante legível da solução bash da AB . Suas principais vantagens sobre esse método são:

  • É mais intuitivo.
  • É mais fácil alterar os limites entre as notas (ou adicionar notas adicionais).
  • Ele aceita automaticamente entradas com espaços à esquerda e à direita (veja abaixo uma explicação de como (( ))funciona).

Todas essas três vantagens surgem porque esse método usa a entrada do usuário como dados numéricos, em vez de examinar manualmente seus dígitos constituintes.

Como funciona

  1. Leia a entrada do usuário. Deixe que eles usem as teclas de seta para se mover no texto digitado ( -e) e não interpretem \como um caractere de escape ( -r).
    Esse script não é uma solução rica em recursos - veja abaixo um refinamento - mas esses recursos úteis apenas aumentam dois caracteres. Eu recomendo sempre usar -rcom read, a menos que você saiba que precisa deixar o fornecimento do usuário \escapar.
  2. Se o usuário escreveu qou Q, saia.
  3. Crie uma matriz associativa ( ). Preencha-o com a nota numérica mais alta associada a cada nota de letra.declare -A
  4. Passe pelas notas das letras da mais baixa para a mais alta, verificando se o número fornecido pelo usuário é baixo o suficiente para cair no intervalo numérico de cada letra.
    Com a (( ))avaliação aritmética, os nomes de variáveis ​​não precisam ser expandidos $. (Na maioria das outras situações, se você quiser usar o valor de uma variável no lugar de seu nome, faça isso .)
  5. Se estiver dentro do intervalo, imprima a nota e saia .
    Por uma questão de brevidade, uso o curto-circuito e o operador ( &&) em vez de um if- then.
  6. Se o loop terminar e nenhum intervalo for correspondido, suponha que o número digitado seja muito alto (acima de 100) e informe ao usuário que ele estava fora do intervalo.

Como isso se comporta, com entrada estranha

Como as outras soluções curtas postadas, esse script não verifica a entrada antes de assumir que é um número. Avaliação aritmética ( (( ))) tiras automaticamente líder e espaços em branco, de modo que é nenhum problema, mas:

  • A entrada que não se parece com um número é interpretada como 0.
  • Com entrada que se parece com um número (ou seja, se começa com um dígito), mas contém caracteres inválidos, o script emite erros.
  • Entrada de dígitos múltiplos começando com 0é interpretada como sendo , em octal . Por exemplo, o script dirá que 77 é um C, enquanto 077 é um D. Embora alguns usuários possam querer isso, provavelmente não querem e isso pode causar confusão.
  • No lado positivo, quando recebe uma expressão aritmética, esse script a simplifica automaticamente e determina a nota da carta associada. Por exemplo, ele dirá que 320/4 é um B.

Uma versão expandida e com todos os recursos

Por esses motivos, convém usar algo como esse script expandido, que verifica se a entrada é boa e inclui alguns outros aprimoramentos.

#!/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

Essa ainda é uma solução bastante compacta.

Quais recursos isso adiciona?

Os pontos principais desse script expandido são:

  • Validação de entrada. O script de terdon verifica a entrada com , por isso mostro outra maneira, que sacrifica alguma brevidade, mas é mais robusta, permitindo ao usuário entrar em espaços iniciais e finais e se recusando a permitir uma expressão que possa ou não ser pretendida como octal (a menos que seja zero) .if [[ ! $response =~ ^[0-9]*$ ]] ...
  • Eu usei casecom globbing estendido em vez de [[com o operador de =~ correspondência de expressão regular (como na resposta de terdon ). Fiz isso para mostrar que (e como) isso também pode ser feito dessa maneira. Globs e regexps são duas maneiras de especificar padrões que correspondem ao texto e qualquer um dos métodos é adequado para este aplicativo.
  • Como o script bash da AB , incluí a coisa toda em um loop externo (exceto a criação inicial da cutoffsmatriz). Ele solicita números e fornece notas correspondentes, desde que a entrada do terminal esteja disponível e o usuário não tenha solicitado que saia. A julgar pelo do... doneao redor do código da sua pergunta, parece que você deseja isso.
  • Para facilitar a desistência, aceito qualquer variante que não diferencia maiúsculas de minúsculas de qou quit.

Esse script usa algumas construções que podem não ser familiares para iniciantes; eles estão detalhados abaixo.

Explicação: Uso de continue

Quando eu quero pular o resto do corpo do whileloop externo , eu uso o continuecomando Isso o leva de volta ao topo do loop, para ler mais entradas e executar outra iteração.

A primeira vez que faço isso, o único loop em que estou é o whileloop externo , para que eu possa ligar continuesem nenhum argumento. (Estou em uma caseconstrução, mas isso não afeta a operação de breakou continue.)

        *) echo "I don't understand that number."; continue;;

Na segunda vez, no entanto, estou em um forloop interno que está aninhado dentro do whileloop externo . Se eu usasse continuesem argumento, isso seria equivalente continue 1e continuaria o forloop interno em vez do whileloop externo .

        ((REPLY <= cutoffs[$letter])) && { echo $letter; continue 2; }

Portanto, nesse caso, eu uso o continue 2bash find e continuo o segundo loop.

Explicação: caseEtiquetas com Globs

Não uso casepara descobrir qual classe da letra bin um número cai (como em resposta a festança da AB ). Mas eu uso casepara decidir se a entrada do usuário deve ser considerada:

  • um número válido, *( )@([1-9]*([0-9])|+(0))*( )
  • o comando quit, *( )[qQ]?([uU][iI][tT])*( )
  • qualquer outra coisa (e, portanto, entrada inválida), *

Estes são globs de concha .

  • Cada um é seguido por um )que não corresponde a nenhuma abertura (, que é casea sintaxe para separar um padrão dos comandos que são executados quando é correspondido.
  • ;;é casea sintaxe para indicar o final dos comandos a serem executados para uma correspondência de caso paticular (e que nenhum caso subsequente deve ser testado após a execução).

O globbing de shell comum fornece *para corresponder a zero ou mais caracteres, ?para corresponder exatamente a um caractere e a classes / intervalos de caracteres entre [ ]colchetes. Mas estou usando globbing estendido , o que vai além disso. O globbing estendido é ativado por padrão ao usar bashinterativamente, mas é desativado por padrão ao executar um script. O shopt -s extglobcomando na parte superior do script o ativa.

Explicação: Globbing estendido

*( )@([1-9]*([0-9])|+(0))*( ), que verifica a entrada numérica , corresponde a uma sequência de:

  • Zero ou mais espaços ( *( )). A *( )construção corresponde a zero ou mais do padrão entre parênteses, o que aqui é apenas um espaço.
    Na verdade, existem dois tipos de espaço em branco horizontal, espaços e guias, e geralmente é desejável combinar as guias também. Mas não estou me preocupando com isso aqui, porque esse script foi escrito para entrada manual, interativa e o -esinalizador para readativar a linha de leitura do GNU. Isso é para que o usuário possa ir e voltar no texto com as teclas de seta esquerda e direita, mas tem o efeito colateral de impedir que as guias sejam inseridas literalmente.
  • Uma ocorrência ( @( )) de qualquer um ( |):
    • Um dígito diferente de zero ( [1-9]) seguido por zero ou mais ( *( )) de qualquer dígito ( [0-9]).
    • Um ou mais ( +( )) de 0.
  • Zero ou mais espaços ( *( )), novamente.

*( )[qQ]?([uU][iI][tT])*( ), que verifica o comando quit , corresponde a uma sequência de:

  • Zero ou mais espaços ( *( )).
  • qou Q( [qQ]).
  • Opcionalmente - ou seja, zero ou uma ocorrência ( ?( )) - de:
    • uou U( [uU]) seguido por iou I( [iI]) seguido por tou T( [tT]).
  • Zero ou mais espaços ( *( )), novamente.

Variante: validando entrada com uma expressão regular estendida

Se você preferir testar a entrada do usuário em relação a uma expressão regular em vez de um shell glob, você pode preferir usar esta versão, que funciona da mesma forma, mas usa [[e =~(como em resposta de terdon ) em vez de caseum globbing estendido.

#!/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

As possíveis vantagens dessa abordagem são as seguintes:

  • Nesse caso em particular, a sintaxe é um pouco mais simples, pelo menos no segundo padrão, em que verifico o comando quit. Isso porque pude definir onocasematch opção de shell e, em seguida, todas as variantes de casos qe quitforam cobertas automaticamente.

    É isso que o shopt -s nocasematch comando faz. O shopt -s extglobcomando é omitido porque o globbing não é usado nesta versão.

  • Habilidades de expressão regular são mais comuns que a proficiência em extglobs do bash.

Explicação: Expressões regulares

Quanto aos padrões especificados à direita do =~operador, veja como essas expressões regulares funcionam.

^\ *([1-9][0-9]*|0+)\ *$, que verifica a entrada numérica , corresponde a uma sequência de:

  • O início - ou seja, borda esquerda - da linha (^ ).
  • Zero ou mais *espaços ( , postfix aplicado). Normalmente, um espaço não precisa ser \escapado em uma expressão regular, mas isso é necessário com[[ para evitar um erro de sintaxe.
  • Uma substring ( ( )) que é uma ou outra (| ) de:
    • [1-9][0-9]*: um dígito diferente de zero ( [1-9]) seguido por zero ou mais ( *, postfix aplicado) de qualquer dígito ([0-9] ).
    • 0+: um ou mais ( +, postfix aplicado) de 0.
  • Zero ou mais espaços (\ * ), como antes.
  • O final - ou seja, a borda direita - da linha ( $).

Ao contrário dos caserótulos, que correspondem à expressão inteira sendo testada, =~retornará true se alguma parte de sua expressão à esquerda corresponder ao padrão fornecido como sua expressão à direita. É por isso que as âncoras ^e $, especificando o início e o fim da linha, são necessárias aqui e não correspondem sintaticamente a nada que apareça no método com casee extglobs.

Os parênteses são necessários para criar ^e $vincular à disjunção de [1-9][0-9]*e 0+. Caso contrário, seria a disjunção de ^[1-9][0-9]*e 0+$, e combinar qualquer entrada que começa com um dígito diferente de zero ou terminando com um0 (ou ambos, que ainda pode incluir não-dígitos no meio).

^\ *q(uit)?\ *$, que verifica o comando quit , corresponde a uma sequência de:

  • O início da linha (^ ).
  • Zero ou mais espaços (\ * veja a explicação acima).
  • A carta q. Ou Q, desdeshopt nocasematch está ativado.
  • Opcionalmente - ou seja, zero ou uma ocorrência (postfix ?) - da substring (( ) ):
    • u, seguido por i, seguido por t. Ou, uma vez que shopt nocasematchestá ativado, upode ser U; independentemente, ipode ser I; e independentemente, tpode ser T. (Ou seja, as possibilidades não se limitam a uite UIT.)
  • Zero ou mais espaços novamente (\ * ).
  • O fim da linha ( $).
Eliah Kagan
fonte
3
me diga a verdade .. quanto tempo levou? ;)
heemayl
4
@heemayl Não tenho muita certeza, já que o escrevi em várias partes pequenas ao longo do dia (seguidas de uma leitura e edição completas, pouco antes da publicação). Tenho certeza de que acabou demorando mais do que eu pensaria quando comecei, se estivesse pensando em quanto tempo isso levaria. :)
Eliah Kagan
6
escreva mais e mais, preciso de um livro de suas respostas.
Grijesh Chauhan
TL; DR, mas me fez rir de qualquer maneira!
Fabby
tudo, do título à explicação, é bom. Eu entendi no primeiro ir, mas eu lê-lo novamente só porque eu queria
Sumeet Deshmukh
23

Você já tem a ideia básica. Se você deseja codificar isso bash(que é uma escolha razoável, já que é o shell padrão no Ubuntu e na maioria dos outros Linux), você não pode usá- caselo porque ele não entende os intervalos. Em vez disso, você pode usar 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
Terdon
fonte
4
Muitos desses -getestes podem ser eliminados, presumivelmente, desde que você esteja usando elif. E sem amor (( $response < X ))?
muru
2
@ muru verdade, obrigado. Eu estava pensando em faixas numéricas, mas não havia motivo para isso. Quanto a (( $response < X )), com certeza, mas acho isso mais claro e o OP é obviamente novo no bash script.
Terdon
12
#!/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

e uma versão mais compacta (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
AB
fonte
1
Essas são faixas de caracteres, com certeza? ie [0-59]significa qualquer caractere de 0,1,2,3,4,5 ou 9 e assim por diante. Não vejo como isso pode funcionar para valores numéricos .
Steeldriver
3
Você não precisa ser FGITW o tempo todo. Não se apresse, escreva boas respostas. Veja como o Terdon ou Eliah Kagan funcionam.
muru
@AB Percebi que essa solução pode ser reduzida, principalmente por meio de alterações estilísticas, enquanto permanece bastante legível. A brevidade raramente é a consideração mais importante, então não acho que você deva mudar o que tem dessa maneira. Mas como a forma mais compacta não ocupa muito espaço, considere mostrá-la também, caso alguns leitores desejem um script mais curto que funcione da mesma maneira. (Se quiser, sinta-se livre para usar a minha versão abreviada, ou qualquer variação do mesmo.)
Elias Kagan
9

Todas as instalações do Ubuntu têm Python, então aqui está um script python para um liner. Se você precisar que ele esteja no bash, também escrevi o equivalente como um script de shell .

print (chr(75-max(5,int('0'+raw_input('Enter the number: ')[:-1]))))

Para executar, salve-o em um arquivo (por exemplo grade.py) e execute-o no terminal com este:

python grade.py

Isso é o que você vai ver:

Enter the number: 65
E

Como é que isso funciona?

  1. Tome entrada - 65.
  2. Adicione um 0 ao início - 065.
  3. Retire o último caractere - 06.
  4. 75 subtrair esse número - 70.
  5. Converta em uma letra (A é 65, B é 66) - E.
  6. Imprima - E.

Meus pronomes são He / Him

Tim
fonte
Eu gosto da sua ideia. +1
AB
Não use input(), ele irá chamar eval(), uso raw_input()instead..also sua classificação não é certo como 90+irá imprimir grau B..use chr(74 - max(4, num))....
heemayl
well..your solução é agradável e irá trabalhar em python2 also..just mudança do input()que raw_input()para python2..thats-lo ..
heemayl
print chr(75-max(5,int('0'+raw_input('Enter the number: ')[:-1])))
Hemayl # 9/15
Então você precisa mudar seu too..your código original código modifed na sua redacção actual é errado embora como python3não tem raw_input().. i sugeriu raw_input()para seu inicial como você disse para executá-lo usando python2..
heemayl
6

Aqui está minha solução bash semi- esotérica, que preenche uma matriz com 101 entradas e depois verifica a entrada do usuário. Mesmo para uso no mundo real, isso é razoável - se você precisasse de excelente desempenho, não usaria o bash, e uma centena de tarefas ainda é rápida. Mas deixaria de ser razoável se estendido a uma faixa muito maior (como um milhão).

#!/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

Vantagens:

  • Não é realmente tão esotérico. Embora seja mais longo que as soluções mais curtas, e não tão auto-documentado quanto as soluções mais longas ... é razoavelmente auto-documentado , embora decididamente ainda pequeno.
  • Permite fácil modificação para alterar faixas de notas ou adicionar / remover notas.
  • Ela opera em um loop e sai em q, quitou qualquer coisa que começa com q/ Q.
  • Lista as notas mais altas primeiro, para ajudá-lo a pensar positivo. :)
  • Hmm, isso faz o trabalho, continua fazendo sentido, mesmo depois que você olha para ele, e possui os recursos essenciais. Você poderia realmente usar isso!

Desvantagens:

  • Dá um F quando você insere entradas não numéricas ... mas isso não é tão ruim assim, é? Se você fornecer um número não onde é necessário, talvez você mereça um F!
  • A entrada ambígua e possivelmente octal é tratada como octal (já que gé um array indexado unidimensional ). Como diz o velho ditado, "Não é um bug, é um recurso!" Bem, talvez.
  • A entrada que está fora da faixa ou não é um número faz com que uma linha vazia seja impressa. No entanto, não há nada realmente errado nisso: ele indica qual nota de letra corresponde à sua entrada e, para entrada errada, não há uma.
  • Coloque um número negativo e ... bem, chame de ovo da páscoa .
  • Ainda significativamente mais longo que a solução python de Tim . Sim, eu realmente não posso girar isso para parecer uma vantagem.

Legal, não é? (Bem, eu acho que sim.)

Como funciona

  1. A pfunção p opula uma matriz numerada indexada gde g rades, em índices que variam do primeiro argumento ao segundo, com o valor (letra) fornecido no terceiro argumento.
  2. p é chamado para cada nota de letra, para definir seu intervalo numérico.
  3. Continue lendo a entrada do usuário, desde que ela esteja disponível e não comece com q(ou Q), verifique a gmatriz para a qual a nota da letra corresponde ao número digitado e imprima essa letra.
Eliah Kagan
fonte
E esse condicional? [[ $n =~ ^(0|[1-9]+[0-9]*)$ ]]
Helio
6

Depois de fazê-lo em Python 2 , decidi fazê-lo no 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"

Para executar, salve-o em um arquivo (por exemplo, grade.sh), torne-o executável chmod +x grade.she, em seguida, execute-o ./grade.sh.

Isso é o que você vai ver:

Enter the number: 65
E

Como é que isso funciona?

  1. Tome entrada - 65.
  2. Adicione um 0 ao início - 065(e o 10#mantém na base 10).
  3. Retire o último caractere - 06.
  4. 75 subtrair esse número - 70.
  5. Converta em uma letra (A é 65, B é 66) - E.
  6. Imprima - E.

Meus pronomes são He / Him

Tim
fonte
Muito esperto, bem feito
kos
@kos thanks :) Duvido que funcione para o OP, porque seus intervalos provavelmente não são o que ele postou. Espero que sejam por simplicidade.
Tim
5

E aqui está a minha versão 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"
    }
}' -

ou como uma linha:

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"}}' -
AB
fonte
4

Aqui está outra resposta "esotérica"

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
'

Explicação

  • perl -E: the -E, like -e, permite passar um script como argumento de linha de comando. Esta é uma maneira de executar perl one-liners. Ao contrário -e, -Etambém habilita todos os recursos opcionais (como say, basicamente, um printcom uma nova linha à direita).
  • print "number: "; : solicita que o usuário insira um número.
  • $n = <>;: salve esse número como $n.

A próxima parte precisa ser dividida um pouco. qw/string/avalia para uma lista feita quebrando stringno espaço em branco. Então, qw/A A B C D E F F F F F/é realmente esta lista:

0 : A
1 : A
2 : B
3 : C
4 : D
5 : E
6 : F
7 : F
8 : F
9 : F
10 : F

Portanto, say qw/A A B C D E F F F F F/[11-($n+1)/10]é equivalente a

my @F=("A","A","B","C","D","E","F","F","F","F","F");
print "$F[11-($n+1)/10]\n"

Agora, o Perl permite o uso de índices negativos para recuperar elementos contados no final da matriz. Por exemplo,$arrray[-1] imprimirá o último elemento da matriz. Além disso, os índices da matriz de ponto flutuante (por exemplo, 10.7) são automaticamente truncados para o próximo número inteiro mais baixo (10.7 ou 10.3 ou o que todos se tornarem 10.)

O resultado de tudo isso é que o índice 11-($n+1)/10sempre avalia o elemento (nota) apropriado da matriz.

Glenn Jackman
fonte
4
As respostas esotéricas são ótimas, mas inclua uma explicação.
Muru
1

Embora você tenha solicitado uma solução bash, acho que em python, isso pode ser feito de maneira elegante e curta. Abrangendo os erros de manipulação em caso de entrada incorreta e a "conversão" de um número entre 0 e 100 em letras de A a F (ou qualquer outra):

#!/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")

Explicação

  1. Primeiro, precisamos obter o número do usuário:

    n = int(input("number: "))
  2. Testamos esse número para ser válido para várias condições:

    n>=50, n>=60, n>=70, n>=80, n>=90

    Para cada um desses testes, o resultado será Falseou True. Portanto (compactando um pouco o código):

    [n>=f for f in [50,60,70,80,90]].count(True)]

    produzirá uma figura de 0para5

  3. Posteriormente, podemos usar esta figura como um índice para uma string, para produzir um caractere como saída, por exemplo

    "ABCDEF"[3] 

    produzirá "D" (desde o primeiro caractere = "A")

  4. O adicional 101à lista é gerar um erro (Índice-) caso o número exceda 100, pois "ABCDEF"[6]não existe. O mesmo vale para n = n if n>=0 else "", que criará um erro (Valor-) se um número abaixo de 0 for inserido
    Nesses casos, bem como se a entrada não for uma figura, o resultado será:

    invalid input

Os testes:

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
Jacob Vlijm
fonte