Làm thế nào để viết một tập lệnh shell để gán các lớp chữ cho các phạm vi số?


19

Tôi muốn tạo một tập lệnh sẽ nhắc bạn một số trong khoảng từ 0 đến 100 và sau đó cung cấp cho bạn điểm dựa trên số đó.

Tôi muốn nó trong 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

Câu trả lời:


20

Lực hấp dẫn so với khả năng đọc: Một nền tảng trung gian

Như bạn đã thấy, vấn đề này thừa nhận các giải pháp dài vừa phải và hơi lặp đi lặp lại nhưng rất dễ đọc ( câu trả lời bash của terdonAB ), cũng như các giải pháp rất ngắn nhưng không trực quan và ít tài liệu hơn ( python của Tim và bash câu trả lời và câu trả lời perl của glenn jackman ). Tất cả những cách tiếp cận này đều có giá trị.

Bạn cũng có thể giải quyết vấn đề này bằng mã ở giữa tính liên tục giữa tính gọn nhẹ và khả năng đọc. Cách tiếp cận này gần như dễ đọc như các giải pháp dài hơn, với độ dài gần với các giải pháp bí truyền nhỏ.

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

Trong giải pháp bash này, tôi đã bao gồm một số dòng trống để tăng cường khả năng đọc, nhưng bạn có thể xóa chúng nếu bạn muốn nó thậm chí còn ngắn hơn.

Bao gồm các dòng trống, cái này thực sự chỉ ngắn hơn một chút so với một biến thể nhỏ gọn, vẫn dễ đọc của giải pháp bash của AB . Ưu điểm chính của nó so với phương pháp đó là:

  • Nó trực quan hơn.
  • Dễ dàng thay đổi ranh giới giữa các lớp (hoặc thêm các lớp bổ sung).
  • Nó tự động chấp nhận đầu vào với các khoảng trắng ở đầu và cuối (xem bên dưới để được giải thích về cách thức (( ))hoạt động).

Tất cả ba ưu điểm này phát sinh do phương pháp này sử dụng đầu vào của người dùng làm dữ liệu số thay vì kiểm tra thủ công các chữ số cấu thành của nó.

Làm thế nào nó hoạt động

  1. Đọc đầu vào từ người dùng. Để họ sử dụng các phím mũi tên để di chuyển xung quanh trong văn bản họ đã nhập ( -e) và không diễn giải \thành ký tự thoát ( -r).
    Kịch bản này không phải là một giải pháp giàu tính năng - xem bên dưới để biết cách tinh chỉnh - nhưng những tính năng hữu ích đó chỉ làm cho nó dài hơn hai ký tự. Tôi khuyên bạn nên luôn luôn sử dụng -rvới read, trừ khi bạn biết bạn cần để cho người dùng \thoát ra.
  2. Nếu người dùng viết qhoặc Q, thoát.
  3. Tạo một mảng kết hợp ( ). Dân số với cấp số cao nhất được liên kết với mỗi cấp chữ cái.declare -A
  4. Lặp lại các cấp chữ cái từ thấp nhất đến cao nhất, kiểm tra xem số do người dùng cung cấp có đủ thấp để rơi vào phạm vi số của từng cấp thư không.
    Với (( ))đánh giá số học, tên biến không cần phải được mở rộng với $. (Trong hầu hết các tình huống khác, nếu bạn muốn sử dụng giá trị của biến thay cho tên của nó, bạn phải thực hiện việc này .)
  5. Nếu nó rơi vào phạm vi, in lớp và thoát .
    Để cho ngắn gọn, tôi sử dụng ngắn mạch toán tử ( &&) chứ không phải là if- then.
  6. Nếu vòng lặp kết thúc và không có phạm vi nào được khớp, giả sử số đã nhập quá cao (trên 100) và cho người dùng biết nó nằm ngoài phạm vi.

Cách cư xử này, với đầu vào lạ

Giống như các giải pháp ngắn khác được đăng, tập lệnh đó không kiểm tra đầu vào trước khi cho rằng đó là một số. Đánh giá số học ( (( ))) tự động loại bỏ khoảng trắng hàng đầu và dấu kiểm, do đó, không có vấn đề gì, nhưng:

  • Đầu vào trông không giống một con số nào được hiểu là 0.
  • Với đầu vào trông giống như một số (nghĩa là, nếu nó bắt đầu bằng một chữ số) nhưng chứa các ký tự không hợp lệ, tập lệnh sẽ phát ra lỗi.
  • Đầu vào nhiều chữ số bắt đầu bằng 0được hiểu là ở dạng bát phân . Ví dụ: tập lệnh sẽ cho bạn biết 77 là C, trong khi 077 là D. Mặc dù một số người dùng có thể muốn điều này, nhưng hầu hết có thể không và nó có thể gây nhầm lẫn.
  • Về mặt tích cực, khi được đưa ra một biểu thức số học, tập lệnh này sẽ tự động đơn giản hóa nó và xác định loại chữ liên quan. Chẳng hạn, nó sẽ cho bạn biết 320/4 là B.

Phiên bản mở rộng, đầy đủ tính năng

Vì những lý do đó, bạn có thể muốn sử dụng một cái gì đó như tập lệnh mở rộng này để kiểm tra để đảm bảo đầu vào tốt và bao gồm một số cải tiến khác.

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

Đây vẫn là một giải pháp khá nhỏ gọn.

Những tính năng này thêm gì?

Các điểm chính của tập lệnh mở rộng này là:

  • Xác nhận đầu vào. terdon của kiểm tra kịch bản đầu vào với , vì vậy tôi cho thấy một cách khác, mà hy sinh một số ngắn gọn nhưng là mạnh mẽ hơn, cho phép người dùng nhập vào ở đầu và đuôi không gian và từ chối cho phép một biểu thức mà có thể hoặc có thể không được dự định như bát phân (trừ khi đó là zero) .if [[ ! $response =~ ^[0-9]*$ ]] ...
  • Tôi đã sử dụng casevới tính năng mở rộng thay vì [[với toán tử =~ khớp biểu thức chính quy (như trong câu trả lời của terdon ). Tôi đã làm điều đó để chỉ ra rằng (và làm thế nào) nó cũng có thể được thực hiện theo cách đó. Globs và regexps là hai cách chỉ định các mẫu phù hợp với văn bản và một trong hai phương pháp đều tốt cho ứng dụng này.
  • Giống như tập lệnh bash của AB , tôi đã bao gồm toàn bộ nội dung trong một vòng lặp bên ngoài (ngoại trừ việc tạo cutoffsmảng ban đầu ). Nó yêu cầu số và đưa ra các cấp chữ cái tương ứng miễn là đầu vào thiết bị đầu cuối có sẵn và người dùng đã không yêu cầu nó thoát. Đánh giá bởi do... donexung quanh mã trong câu hỏi của bạn, có vẻ như bạn muốn điều đó.
  • Để làm cho việc bỏ thuốc dễ dàng, tôi chấp nhận bất kỳ biến thể không phân biệt chữ hoa chữ thường qhoặc quit.

Kịch bản này sử dụng một vài cấu trúc có thể xa lạ với người mới; chúng chi tiết dưới đây.

Giải thích: Sử dụng continue

Khi tôi muốn bỏ qua phần còn lại của cơ thể của whilevòng lặp bên ngoài , tôi sử dụng continuelệnh. Điều này đưa nó trở lại lên đầu vòng lặp, để đọc thêm đầu vào và chạy một lần lặp khác.

Lần đầu tiên tôi làm điều này, vòng lặp duy nhất tôi tham gia là whilevòng lặp bên ngoài , vì vậy tôi có thể gọi continuemà không có đối số. (Tôi đang ở trong một casecấu trúc, nhưng điều đó không ảnh hưởng đến hoạt động của breakhoặc continue.)

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

Tuy nhiên, lần thứ hai, tôi ở trong một forvòng lặp bên trong , chính nó được lồng bên trong whilevòng lặp bên ngoài . Nếu tôi sử dụng continuekhông có đối số, điều này sẽ tương đương continue 1và sẽ tiếp tục forvòng lặp bên trong thay vì whilevòng lặp bên ngoài .

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

Vì vậy, trong trường hợp đó, tôi sử dụng continue 2để thực hiện bash find và tiếp tục vòng lặp thứ hai.

Giải thích: caseNhãn có Globs

Tôi không sử dụng caseđể tìm ra thùng thư nào mà một số rơi vào (như trong câu trả lời bash của AB ). Nhưng tôi sử dụng caseđể quyết định xem có nên xem xét đầu vào của người dùng không:

  • một số hợp lệ, *( )@([1-9]*([0-9])|+(0))*( )
  • lệnh bỏ, *( )[qQ]?([uU][iI][tT])*( )
  • bất cứ điều gì khác (và do đó đầu vào không hợp lệ), *

Đây là những quả cầu vỏ .

  • Mỗi cái được theo sau bởi một )cái không khớp với bất kỳ mở nào (, đó là casecú pháp để tách một mẫu khỏi các lệnh chạy khi nó được khớp.
  • ;;casecú pháp để chỉ ra sự kết thúc của các lệnh để chạy cho khớp trường hợp paticular (và không có trường hợp nào tiếp theo nên được kiểm tra sau khi chạy chúng).

Globing shell thông thường cung cấp *để khớp 0 hoặc nhiều ký tự, ?để khớp chính xác một ký tự và các lớp / phạm vi ký tự trong [ ]ngoặc. Nhưng tôi đang sử dụng Globing mở rộng , vượt xa điều đó. Mở rộng toàn cầu được bật theo mặc định khi sử dụng bashtương tác, nhưng bị tắt theo mặc định khi chạy tập lệnh. Các shopt -s extgloblệnh ở phía trên cùng của kịch bản biến nó trên.

Giải thích: Globbing mở rộng

*( )@([1-9]*([0-9])|+(0))*( ), kiểm tra đầu vào số , khớp với một chuỗi:

  • Không hoặc nhiều khoảng trắng ( *( )). Cấu *( )trúc khớp với 0 hoặc nhiều mẫu trong ngoặc đơn, ở đây chỉ là một khoảng trắng.
    Thực tế, có hai loại khoảng trắng ngang, khoảng trắng và tab và thường thì cũng mong muốn khớp với các tab. Nhưng tôi không lo lắng về điều đó ở đây, bởi vì tập lệnh này được viết cho thủ công, đầu vào tương tác và -ecờ để readcho phép đọc đường dẫn GNU. Điều này là để người dùng có thể di chuyển qua lại trong văn bản của họ bằng các phím mũi tên trái và phải, nhưng nó có tác dụng phụ là thường ngăn các tab được nhập theo nghĩa đen.
  • Một lần xuất hiện ( @( )) của một trong hai ( |):
    • Một chữ số khác không ( [1-9]) theo sau là 0 hoặc nhiều hơn ( *( )) của bất kỳ chữ số nào ( [0-9]).
    • Một hoặc nhiều ( +( )) của 0.
  • Không hoặc nhiều khoảng trắng ( *( )), một lần nữa.

*( )[qQ]?([uU][iI][tT])*( ), kiểm tra lệnh thoát , khớp với một chuỗi:

  • Không hoặc nhiều khoảng trắng ( *( )).
  • qhoặc Q( [qQ]).
  • Tùy chọn - tức là không hoặc một lần xuất hiện ( ?( )) - của:
    • uhoặc U( [uU]) theo sau ihoặc I( [iI]) theo sau thoặc T( [tT]).
  • Không hoặc nhiều khoảng trắng ( *( )), một lần nữa.

Biến thể: Xác thực đầu vào bằng biểu thức chính quy mở rộng

Nếu bạn muốn kiểm tra đầu vào của người dùng dựa trên biểu thức chính quy thay vì toàn cầu shell, bạn có thể thích sử dụng phiên bản này, hoạt động tương tự nhưng sử dụng [[=~(như trong câu trả lời của terdon ) thay vì casevà toàn cầu hóa mở rộng.

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

Ưu điểm có thể có của phương pháp này là:

  • Trong trường hợp cụ thể này, cú pháp đơn giản hơn một chút, ít nhất là trong mẫu thứ hai, nơi tôi kiểm tra lệnh thoát. Điều này là do tôi có thể đặt nocasematchtùy chọn shell, và sau đó tất cả các biến thể trường hợp qquitđược bao phủ tự động.

    Đó là những gì shopt -s nocasematchlệnh làm. Các shopt -s extgloblệnh được bỏ qua như globbing không được sử dụng trong phiên bản này.

  • Các kỹ năng diễn đạt thông thường là phổ biến hơn là thành thạo các biểu hiện của bash.

Giải thích: Biểu thức chính quy

Đối với các mẫu được chỉ định ở bên phải của =~toán tử, đây là cách các biểu thức chính quy đó hoạt động.

^\ *([1-9][0-9]*|0+)\ *$, kiểm tra đầu vào số , khớp với một chuỗi:

  • Bắt đầu - tức là cạnh trái - của dòng ( ^).
  • Không hoặc nhiều hơn ( *, hậu tố được áp dụng). Một không gian thông thường không cần phải được định \nghĩa trong một biểu thức thông thường, nhưng điều này là cần thiết [[để ngăn ngừa lỗi cú pháp.
  • Một chuỗi con ( ( )) là một hoặc khác ( |) của:
    • [1-9][0-9]*: một chữ số khác ( [1-9]) theo sau là 0 hoặc nhiều hơn ( *, hậu tố được áp dụng) của bất kỳ chữ số nào ( [0-9]).
    • 0+: một hoặc nhiều ( +, hậu tố được áp dụng) của 0.
  • Không hoặc nhiều khoảng trắng ( \ *), như trước đây.
  • Kết thúc - tức là cạnh phải - của dòng ( $).

Không giống như casecác nhãn khớp với toàn bộ biểu thức đang được kiểm tra, =~trả về giá trị true nếu bất kỳ phần nào trong biểu thức bên trái của nó khớp với mẫu được đưa ra dưới dạng biểu thức bên phải của nó. Đây là lý do tại sao ^$các neo, chỉ định điểm bắt đầu và kết thúc của dòng, là cần thiết ở đây, và không tương ứng về mặt cú pháp với bất cứ điều gì xuất hiện trong phương thức với casevà extglobs.

Các dấu ngoặc đơn là cần thiết để thực hiện ^$liên kết với sự phân biệt của [1-9][0-9]*0+. Mặt khác, nó sẽ là sự phân biệt của ^[1-9][0-9]*0+$, và khớp với bất kỳ đầu vào nào bắt đầu bằng một chữ số khác hoặc kết thúc bằng một 0(hoặc cả hai, vẫn có thể bao gồm các chữ số không ở giữa).

^\ *q(uit)?\ *$, kiểm tra lệnh thoát , khớp với một chuỗi:

  • Đầu dòng ( ^).
  • Không hoặc nhiều khoảng trắng ( \ *, xem phần giải thích ở trên).
  • Bức thư q. Hoặc Q, kể từ khi shopt nocasematchđược kích hoạt.
  • Tùy chọn - tức là không hoặc một lần xuất hiện (postfix ?) - của chuỗi con ( ( )):
    • u, theo sau i, tiếp theo t. Hoặc, vì shopt nocasematchđược kích hoạt ucó thể được U; độc lập, icó thể I; và độc lập, tcó thể T. (Đó là, các khả năng không giới hạn ở uitUIT.)
  • Không hoặc nhiều khoảng trắng nữa ( \ *).
  • Kết thúc dòng ( $).

3
nói cho tôi biết sự thật..có bao nhiêu thời gian? ;)
heemayl

4
@heemayl Tôi không hoàn toàn chắc chắn, vì tôi đã viết nó thành nhiều phần nhỏ trong suốt cả ngày (tiếp theo là toàn bộ phần đọc và chỉnh sửa, ngay trước khi đăng). Tôi khá chắc chắn rằng nó đã kết thúc lâu hơn tôi nghĩ khi tôi bắt đầu, mặc dù, nếu tôi đã suy nghĩ về việc nó sẽ kéo dài bao lâu. :)
Eliah Kagan

6
viết nhiều hơn và nhiều hơn nữa, tôi cần một cuốn sách câu trả lời của bạn.
Grijesh Chauhan

TL; DR nhưng dù sao cũng làm tôi cười!
Fabby

tất cả mọi thứ từ tiêu đề để giải thích là tốt. Tôi đã hiểu nó ngay từ lần đầu tiên nhưng tôi đã đọc lại chỉ vì tôi muốn
Sumeet Deshmukh

23

Bạn đã có ý tưởng cơ bản. Nếu bạn muốn mã hóa mã này bash(đây là một lựa chọn hợp lý vì nó là vỏ mặc định trên Ubuntu và hầu hết các Linux khác), bạn không thể sử dụng casevì nó không hiểu phạm vi. Thay vào đó, bạn có thể sử dụng 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
Một loạt các -gebài kiểm tra có thể được loại bỏ, có lẽ, vì bạn đang sử dụng elif. Và không có tình yêu cho (( $response < X ))?
muru

2
@muru đúng, cảm ơn. Tôi đã bị mắc kẹt trong suy nghĩ trong phạm vi số nhưng không có lý do để. Đối với (( $response < X )), chắc chắn, nhưng tôi thấy rõ ràng này và OP rõ ràng là mới để 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

và một phiên bản nhỏ gọn hơn (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
Đó là những phạm vi nhân vật, chắc chắn? tức là [0-59]có nghĩa là bất kỳ ký tự nào từ 0,1,2,3,4,5 hoặc 9, v.v. Tôi không thấy làm thế nào mà có thể làm việc cho các giá trị số .
Steeldo

3
Bạn không cần phải là FGITW mọi lúc. Hãy dành thời gian cho bạn, viết câu trả lời tốt. Hãy nhìn cách terdon hoặc Eliah Kagan làm việc.
muru

@AB Tôi nhận thấy rằng giải pháp này có thể được rút ngắn, chủ yếu thông qua các thay đổi về phong cách, trong khi vẫn còn khá dễ đọc. Brevity hiếm khi được xem xét quan trọng nhất vì vậy tôi không nghĩ bạn nên thay đổi những gì bạn có theo cách này. Nhưng vì hình thức nhỏ gọn hơn không chiếm nhiều dung lượng, bạn cũng có thể xem xét hiển thị nó, trong trường hợp một số độc giả muốn một tập lệnh ngắn hơn hoạt động theo cùng một cách. (Nếu bạn thích, xin vui lòng sử dụng phiên bản rút gọn của tôi hoặc bất kỳ biến thể nào trên đó.)
Eliah Kagan

9

Tất cả các bản cài đặt Ubuntu đều có Python, vì vậy đây là một đoạn python . Nếu bạn cần nó trong bash, tôi cũng đã viết tương đương như một tập lệnh shell .

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

Để chạy, lưu nó trong một tệp (ví dụ grade.py) và sau đó chạy nó trong thiết bị đầu cuối với điều này:

python grade.py

Đây là những gì bạn sẽ thấy:

Enter the number: 65
E

Cái này hoạt động ra sao?

  1. Lấy đầu vào - 65.
  2. Thêm 0 vào đầu - 065.
  3. Xóa char cuối cùng - 06.
  4. 75 trừ số đó - 70.
  5. Chuyển đổi thành một chữ cái (A là 65, B là 66) - E.
  6. In nó ra - E.

Đại từ của tôi là He / Ngài


Tôi thích ý tưởng của bạn. +1
AB

Đừng sử dụng input(), nó sẽ gọi eval(), sử dụng raw_input()thay vào đó..cũng việc chấm điểm của bạn không đúng như 90+sẽ in lớp B.. sử dụng chr(74 - max(4, num))....
heemayl

tốt .. giải pháp của bạn là tốt và cũng sẽ hoạt động trong python2..chỉ cần thay đổi input()thành raw_input()cho python2..thật vậy ..
heemayl

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

Sau đó, bạn cũng cần thay đổi mã ban đầu của mình .. mã đã được sửa đổi của bạn vì hiện tại nó bị sai mặc dù python3không có raw_input().. tôi đã đề xuất raw_input()cho mã ban đầu của bạn như bạn đã nói để chạy nó bằng cách sử dụng python2..
heemayl

6

Đây là giải pháp bash semi -esoteric của tôi , tập hợp một mảng với 101 mục và sau đó kiểm tra đầu vào của người dùng đối với chúng. Ngay cả đối với việc sử dụng trong thế giới thực là hợp lý - nếu bạn cần hiệu suất tuyệt vời, bạn sẽ không sử dụng bash, và một trăm bài tập (hoặc hơn) vẫn còn nhanh. Nhưng nó sẽ không còn hợp lý nếu được mở rộng ra một phạm vi lớn hơn (như một triệu).

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

Ưu điểm:

  • Nó không thực sự bí truyền. Mặc dù nó dài hơn các giải pháp ngắn nhất và không hoàn toàn tự ghi lại như các giải pháp dài hơn ... đó là tài liệu tự ghi chép một cách hợp lý , trong khi vẫn quyết định ở khía cạnh nhỏ bé.
  • Nó cho phép dễ dàng sửa đổi để thay đổi phạm vi lớp hoặc thêm / xóa điểm.
  • Nó hoạt động trong một vòng lặp và bỏ vào q, quithoặc bất cứ điều gì bắt đầu với q/ Q.
  • Liệt kê các lớp cao hơn trước, để giúp bạn suy nghĩ tích cực. :)
  • Hmm, điều này thực hiện công việc, giữ ý nghĩa ngay cả sau khi bạn nhìn vào nó, và có các tính năng cần thiết. Bạn thực sự có thể sử dụng này!

Nhược điểm:

  • Nó cung cấp cho bạn một F khi bạn đặt đầu vào không phải là số ... nhưng điều đó không thực sự quá tệ phải không? Nếu bạn đưa ra một số không cần số, có thể bạn xứng đáng với F!
  • Đầu vào mơ hồ, có thể là bát phân được coi là bát phân (vì gmảng được lập chỉ mục một chiều ). Như người xưa thường nói, "Đó không phải là một lỗi, đó là một tính năng!" Vâng, có lẽ.
  • Đầu vào nằm ngoài phạm vi hoặc không phải là số khiến dòng trống được in. Không có gì thực sự sai với điều đó mặc dù: nó cho bạn biết loại chữ nào tương ứng với đầu vào của bạn và đối với đầu vào sai thì không có.
  • Đưa vào một số tiêu cực, và nó ... tốt, gọi nó là một quả trứng Phục sinh .
  • Vẫn dài hơn đáng kể so với giải pháp python của Tim . Vâng, tôi thực sự không thể quay nó để có vẻ như là một lợi thế.

Thật tuyệt phải không? (Tôi cũng nghĩ vậy.)

Làm thế nào nó hoạt động

  1. Các pchức năng p opulates một mảng số lượng được lập chỉ mục gcủa g rades, tại chỉ số khác nhau, từ số đầu tiên của mình cho lần thứ hai, với (thư) giá trị được đưa ra trong tham số thứ ba của nó.
  2. p được gọi cho mỗi lớp chữ cái, để xác định phạm vi số của nó.
  3. Tiếp tục đọc dữ liệu nhập của người dùng miễn là có sẵn và không bắt đầu bằng q(hoặc Q), kiểm tra gmảng cho loại chữ cái nào tương ứng với số đã nhập và in chữ cái đó.

Điều gì về điều kiện này? [[ $n =~ ^(0|[1-9]+[0-9]*)$ ]]
Helio

6

Sau khi thực hiện nó trong Python 2 , tôi quyết định làm cho nó trong 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"

Để chạy, hãy lưu nó trong một tệp (ví dụ: class.sh), làm cho nó có thể thực thi được chmod +x grade.shvà sau đó chạy với ./grade.sh.

Đây là những gì bạn sẽ thấy:

Enter the number: 65
E

Cái này hoạt động ra sao?

  1. Lấy đầu vào - 65.
  2. Thêm số 0 vào đầu - 065(và 10#giữ nguyên số 10).
  3. Xóa char cuối cùng - 06.
  4. 75 trừ số đó - 70.
  5. Chuyển đổi thành một chữ cái (A là 65, B là 66) - E.
  6. In nó ra - E.

Đại từ của tôi là He / Ngài


Rất thông minh, hoàn thành tốt
kos

@kos cảm ơn :) Tôi nghi ngờ nó sẽ hoạt động cho OP vì phạm vi của anh ấy có thể không phải là những gì anh ấy đã đăng. Tôi mong đợi những điều đó là đơn giản.
Tim

5

Và đây là phiên bản awk của tôi:

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

hoặc như một lớp lót:

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

Đây là một câu trả lời "bí truyền" khác

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
'

Giải trình

  • perl -E: the -E, like -e, cho phép truyền một tập lệnh dưới dạng đối số dòng lệnh. Đây là một cách để chạy perl one-liners. Không giống như -e, -Ecũng cho phép tất cả các tính năng tùy chọn (chẳng hạn như say, về cơ bản là printvới một dòng mới.).
  • print "number: "; : nhắc người dùng nhập số.
  • $n = <>;: lưu số đó dưới dạng $n.

Các bit tiếp theo cần được chia nhỏ một chút. qw/string/đánh giá một danh sách được thực hiện bằng cách phá vỡ stringở khoảng trắng. Vì vậy, qw/A A B C D E F F F F F/thực sự là danh sách này:

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

Do đó, say qw/A A B C D E F F F F F/[11-($n+1)/10]tương đương với

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

Bây giờ, Perl cho phép sử dụng các chỉ số âm để lấy các phần tử đếm từ cuối mảng. Ví dụ, $arrray[-1]sẽ in phần tử cuối cùng của mảng. Ngoài ra, các chỉ số mảng dấu phẩy động (ví dụ 10.7) sẽ tự động bị cắt cụt sang số nguyên thấp hơn tiếp theo (10,7 hoặc 10,3 hoặc bất cứ thứ gì trở thành 10.)

Kết quả của tất cả điều này là chỉ số 11-($n+1)/10luôn luôn đánh giá thành phần tử (lớp) thích hợp của mảng.


4
Tất cả các câu trả lời bí truyền đều tốt, nhưng vui lòng bao gồm một lời giải thích.
muru

1

Mặc dù bạn đã yêu cầu một giải pháp bash, tôi nghĩ trong python, điều này có thể được thực hiện một cách thanh lịch một cách ngắn gọn. Bao gồm cả lỗi xử lý trong trường hợp nhập sai và "chuyển đổi" một số từ 0 đến 100 thành các chữ cái từ A đến F (hoặc bất kỳ số nào khác):

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

Giải trình

  1. Đầu tiên chúng ta cần lấy số từ người dùng:

    n = int(input("number: "))
  2. Chúng tôi kiểm tra con số này là hợp lệ cho một số điều kiện:

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

    Đối với mỗi thử nghiệm này, kết quả sẽ là Falsehoặc True. Do đó (nén mã một chút):

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

    sẽ tạo ra một con số từ 0đến5

  3. Sau đó, chúng ta có thể sử dụng hình này làm chỉ mục cho một chuỗi, để tạo ra một ký tự làm đầu ra, ví dụ:

    "ABCDEF"[3] 

    sẽ xuất "D" (vì ký tự đầu tiên = "A")

  4. Việc bổ sung 101vào danh sách là tạo ra lỗi (Index-) trong trường hợp số lượng vượt quá 100, vì "ABCDEF"[6]không tồn tại. Điều tương tự cũng xảy ra n = n if n>=0 else "", sẽ tạo ra lỗi (Giá trị-) nếu nhập số dưới 0
    Trong các trường hợp đó, cũng như nếu đầu vào không phải là hình, kết quả sẽ là:

    invalid input

Các bài kiểm tra:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.