C10-06 · Quản lý bằng lệnh
Xem dạng PDFĐề
Bài lớn nhất của chuỗi dự án Chương 10: hoàn thiện bản đọc lệnh của chương trình quản lý điểm (mục 10.3.4). Chương trình đọc danh sách ban đầu gồm n sinh viên theo khung XS-07 (dòng mã sai bị bỏ qua lặng lẽ), rồi thực hiện lần lượt m lệnh, in kết quả của từng lệnh ngay sau khi thực hiện. Mỗi sinh viên khi được in có dạng ma Ho Ten diem_hp loai như C10-01.
So với bản của sách, bản này có thêm hai lệnh TOP, TEN và vá hai chỗ hở mà mục 10.4 đã chỉ ra (điểm ngoài [0; 10] vẫn lọt; SUA chưa nhận dấu phẩy thập phân).
| Lệnh | Kết quả in |
|---|---|
XEM |
bảng điểm theo thứ tự hiện tại của danh sách; danh sách rỗng: RONG |
THEM ma ho ten qt ck |
ma DA THEM (thêm vào cuối danh sách), hoặc báo lỗi theo thứ tự kiểm ở dưới |
TIM ma |
dòng của sinh viên đó, hoặc ma KHONG TIM THAY |
SUA ma qt ck |
ma DA SUA, hoặc báo lỗi theo thứ tự kiểm ở dưới |
XOA ma |
ma DA XOA, hoặc ma KHONG TIM THAY |
THONGKE |
năm dòng A so, B so, C so, D so, F so (kể cả loại 0 người) |
TOP k (mới) |
k sinh viên điểm học phần cao nhất, bằng điểm thì mã nhỏ trước (như C10-03); k lớn hơn số sinh viên thì in hết; danh sách rỗng thì không in gì |
TEN tu_khoa (mới) |
mọi sinh viên có họ tên chứa từ khóa, không phân biệt hoa thường, theo thứ tự trong danh sách (như C10-04); không có ai: KHONG TIM THAY |
Thứ tự kiểm của THEM (giống them_sv của sách, thêm bước 3):
- phần đầu (mã) không đúng 8 chữ số →
ma MA SAI; - thiếu họ tên hoặc thiếu điểm, điểm không đổi được thành số, họ tên có dấu phẩy →
LENH SAI; - có điểm ngoài [0; 10] →
LENH SAI(xét sau mã sai, trước mã trùng); - mã đã có trong danh sách →
ma TRUNG; - còn lại: thêm, in
ma DA THEM. Họ tên được chuẩn hóa; điểm được viết dấu phẩy (8,8).
Thứ tự kiểm của SUA: thiếu tham số hoặc điểm không đổi được thành số → LENH SAI; có điểm ngoài [0; 10] → LENH SAI (xét trước khi tìm mã); mã không có → ma KHONG TIM THAY; còn lại đổi hai điểm, in ma DA SUA. SUA nhận điểm viết dấu phẩy như THEM (SUA 23010005 4,5 5).
Các trường hợp LENH SAI khác — chương trình in LENH SAI rồi làm tiếp, không được dừng: dòng lệnh trống; tên lệnh lạ (tên lệnh phân biệt hoa thường: xem, Top là lệnh lạ); TIM, XOA, TEN thiếu tham số; TOP thiếu tham số hoặc tham số không phải số nguyên (TOP abc, TOP 2.5).
Lưu ý về điểm: float() đổi được cả chữ nan, inf mà không báo lỗi; đó không phải điểm trong [0; 10] nên THEM, SUA với các "điểm" đó phải in LENH SAI.
TIM, XOA, SUA không kiểm dạng mã: mã không có trong danh sách (kể cả mã sai dạng) thì KHONG TIM THAY. Điểm học phần và loại không lưu mà luôn tính lại, nên sau SUA loại và thứ hạng TOP tự đổi theo.
Dữ liệu vào
- Dòng đầu: số nguyên n; n dòng tiếp theo, mỗi dòng
ma ho ten diem_qt diem_ck(như C10-01). - Dòng tiếp: số nguyên m; m dòng tiếp theo, mỗi dòng một lệnh. Tên lệnh và các tham số cách nhau bởi dấu cách.
Kết quả
Với mỗi lệnh, theo thứ tự, các dòng kết quả như bảng trên.
Ràng buộc
- 0 ≤ n ≤ 1000; 0 ≤ m ≤ 1000.
- Mã sinh viên trong danh sách: 1–12 ký tự, chỉ gồm chữ số và chữ cái Latin; hợp lệ khi gồm đúng 8 chữ số. Các mã hợp lệ của danh sách ban đầu không trùng nhau.
- Họ tên trong danh sách: 1–10 từ, mỗi từ 1–20 chữ cái Latin không dấu, hoa thường tùy ý; giữa các phần có thể có nhiều dấu cách.
- Mỗi điểm trong danh sách thuộc [0; 10], dạng số nguyên hoặc một chữ số thập phân, dấu chấm hoặc dấu phẩy.
- Mỗi dòng lệnh có thể trống; nếu không, gồm tối đa 14 phần cách nhau bởi dấu cách, mỗi phần 1–20 ký tự là chữ cái Latin, chữ số, dấu chấm, dấu phẩy hoặc dấu trừ; không có khoảng trắng ở đầu và cuối dòng.
XEM,THONGKEkhông có tham số;TIM,XOA,TOP,TENcó nhiều nhất 1 tham số;SUAcó nhiều nhất 3 tham số. Mã trong lệnh: 1–12 chữ cái/chữ số.- Ở vị trí điểm (
SUA: tham số 2, 3;THEM: các phần sau mã), một phần có chữ số luôn là số thập phân có tối đa 1 chữ số sau dấu chấm/phẩy (có thể âm, có thể lớn hơn 10, không viết-0); phần không có chữ số chỉ gồm chữ cái (hoặc chữ cái và dấu phẩy trong họ tên). - Tham số của
TOPnếu là số nguyên (chỉ gồm chữ số, có thể có dấu trừ ở đầu) thì là số từ 1 đến 10¹⁰ − 1, viết không có số 0 đầu; nghĩa là dữ liệu không cóTOP 0hayTOP -3, không cần xử lý hai trường hợp này. Chú ý k có thể rất lớn (tới 10¹⁰ − 1): đừng lặp k lần. Từ khóa củaTENgồm 1–20 chữ cái Latin.
Gợi ý
Làm đúng như mục 10.5 gợi ý: viết top_k(ds_sv, k) và tim_theo_ten(ds_sv, tu_khoa) trả về list dòng như bang_diem; thêm hai nhánh TOP, TEN vào thuc_hien trước dòng return ["LENH SAI"]; viết diem_hop_le(diem) rồi gọi trong them_sv và sua_diem; với SUA, đổi dấu phẩy bằng replace(",", ".") trước khi float. TOP abc không cần xử lý riêng: int("abc") báo ValueError, chay_lenh đã bắt. Kiểm từng hàm mới bằng assert trước khi chạy cả chương trình (mục 10.4).
Ví dụ 1
Dữ liệu vào:
2
23010001 Nguyen Van An 7.5 8.0
23010008 Nguyen Hoai Nam 9 7
5
THEM 23010003 le minh DUC 8 8,8
SUA 23010001 11 8,5
TOP 2
TEN nguyen
XOA 23010009
Kết quả:
23010003 DA THEM
LENH SAI
23010003 Le Minh Duc 8.5 A
23010001 Nguyen Van An 7.8 B
23010001 Nguyen Van An 7.8 B
23010008 Nguyen Hoai Nam 7.8 B
23010009 KHONG TIM THAY
Ví dụ 2
Dữ liệu vào:
6
23010001 Nguyen Van An 7.5 8.0
23010002 Tran Thi Binh 6.5 7.0
23010003 Le Minh Duc 8.0 8.8
23010004 Pham Quoc Dung 4.0 4.0
23010005 Hoang Gia Han 5.0 3.0
23010006 Vu Ngoc Khanh 10 10
9
THONGKE
THEM 23010002 Tran Binh 7 7
THEM 23010007 do bach 6,5 7
SUA 23010005 4.5 nam
SUA 23010005 3.9 4
TIM 23010005
XOA 23010004
XEMM
THONGKE
Kết quả:
A 2
B 1
C 1
D 1
F 1
23010002 TRUNG
23010007 DA THEM
LENH SAI
23010005 DA SUA
23010005 Hoang Gia Han 4.0 D
23010004 DA XOA
LENH SAI
A 2
B 1
C 2
D 1
F 0
Giải thích ví dụ
- Ví dụ 1 (ví dụ của sách):
THEMnhận điểm8,8viết dấu phẩy và chuẩn hóa họ tên thànhLe Minh Duc(0,4 × 8 + 0,6 × 8,8 = 8,48 → 8,5).SUA 23010001 11 8,5có điểm 11 ngoài [0; 10] nênLENH SAI, điểm của An giữ nguyên.TOP 2: Duc 8,5 đứng đầu; An và Nam cùng 7,8, An mã nhỏ hơn nên được chọn.TEN nguyenkhớpNguyencủa An và Nam (không phân biệt hoa thường), in theo thứ tự trong danh sách. Mã 23010009 không có nênKHONG TIM THAY. - Ví dụ 2 là "lần chạy đầy đủ" của mục 10.4:
THONGKEđầu khớp sáu ca chuẩn; mã 23010002 đã có nênTRUNG; Bach được 6,8 (C);namkhông đổi được thành số →LENH SAI; sửa Han thành 3,9 và 4 thì 1,56 + 2,4 = 3,96 → 4,0, loại D chứ không phải F; xóa Dung (D);XEMMlà lệnh lạ;THONGKEcuối: A 2, B 1, C 2, D 1, F 0.
Phụ lục: các hàm của quan_ly.py (mục 10.3)
Chép nguyên từ sách (bỏ ba hàm đọc/ghi tệp CSV tach_dong_csv, ghi_danh_sach, doc_danh_sach vì bài nộp không dùng tệp). Hệ thống chấm chỉ nhận một tệp: chép các hàm này lên đầu bài nộp, bỏ dòng import quan_ly và bỏ tiền tố quan_ly. trước tên hàm.
def tinh_diem_hp(diem_qt, diem_ck):
return round(0.4 * diem_qt + 0.6 * diem_ck, 1)
def xep_loai(diem_hp):
if diem_hp >= 8.5:
return "A"
if diem_hp >= 7.0:
return "B"
if diem_hp >= 5.5:
return "C"
if diem_hp >= 4.0:
return "D"
return "F"
def chuan_hoa_ho_ten(ho_ten):
return " ".join(ho_ten.split()).title()
def ma_sv_hop_le(ma):
return len(ma) == 8 and ma.isdigit()
def tach_dong(dong):
ma, phan_sau = dong.split(maxsplit=1)
if not ma_sv_hop_le(ma):
return None
ho_ten, diem_qt, diem_ck = phan_sau.rsplit(maxsplit=2)
diem_qt = float(diem_qt.replace(",", "."))
diem_ck = float(diem_ck.replace(",", "."))
return (ma, chuan_hoa_ho_ten(ho_ten), diem_qt, diem_ck)
def tao_sinh_vien(dong):
ban_ghi = tach_dong(dong)
if ban_ghi is None:
return None
ma, ho_ten, diem_qt, diem_ck = ban_ghi
return {"ma": ma, "ho_ten": ho_ten, "diem_qt": diem_qt, "diem_ck": diem_ck}
def tim_theo_ma(ds_sv, ma):
for sv in ds_sv:
if sv["ma"] == ma:
return sv
return None
def them_sv(ds_sv, dong):
ma = dong.split()[0]
sv = tao_sinh_vien(dong)
if sv is None:
return ma + " MA SAI"
if "," in sv["ho_ten"]:
return "LENH SAI"
if tim_theo_ma(ds_sv, ma) is not None:
return ma + " TRUNG"
ds_sv.append(sv)
return ma + " DA THEM"
def sua_diem(ds_sv, ma, diem_qt, diem_ck):
sv = tim_theo_ma(ds_sv, ma)
if sv is None:
return ma + " KHONG TIM THAY"
sv["diem_qt"], sv["diem_ck"] = diem_qt, diem_ck
return ma + " DA SUA"
def xoa_sv(ds_sv, ma):
sv = tim_theo_ma(ds_sv, ma)
if sv is None:
return ma + " KHONG TIM THAY"
ds_sv.remove(sv)
return ma + " DA XOA"
def dong_bang_diem(sv):
diem_hp = tinh_diem_hp(sv["diem_qt"], sv["diem_ck"])
return " ".join([sv["ma"], sv["ho_ten"],
f"{diem_hp:.1f}", xep_loai(diem_hp)])
def bang_diem(ds_sv):
if len(ds_sv) == 0:
return ["RONG"]
return [dong_bang_diem(sv) for sv in ds_sv]
def dem_theo_loai(ds_sv):
dem = {}
for sv in ds_sv:
diem_hp = tinh_diem_hp(sv["diem_qt"], sv["diem_ck"])
loai = xep_loai(diem_hp)
dem[loai] = dem.get(loai, 0) + 1
return dem
def thong_ke(ds_sv):
dem = dem_theo_loai(ds_sv)
return [loai + " " + str(dem.get(loai, 0))
for loai in "ABCDF"]
def thuc_hien(ds_sv, lenh):
if lenh[0] == "XEM":
return bang_diem(ds_sv)
if lenh[0] == "THONGKE":
return thong_ke(ds_sv)
if lenh[0] == "TIM":
sv = tim_theo_ma(ds_sv, lenh[1])
if sv is None:
return [lenh[1] + " KHONG TIM THAY"]
return [dong_bang_diem(sv)]
if lenh[0] == "THEM":
return [them_sv(ds_sv, " ".join(lenh[1:]))]
if lenh[0] == "SUA":
return [sua_diem(ds_sv, lenh[1],
float(lenh[2]), float(lenh[3]))]
if lenh[0] == "XOA":
return [xoa_sv(ds_sv, lenh[1])]
return ["LENH SAI"]
def chay_lenh(ds_sv, lenh):
try:
return thuc_hien(ds_sv, lenh)
except (ValueError, IndexError):
return ["LENH SAI"]
Nguồn trong sách: Bài C10-06, mục 10.5, giáo trình Lập trình cơ sở với Python — TS. Nguyễn Quốc Long · Mức: (****)