Come scrivere uno script shell per assegnare i voti in lettere agli intervalli numerici?


19

Voglio creare uno script che ti chieda un numero compreso tra 0 e 100 e quindi ti dia il voto in base al numero.

Mi piacerebbe bash.

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

Risposte:


20

Brevità contro leggibilità: una via di mezzo

Come hai visto, questo problema ammette soluzioni moderatamente lunghe e in qualche modo ripetitive ma altamente leggibili ( risposte di basdon's e AB bash), così come quelle che sono molto brevi ma non intuitive e molto meno auto-documentanti ( pitone di Tim e bash risposte e glenn jackman's perl risposta ). Tutti questi approcci sono preziosi.

È inoltre possibile risolvere questo problema con il codice nel mezzo del continuum tra compattezza e leggibilità. Questo approccio è quasi leggibile come le soluzioni più lunghe, con una lunghezza più vicina alle soluzioni piccole ed esoteriche.

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

In questa soluzione bash, ho incluso alcune righe vuote per migliorare la leggibilità, ma potresti rimuoverle se lo volessi ancora più breve.

Linee vuote incluse, in realtà è solo leggermente più corta di una variante compattata, ancora piuttosto leggibile della soluzione bash di AB . I suoi principali vantaggi rispetto a tale metodo sono:

  • È più intuitivo.
  • È più facile cambiare i confini tra i voti (o aggiungere voti aggiuntivi).
  • Accetta automaticamente input con spazi iniziali e finali (vedi sotto per una spiegazione di come (( ))funziona).

Tutti e tre questi vantaggi derivano dal fatto che questo metodo utilizza l'input dell'utente come dati numerici anziché esaminando manualmente le sue cifre costitutive.

Come funziona

  1. Leggi l'input dell'utente. Lasciateli usare i tasti freccia per spostarsi nel testo che hanno inserito ( -e) e non interpretarli \come caratteri di escape ( -r).
    Questo script non è una soluzione ricca di funzionalità - vedi sotto per un perfezionamento - ma queste utili funzionalità ne aumentano solo due caratteri. Consiglio di utilizzare sempre -rcon read, a meno che tu non sappia che devi lasciare scappare la fornitura dell'utente \.
  2. Se l'utente ha scritto qo Q, esci.
  3. Crea un array associativo ( ). Inseriscilo con il voto numerico più alto associato a ciascun voto di lettera.declare -A
  4. Scorri tra i gradi delle lettere dal più basso al più alto, controllando se il numero fornito dall'utente è abbastanza basso da rientrare nell'intervallo numerico di ciascuna lettera.
    Con (( ))la valutazione aritmetica, non è necessario espandere i nomi delle variabili $. (Nella maggior parte delle altre situazioni, se si desidera utilizzare il valore di una variabile al posto del suo nome, è necessario farlo .)
  5. Se rientra nell'intervallo, stampare il voto ed uscire .
    Per brevità, uso il cortocircuito e l' operatore ( &&) anziché un if- then.
  6. Se il ciclo termina e nessun intervallo è stato abbinato, supponiamo che il numero inserito sia troppo alto (oltre 100) e dire all'utente che era fuori intervallo.

Come si comporta, con input strani

Come le altre brevi soluzioni pubblicate, quello script non controlla l'input prima di assumere che sia un numero. Valutazione aritmetica ( (( ))) strisce automaticamente spazi iniziali e finali, in modo tale non un problema, ma:

  • L'input che non assomiglia affatto a un numero viene interpretato come 0.
  • Con un input che assomiglia a un numero (ovvero, se inizia con una cifra) ma contiene caratteri non validi, lo script emette errori.
  • Input multi cifre iniziano 0viene interpretato come essere in ottale . Ad esempio, lo script ti dirà che 77 è una C, mentre 077 è una D. Anche se alcuni utenti potrebbero volerlo, molto probabilmente no e può causare confusione.
  • Tra i lati positivi, quando viene data un'espressione aritmetica, questo script semplifica automaticamente e determina il grado di lettera associato. Ad esempio, ti dirà che 320/4 è un B.

Una versione estesa e completamente in primo piano

Per questi motivi, potresti voler utilizzare qualcosa di simile a questo script espanso, che verifica che l'input sia buono e include alcuni altri miglioramenti.

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

Questa è ancora una soluzione piuttosto compatta.

Quali funzionalità aggiunge?

I punti chiave di questo script espanso sono:

  • Convalida input. lo script di terdon controlla l'input conif [[ ! $response =~ ^[0-9]*$ ]] ... , quindi mostro un altro modo, che sacrifica un po 'di brevità ma è più robusto, consentendo all'utente di entrare in spazi iniziali e finali e rifiutando di consentire un'espressione che potrebbe o non potrebbe essere intesa come ottale (a meno che non sia zero) .
  • Ho usato casecon globbing esteso invece che [[con l' operatore di =~ corrispondenza delle espressioni regolari (come nella risposta di Terdon ). L'ho fatto per dimostrare che (e come) può essere fatto anche in quel modo. Globi e regexps sono due modi per specificare modelli che corrispondono al testo, e entrambi i metodi vanno bene per questa applicazione.
  • Come lo script bash di AB , ho racchiuso il tutto in un ciclo esterno (tranne la creazione iniziale cutoffsdell'array). Richiede numeri e fornisce i corrispondenti voti in lettere fintanto che l'input del terminale è disponibile e l'utente non gli ha detto di smettere. A giudicare dal do... doneintorno al codice nella tua domanda, sembra che tu lo voglia.
  • Per facilitare la chiusura, accetto qualsiasi variante senza distinzione tra maiuscole e minuscole di qo quit.

Questo script utilizza alcuni costrutti che potrebbero non essere familiari ai principianti; sono dettagliati di seguito.

Spiegazione: Uso di continue

Quando voglio saltare il resto del corpo del whileloop esterno , utilizzo il continuecomando. Questo lo riporta in cima al ciclo, per leggere più input ed eseguire un'altra iterazione.

La prima volta che lo faccio, l'unico loop in cui mi trovo è il whileloop esterno , quindi posso chiamare continuesenza argomenti. (Sono in un casecostrutto, ma ciò non influisce sul funzionamento di breako continue.)

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

La seconda volta, tuttavia, mi trovo in un forcircuito interno annidato all'interno del whilecircuito esterno . Se avessi usato continuesenza alcun argomento, questo sarebbe equivalente continue 1e continuerebbe il forciclo interno anziché il whileciclo esterno .

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

Quindi in quel caso, uso invece continue 2per trovare bash e continuare il secondo loop.

Spiegazione: caseetichette con globi

Non ho l'abitudine casedi capire in quale scomparto di grado lettera cade un numero (come nella risposta bash di AB ). Ma uso caseper decidere se l'input dell'utente debba essere considerato:

  • un numero valido, *( )@([1-9]*([0-9])|+(0))*( )
  • il comando quit, *( )[qQ]?([uU][iI][tT])*( )
  • qualsiasi altra cosa (e quindi input non valido), *

Questi sono globs di shell .

  • Ciascuno è seguito da un )non corrispondente ad alcuna apertura (, che ècase la sintassi per separare un modello dai comandi eseguiti quando viene abbinato.
  • ;;è casela sintassi per indicare la fine dei comandi da eseguire per un caso paticolare (e che nessun caso successivo deve essere testato dopo averli eseguiti).

Il globbing di shell ordinario prevede la *corrispondenza di zero o più caratteri, la ?corrispondenza di esattamente un carattere e le classi / intervalli di caratteri tra [ ]parentesi. Ma sto usando un globbing esteso , che va oltre. Il globbing esteso è abilitato per impostazione predefinita quando si utilizza in modo bashinterattivo, ma è disabilitato per impostazione predefinita quando si esegue uno script. Il shopt -s extglobcomando nella parte superiore dello script lo attiva.

Spiegazione: Extended Globbing

*( )@([1-9]*([0-9])|+(0))*( ), che verifica l'immissione numerica , corrisponde a una sequenza di:

  • Zero o più spazi ( *( )). Il *( )costrutto corrisponde a zero o più del motivo tra parentesi, che qui è solo uno spazio.
    Esistono in realtà due tipi di spazi bianchi orizzontali, spazi e schede, e spesso è preferibile abbinare anche le schede. Ma non mi preoccupo di questo qui, perché questo script è scritto per input manuale e interattivo e il -eflag per readabilitare readline GNU. In questo modo l'utente può spostarsi avanti e indietro nel proprio testo con i tasti freccia sinistra e destra, ma ha l'effetto collaterale di impedire l'inserimento letterale di schede.
  • Una ricorrenza ( @( )) di uno dei due ( |):
    • Una cifra [1-9]diversa da zero ( ) seguita da zero o più ( *( )) di qualsiasi cifra ( [0-9]).
    • Uno o più ( +( )) di 0.
  • Zero o più spazi ( *( )), di nuovo.

*( )[qQ]?([uU][iI][tT])*( ), che verifica il comando quit , corrisponde a una sequenza di:

  • Zero o più spazi (*( ) ).
  • qo Q([qQ] ).
  • Facoltativamente, ovvero zero o una occorrenza (?( ) ) - di:
    • uo U( [uU]) seguito da io I( [iI]) seguito da to T( [tT]).
  • Zero o più spazi ( *( )), di nuovo.

Variante: convalida dell'input con un'espressione regolare estesa

Se si preferisce testare l'input dell'utente rispetto a un'espressione regolare piuttosto che a una shell glob, si potrebbe preferire utilizzare questa versione, che funziona allo stesso modo ma usa [[e =~(come nella risposta di Terdon ) invece di caseestendere il globbing.

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

I possibili vantaggi di questo approccio sono:

  • In questo caso particolare, la sintassi è un po 'più semplice, almeno nel secondo modello, dove controllo il comando quit. Questo perché sono stato in grado di impostare l' nocasematchopzione shell e quindi tutte le varianti di case di qe quitsono state coperte automaticamente.

    Questo è ciò che fa il shopt -s nocasematchcomando. Il shopt -s extglobcomando è stato omesso poiché il globbing non è utilizzato in questa versione.

  • Le abilità di espressione regolare sono più comuni della competenza nei extglob di bash.

Spiegazione: espressioni regolari

Per quanto riguarda i modelli specificati a destra =~dell'operatore, ecco come funzionano quelle espressioni regolari.

^\ *([1-9][0-9]*|0+)\ *$, che verifica l'immissione numerica , corrisponde a una sequenza di:

  • L'inizio - ovvero il bordo sinistro - della linea ( ^).
  • Zero o più *spazi ( , postfix applicato). \Normalmente uno spazio non deve essere salvato in un'espressione regolare, ma ciò è necessario [[per prevenire un errore di sintassi.
  • Una sottostringa ( ( )) che è l'una o l'altra ( |) di:
    • [1-9][0-9]*: una cifra [1-9]diversa da zero ( ) seguita da zero o più ( *, postfisso applicato) di qualsiasi cifra ( [0-9]).
    • 0+: uno o più ( +, applicato postfix) di 0.
  • Zero o più spazi (\ * ), come prima.
  • L'estremità, ovvero il bordo destro, della linea ( $).

A differenza delle caseetichette, che corrispondono a tutta l'espressione in fase di test, =~restituisce true se una parte dell'espressione della mano sinistra corrisponde al modello fornito come espressione della mano destra. Questo è il motivo per cui le ancore ^e $, specificando l'inizio e la fine della linea, sono necessarie qui, e non corrispondono sintatticamente a qualsiasi cosa appaia nel metodo con casee extglobs.

Le parentesi sono necessarie per creare ^e $legare alla disgiunzione di [1-9][0-9]*e 0+. Altrimenti sarebbe la disgiunzione di ^[1-9][0-9]*e 0+$, e corrisponderebbe a qualsiasi input che inizia con una cifra diversa da zero o termina con un 0(o entrambi, che potrebbe comunque includere non cifre tra).

^\ *q(uit)?\ *$, che verifica il comando quit , corrisponde a una sequenza di:

  • L'inizio della riga ( ^).
  • Zero o più spazi ( \ *, vedere la spiegazione sopra).
  • La lettera q. Oppure Q, poiché shopt nocasematchè abilitato.
  • Opzionalmente - ovvero zero o una occorrenza (postfisso ?) - della sottostringa ( ( )):
    • u, seguito da i , seguito da t. Oppure, poiché shopt nocasematchè abilitato upuò essere U; indipendentemente, ipuò essere I; e indipendentemente, tpuò essere T. (Cioè, le possibilità non sono limitate a uite UIT.)
  • Zero o più spazi di nuovo ( \ *).
  • La fine della linea ( $).

3
dimmi la verità..quanto tempo ci è voluto? ;)
heemayl

4
@heemayl Non ne sono del tutto sicuro, dal momento che l'ho scritto in molte piccole parti durante il giorno (seguito da una lettura completa e modifiche, appena prima di pubblicare). Sono abbastanza sicuro che alla fine ho impiegato più tempo di quanto avrei pensato se avessi pensato a quanto tempo ci sarebbe voluto. :)
Eliah Kagan,

6
scrivi sempre di più, ho bisogno di un libro con le tue risposte.
Grijesh Chauhan,

TL; DR ma mi ha fatto ridere comunque!
Fabby,

tutto dal titolo alla spiegazione è buono. L'ho capito al primo
tentativo

23

Hai già l'idea di base. Se vuoi codificare questo in bash(che è una scelta ragionevole poiché è la shell predefinita su Ubuntu e la maggior parte degli altri Linux), non puoi usarlo caseperché non capisce gli intervalli. Invece, potresti usare 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

4
Molti di questi -getest possono essere eliminati, presumibilmente, dal momento che stai usando elif. E nessun amore per (( $response < X ))?
muru,

2
@muru vero, grazie. Ero bloccato a pensare in intervalli di numeri, ma non c'era motivo di farlo. Per quanto riguarda (( $response < X )), certo, ma trovo questo più chiara e l'OP è ovviamente nuovo a bash scripting.
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 una versione più compatta (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

1
Quelle sono gamme di caratteri, sicuramente? vale a [0-59]dire qualsiasi carattere compreso tra 0,1,2,3,4,5 o 9 e così via. Non vedo come possa funzionare per valori numerici .
steeldriver,

3
Non devi essere FGITW per tutto il tempo. Prenditi del tempo, scrivi buone risposte. Guarda come funzionano Terdon o Eliah Kagan.
muru,

@AB Ho notato che questa soluzione può essere abbreviata, principalmente attraverso cambiamenti stilistici, pur rimanendo abbastanza leggibile. La brevità è raramente la considerazione più importante, quindi non credo che dovresti cambiare ciò che hai in questo modo. Ma poiché la forma più compatta non occupa molto spazio, potresti considerare di mostrarla anche nel caso in cui alcuni lettori desiderino uno script più breve che funzioni allo stesso modo. (Se vuoi, sentiti libero di usare la mia versione abbreviata, o qualsiasi variazione su di essa.)
Eliah Kagan

9

Tutte le installazioni di Ubuntu hanno Python, quindi ecco uno script di Python una riga. Se hai bisogno che sia in bash, ho anche scritto l'equivalente come script di shell .

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

Per eseguirlo, salvarlo in un file (ad esempio grade.py) e quindi eseguirlo nel terminale con questo:

python grade.py

Questo è quello che vedrai:

Enter the number: 65
E

Come funziona?

  1. Prendi input - 65 .
  2. Aggiungi uno 0 all'inizio - 065 .
  3. Rimuovi l'ultimo carattere - 06 .
  4. 75 sottrarre quel numero - 70 .
  5. Converti in una lettera (A è 65, B è 66) - E .
  6. Stampalo - E.

I miei pronomi sono Lui / Lui


Mi piace la tua idea +1
AB

Non usare input(), chiamerà eval(), usa raw_input()invece..anche il tuo 90+voto non è giusto come lo sarà il grado di stampa ... Buso chr(74 - max(4, num))....
heemayl

beh ... la tua soluzione è buona e funzionerà anche in python2 ... basta cambiarla input()in raw_input()per python2..questa è ..
heemayl

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

Quindi devi cambiare anche il tuo codice originale..il tuo codice modificato così com'è attualmente è sbagliato anche se python3non lo è raw_input().. ho suggerito raw_input()quello iniziale come ti è stato detto di eseguirlo usando python2..
heemayl

6

Ecco la mia soluzione bash semi- esoterica, che popola un array con 101 voci e quindi controlla l'input dell'utente rispetto a esse. Anche per un uso nel mondo reale questo è ragionevole: se avessi bisogno di prestazioni eccellenti non useresti bash, e un centinaio di incarichi è ancora veloce. Ma smetterebbe di essere ragionevole se esteso a un raggio molto più ampio (come un milione).

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

vantaggi:

  • Non è poi così esoterico. Anche se è più lungo delle soluzioni più brevi, e non è così autodocumentante come le soluzioni più lunghe ... è ragionevolmente autocompattante , mentre è ancora decisamente piccolo.
  • Consente una facile modifica per cambiare la gamma di voti o aggiungere / rimuovere voti.
  • Opera in un ciclo e si chiude su q, quito tutto ciò che inizia con q/ Q.
  • Elenca prima i voti più alti, per aiutarti a pensare in modo positivo. :)
  • Hmm, questo fa il lavoro, continua a dare un senso anche dopo averlo visto, e ha le caratteristiche essenziali. Potresti davvero usarlo!

svantaggi:

  • Ti dà una F quando inserisci un input non numerico ... ma non è poi così male, vero? Se dai un non numero dove è necessario un numero, forse ti meriti una F!
  • L'input ambiguo, possibilmente ottale, viene trattato come ottale (poiché si gtratta di una matrice indicizzata unidimensionale ). Come dice il vecchio proverbio, "Non è un bug, è una caratteristica!" Beh forse.
  • L'input non compreso nell'intervallo o meno un numero provoca la stampa di una riga vuota. Non c'è nulla di veramente sbagliato in questo: ti dice quale lettera corrisponde al tuo input e per input errati non ce n'è uno.
  • Inserisci un numero negativo e ... beh, chiamalo un uovo di Pasqua .
  • Ancora significativamente più lungo della soluzione Python di Tim . Sì, non riesco davvero a farlo sembrare un vantaggio.

Un po 'figo, eh? (Beh, penso di sì.)

Come funziona

  1. La pfunzione p opziona una matrice gdi g rades indicizzata numericamente , con indici che vanno dal suo primo argomento al secondo, con il valore (lettera) dato nel suo terzo argomento.
  2. p viene chiamato per ogni lettera, per definire il suo intervallo numerico.
  3. Continua a leggere l'input dell'utente finché è disponibile e non inizia con q(o Q), verificando l' garray per il quale il grado della lettera corrisponde al numero inserito e stampando quella lettera.

Che dire di questo condizionale? [[ $n =~ ^(0|[1-9]+[0-9]*)$ ]]
Helio,

6

Dopo averlo realizzato in Python 2 , ho deciso di farlo in 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"

Per eseguirlo, salvarlo in un file (ad esempio grade.sh), renderlo eseguibile con chmod +x grade.she quindi eseguire con ./grade.sh.

Questo è quello che vedrai:

Enter the number: 65
E

Come funziona?

  1. Prendi input -65 .
  2. Aggiungi uno 0 all'inizio - 065(e lo 10#mantiene in base 10).
  3. Rimuovi l'ultimo carattere -06 .
  4. 75 sottrarre quel numero -70 .
  5. Converti in una lettera (A è 65, B è 66) -E .
  6. Stampalo - E.

I miei pronomi sono Lui / Lui


Molto intelligente, ben fatto
kos

@kos grazie :) Dubito che funzionerà per l'OP perché le sue gamme probabilmente non sono quelle che ha pubblicato. Mi aspetto che siano per semplicità.
Tim

5

Ed ecco la mia versione di 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"
    }
}' -

o come one-liner:

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"}}' -

4

Ecco un'altra risposta "esoterica"

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
'

Spiegazione

  • perl -E: il -E, mi piace -e, consente di passare uno script come argomento della riga di comando. Questo è un modo per eseguire perl liners. A differenza -e, -Eabilita anche tutte le funzionalità opzionali (come say, che è fondamentalmente a printcon una nuova riga finale).
  • print "number: "; : richiede all'utente di inserire un numero.
  • $n = <>;: salva quel numero come $n.

Il prossimo passo deve essere scomposto un po '. qw/string/valuta un elenco creato rompendo stringnello spazio bianco. Quindi, in qw/A A B C D E F F F F F/realtà è questo elenco:

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

Pertanto, 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"

Ora, Perl consente di utilizzare gli indici negativi per recuperare gli elementi contando dalla fine dell'array. Ad esempio, $arrray[-1]stamperà l'ultimo elemento dell'array. Inoltre, gli indici di array a virgola mobile (ad es. 10.7) vengono automaticamente troncati al successivo numero intero inferiore (10.7 o 10.3 o qualunque cosa diventi 10.)

Il risultato di tutto ciò è che l'indice 11-($n+1)/10valuta sempre l'elemento appropriato (grado) dell'array.


4
Le risposte esoteriche sono tutte belle, ma per favore includi una spiegazione.
muru,

1

Anche se hai chiesto una soluzione bash, penso in Python, questo può essere fatto in modo elegante in breve tempo. Coprendo entrambi gli errori di gestione in caso di input errato e la "conversione" di un numero compreso tra 0 e 100 in lettere da A a F (o qualsiasi altro):

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

Spiegazione

  1. Per prima cosa dobbiamo ottenere il numero dall'utente:

    n = int(input("number: "))
  2. Testiamo questo numero per essere valido per una serie di condizioni:

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

    Per ciascuno di questi test, il risultato sarà Falseo True. Pertanto (comprimendo un po 'il codice):

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

    produrrà una cifra da 0a5

  3. Successivamente, possiamo usare questa figura come indice per una stringa, per produrre un carattere come output, ad es

    "ABCDEF"[3] 

    produrrà "D" (poiché il primo carattere = "A")

  4. L'aggiunta 101all'elenco è di generare un errore (Indice-) nel caso in cui il numero superi 100, poiché "ABCDEF"[6]non esiste. Lo stesso vale per n = n if n>=0 else "", che creerà un errore (Valore-) se viene inserito un numero inferiore a 0
    In questi casi, così come se l'input non è una cifra, il risultato sarà:

    invalid input

I test:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.