TRẦN NHẬT QUANG PHẠM VĂN KHOA GIÁO TRÌNH LẬP TRÌNH PYTHON CĂN BẢN NHAØ XUAÁT BAÛN ÑAÏI HOÏC QUOÁC GIA TP. HOÀ CHÍ MINH TS. TRẦN NHẬT QUANG, TS. PHẠM VĂN KHOA GIÁO TRÌNH LẬP TRÌNH PYTHON CĂN BẢN NHÀ XUẤT BẢN ĐẠI HỌC QUỐC GIA THÀNH PHỐ HỒ CHÍ MINH – 2023 2 LỜI NÓI ĐẦU Python hiện là ngôn ngữ lập trình phổ biến nhất thế giới1. Ưu điểm nổi bật của Python là dễ học, dễ viết. Không những thế, Python còn có một cộng đồng người dùng lớn và hệ thống thư viện mã nguồn mở đồ sộ giúp bạn hoàn thành các dự án của mình nhanh chóng và hiệu quả. Cho dù đó là một dự án về phân tích dữ liệu, học máy, xử lý ảnh, game, điều khiển thiết bị, hoặc chỉ đơn giản là tự động hóa các tác vụ trên máy tính của bạn, thì gần như bạn đều có thể tìm thấy các thư viện Python hỗ trợ. Quyển giáo trình này được biên soạn với mong muốn hỗ trợ hiệu quả cho các bạn sinh viên học lập trình Python. Tiêu chí thứ nhất của nhóm biên soạn là giúp người học lập trình lần đầu cũng có thể dễ dàng nắm bắt được các khái niệm và kỹ thuật lập trình với Python. Tiêu chí thứ hai là cố gắng trình bày nội dung một cách súc tích, nhưng vẫn đầy đủ, để những bạn đã học qua lập trình có thể nhanh chóng làm quen và sử dụng được Python khi học xong giáo trình. Rất mong quyển giáo trình này sẽ hữu ích với các bạn! Nhóm biên soạn cũng mong nhận được góp ý của độc giả để cải thiện giáo trình này ngày càng tốt hơn. Xin cảm ơn các bạn! 1 Theo xếp hạng của TIOBE Index và PYPL Index cập nhật vào tháng 3 năm 2022. 3 4 MỤC LỤC LỜI NÓI ĐẦU .......................................................................................... 3 MỤC LỤC ................................................................................................ 5 DANH MỤC HÌNH ẢNH ......................................................................... 8 Chương 1 GIỚI THIỆU VÀ CÀI ĐẶT PYTHON .................................. 9 1.1 Đôi nét về ngôn ngữ lập trình Python ............................................ 9 1.2 Lịch sử phát triển Python ............................................................. 10 1.3 Cài đặt Python .............................................................................. 11 1.4 Một số lời khuyên hữu ích cho những người mới ...................... 16 Chương 2 PHÉP TOÁN CƠ BẢN, BIẾN VÀ NHẬP XUẤT TRONG PYTHON .............................................................................................. 18 2.1 Sử dụng VS Code như một máy tính cầm tay.............................. 18 2.2 Các phép toán ............................................................................... 19 2.3 Biến .............................................................................................. 21 2.4 Nhập xuất cơ bản ......................................................................... 23 2.5 Lệnh if .......................................................................................... 26 Bài tập có lời giải ................................................................................. 32 Bài tập thực hành ................................................................................. 33 Chương 3 VÒNG LẶP VÀ CẤU TRÚC DỮ LIỆU MẢNG ................ 35 3.1 Vòng lặp while ............................................................................. 35 3.2 Cấu trúc dữ liệu mảng .................................................................. 41 3.3 Vòng lặp for ................................................................................. 46 3.4 Break, continue và pass................................................................ 49 Bài tập có lời giải ................................................................................. 50 Bài tập thực hành ................................................................................. 51 Chương 4 NUMPY ................................................................................ 54 4.1 Giới thiệu về Numpy.................................................................... 54 4.2 Cài đặt thư viện numpy ................................................................ 54 4.3 Numpy arrays ............................................................................... 55 5 Bài tập thực hành ................................................................................. 58 Chương 5 SETS VÀ DICTIONARIES ................................................. 60 5.1 Sets............................................................................................... 60 5.2 Dictionaries .................................................................................. 62 Bài tập có lời giải ................................................................................. 67 Bài tập thực hành ................................................................................. 68 Chương 6 STRINGS .............................................................................. 69 6.1 Khái niệm và khởi tạo strings ...................................................... 69 6.2 Hàm xử lý strings ......................................................................... 71 Bài tập thực hành ................................................................................. 73 Chương 7 HÀM ..................................................................................... 74 7.1 Khái niệm và cú pháp .................................................................. 74 7.2 Một số ví dụ .....................................................................................74 7.3 Biến đoạn code bất kỳ thành hàm ................................................ 81 Bài tập có lời giải ................................................................................. 83 Bài tập thực hành ................................................................................. 84 Chương 8 LỖI VÀ SỬA LỖI ................................................................ 86 8.1 Các dạng lỗi trong lập trình ......................................................... 86 8.2 Xử lý lỗi runtime .......................................................................... 87 8.3 Xử lý lỗi logic .............................................................................. 90 8.4 Các lưu ý khi viết code để hạn chế lỗi ......................................... 95 Bài tập thực hành ................................................................................. 97 Chương 9 VẼ VỚI MATPLOTLIB ....................................................... 99 9.1 Cách thức vẽ trên màn hình kỹ thuật số ....................................... 99 9.2 Vẽ với thư viện matplotlib ......................................................... 100 9.3 Vẽ đồ thị trong tọa độ cực.......................................................... 106 9.4 Tùy chỉnh hình vẽ ...................................................................... 108 9.5 Vẽ trên nhiều phân vùng với subplotlib ..................................... 116 Bài tập thực hành ............................................................................... 120 6 PHỤ LỤC .............................................................................................. 122 Mã nguồn bài tập có lời giải chương 2 .............................................. 122 Mã nguồn bài tập có lời giải chương 3 .............................................. 127 Mã nguồn bài tập có lời giải chương 5 .............................................. 131 Mã nguồn bài tập có lời giải chương 7 .............................................. 135 Keywords của Python ........................................................................ 142 Cài đặt các thuật toán sắp xếp bằng Python ...................................... 145 Cài đặt các thuật toán tìm kiếm trên mảng bằng Python ................... 151 Cài đặt các thuật toán tìm kiếm trên chuỗi bằng Python ................... 158 INDEX ................................................................................................... 161 TÀI LIỆU THAM KHẢO ..................................................................... 163 7 DANH MỤC HÌNH ẢNH Hình 1-1 Logo của Python. Nguồn: Python Software Foundation ............ 9 Hình 1-2 Cài đặt Python bằng cách tick vào ô Add Python... to PATH ở cửa sổ cài đặt đầu tiên. ............................................................................. 12 Hình 1-3 Giao diện của Visual Studio Code. .......................................... 12 Hình 1-4 Giao diện trang OnlineGDB. .................................................... 16 Hình 1-5 Ứng dụng Pydroid 3 trên Google Play. .................................... 16 Hình 2-1 Lưu đồ của khối lệnh if. ........................................................... 28 Hình 3-1 Lưu đồ hoạt động của vòng lặp while. ..................................... 35 Hình 3-2 Lưu đồ hoạt động của vòng lặp for. ......................................... 47 8 Chương 1 GIỚI THIỆU VÀ CÀI ĐẶT PYTHON 1.1 Đôi nét về ngôn ngữ lập trình Python Python là một ngôn ngữ lập trình cấp cao và đa dụng (generalpurpose) được phát triển bởi Guido van Rossum. Phiên bản đầu tiên của nó được phát hành vào năm 1991. Tên của nó được đặt theo chương trình hài Monty Python1 của Anh như một cách phản ánh triết lý thiết kế của Python: một ngôn ngữ lập trình thú vị khi sử dụng. Hình 1-1 Logo của Python. Nguồn: Python Software Foundation Triết lý của ngôn ngữ lập trình Python được mô tả bằng những cách ngôn trong tài liệu The Zen of Python (PEP 20) như: ▪ ▪ ▪ ▪ Simple is better than complex (tạm dịch: Đơn giản tốt hơn phức tạp). Complex is better than complicated (tạm dịch: Phức hợp tốt hơn phức tạp). Explicit is better than implicit (tạm dịch: Tường minh tốt hơn là ngầm định). Readability counts (tạm dịch: Lưu tâm đến sự dễ đọc hiểu). Với những triết lý đó, Python hướng tới sự đơn giản, ngắn gọn trong mã lệnh của mình. Bạn sẽ cảm nhận được điều này khi bắt đầu lập trình với Python và so sánh nó với các ngôn ngữ như C/C++, Java. Đến nay, Python đã được phát triển qua nhiều phiên bản. Hai nhóm phiên bản được sử dụng hiện nay là Python 2.x và Python 3.x. Tuy nhiên, 1 Theo General Python FAQ, docs.python.org 9 các phiên bản 2.x đã không còn được hỗ trợ đầy đủ từ ngày 1/1/20201. Phiên bản mới nhất của Python là 3.10 (phát hành ngày 4/10/2021)2. 1.2 Lịch sử phát triển Python3 Python được bắt đầu phát triển vào cuối những năm 1980 bởi Guido van Rossum tại Centrum Wiskunde & Informatica (CWI), Hà Lan như một ngôn ngữ kế thừa của ngôn ngữ lập trình ABC có khả năng xử lý ngoại lệ và giao tiếp với hệ điều hành Amoeba. Python được bắt đầu phát triển vào tháng 12 năm 1989. Vào thời điểm đó, van Rossum là tác giả duy nhất của dự án, với tư cách là nhà phát triển chính, cho đến ngày 12 tháng 7 năm 2018. Vào tháng 1 năm 2019, các nhà phát triển cốt lõi Python đã bầu ra một Hội đồng chỉ đạo gồm năm thành viên để lãnh đạo dự án. Python 2.0 được phát hành vào ngày 16 tháng 10 năm 2000, với nhiều tính năng mới. Python 3.0, được phát hành vào ngày 3 tháng 12 năm 2008, với nhiều tính năng chính vẫn hỗ trợ ngược Python 2.6.x và 2.7.x. Các bản phát hành của Python 3 tích hợp tiện ích 2to3 giúp dịch mã tự động từ Python 2 sang Python 3. Python 2.7 ban đầu được chỉ định sẽ chấm dứt hoạt động vào năm 2015, nhưng sau đó bị hoãn lại đến năm 2020 vì những lo ngại về việc chuyển đổi các code Python 2 hiện có sang Python 3. Từ thời điểm đó, Python 2 không nhận được thêm bất kỳ bản vá bảo mật hoặc cải tiến nào nữa. Sau khi Python 2 bị ngừng hỗ trợ, chỉ còn Python 3.6.x và các phiên bản mới hơn được hỗ trợ. Một thời gian sau, Python 3.6 cũng bị ngừng hỗ trợ. Đến năm 2021, Python 3.9.2 và 3.8.8 được phát triển vì tất cả các phiên bản Python trước (bao gồm 2.7) đều có vấn đề bảo mật có thể khiến máy tính bị thực thi mã từ xa và nhiễm độc bộ nhớ web cache. Vào năm 2022, Python 3.10.4 và 3.9.12 được phát triển và các bản cũ hơn bao gồm 3.8.13 và 3.7.13 được cập nhật vì nhiều vấn đề bảo mật. 1 Theo PEP 373 -- Python 2.7 Release Schedule Theo PEP 619 -- Python 3.10 Release Schedule 3 Theo https://en.wikipedia.org/wiki/Python_(programming_language) 2 10 1.3 Cài đặt Python Có nhiều cách khác nhau để lập trình với Python. Phần này mô tả ba cách phù hợp với các nhóm người dùng với điều kiện về thiết bị khác nhau. Cách 1. Lập trình Python trên máy tính sử dụng VS Code Nếu bạn sở hữu một máy tính cá nhân thì nên dùng cách này để tận dụng được đầy đủ chức năng của Python một cách thuận tiện. Trước tiên, bạn cần cài đặt trình biên dịch Python (Python interpreter). Nên chọn phiên bản Python 3.7 hoặc mới hơn.1 Một lưu ý khi cài đặt Python intepreter là bạn nên tick vào ô “Add Python ... to PATH” ở cửa sổ cài đặt đầu tiên (xem Hình 1-2). Sau khi đã cài đặt xong trình biên dịch Python, bạn nên cài đặt một editor (trình soạn thảo) hoặc một IDE (Integrated Development Environment) để lập trình Python được dễ dàng hơn. Có nhiều editor, IDE khác nhau hỗ trợ lập trình Python. Trong giáo trình này, chúng tôi khuyến nghị sử dụng Visual Studio Code2 (VS Code). Editor này có các ưu điểm như: ▪ ▪ ▪ ▪ Miễn phí, mã nguồn mở, hỗ trợ nhiều nền tảng (Windows, Linux, Mac). IntelliSense: giúp viết code nhanh chóng hơn bằng cách đưa ra các lựa chọn tự động hoàn thành code cho bạn. Hỗ trợ tìm và sửa lỗi (debug) hiệu quả. Nhiều extension hữu ích: như kết nối với Git, đọc file Jupyter Notebook (.ipynb), hỗ trợ Docker. 1 Download Python phiên bản mới nhất tại https://www.python.org/downloads/. Với những phiên bản cũ, download tại https://www.python.org/ftp/python/. 2 Download bản cài đặt VS Code tại https://code.visualstudio.com/download. 11 Hình 1-2 Lưu ý: Khi cài đặt Python nên tick vào ô Add Python ... to PATH để thuận tiện chạy các Scripts Python về sau.. Hình 1-3 Giao diện của Visual Studio Code. Sau khi cài đặt VS Code xong, bạn nên thực hiện các bước sau để tạo một file code Python đầu tiên và cũng để VS Code hoàn tất cài đặt các extension hỗ trợ lập trình Python. 12 1. Chọn File > Open Folder… 2. Tạo mới hoặc chọn một thư mục rồi nhấn Select Folder. Lưu ý: đối với máy tính sử dụng Windows, bạn nên chọn một thư mục trong ổ D: hoặc một ổ đĩa mà bạn có đầy đủ quyền chạy các mã lệnh. Không dùng các thư mục có tên “python” hoặc “code” vì có thể gây lỗi về sau. Tên và đường dẫn thư mục tốt nhất là không chứa khoảng trắng và không chứa dấu tiếng Việt. 13 3. Nhấn vào biểu tượng và nhập tên file. Lưu ý: Tên file phải có phần mở rộng .py. Không nên dùng tên file python.py hoặc code.py để tránh bị lỗi khi chạy. Tên file tốt nhất là không chứa khoảng trắng và không chứa dấu tiếng Việt. 4. Sau khi tạo file xong, VS Code có thể hỏi bạn có muốn cài đặt extension cho Python không. Hãy chọn Install. 5. Nhập nội dung sau đây vào file vừa tạo: #%% print("Xin chao!") 6. Nhấn Shift-Enter để chạy. Lưu ý: Lúc này VS Code có thể hỏi bạn muốn cài đặt các extension hỗ trợ không, hãy chọn Install. 7. Nếu kết quả hiện ra như hình sau tức là bạn đã hoàn thành cài đặt và chạy thành công đoạn lệnh Python đầu tiên. 8. Chọn File > Auto Save để VS Code tự động lưu code. Lưu ý: Nếu không dùng Auto Save thì phải lưu (Save) file code thủ công bằng cách nhấn Ctrl-S mỗi khi muốn lưu. 14 Cách 2. Lập trình Python trên máy tính sử dụng trình duyệt web Nếu bạn sử dụng máy tính công cộng hoặc một máy tính không thể cài đặt Python thì bạn có thể sử dụng trình duyệt web có kết nối internet và truy cập vào địa chỉ sau để lập trình Python: https://www.onlinegdb.com/online_python_compiler. Lưu ý: Cách này không đảm bảo hỗ trợ đầy đủ chức năng của Python. Chỉ nên dùng khi không thể cài đặt Python và VS Code như cách 1. 15 Hình 1-4. Giao diện trang OnlineGDB. Cách 3. Lập trình Python sử dụng smartphone Trong trường hợp không có máy tính, bạn vẫn có thể lập trình Python bằng cách sử dụng smartphone và cài đặt ứng dụng Pydroid 3 (hoặc một ứng dụng tương tự). Lưu ý: cách này không được khuyến khích sử dụng vì không hỗ trợ đủ tính năng của Python. Chỉ nên sử dụng tạm thời cách này trong một thời gian ngắn khi không thể dùng máy tính. Hình 1-5 Ứng dụng Pydroid 3 trên Google Play. 1.4 Một số lời khuyên hữu ích cho những người mới Lập trình là một kỹ năng. Vì vậy nếu muốn lập trình tốt không có cách nào khác ngoài thực hành. Tự mình thực hành càng nhiều càng tốt! Một số kinh nghiệm khi luyện lập trình: 16 ▪ Cảm thấy không hiểu rõ khi lần đầu học về một khái niệm, kỹ năng lập trình. Điều này là hoàn toàn bình thường! Bạn chỉ cần bỏ thêm chút thời gian xem lại một vài lần, rồi tự mình ngồi code lại nội dung được học thì sẽ dần dần hiểu rõ. Thậm chí đôi khi bạn đã rất cố gắng mà vẫn cảm thấy không hiểu rõ hết. Không sao cả! Có một hiện tượng là: dường như não của chúng ta tự động tổng hợp kiến thức mà nó từng biết qua. Sau một học kỳ, hoặc vài tháng, thậm chí một năm sau, bạn sẽ ngạc nhiên khi gặp lại kiến thức lúc trước bạn thấy bế tắc: lúc này bạn hiểu nó rất rõ ràng! Vì vậy, đừng ngại khi học qua một lần mà chưa hiểu rõ. ▪ ▪ ▪ Keep learning! Những lần đầu lập trình đôi khi giống học thuộc lòng, sao chép code. Tức là bạn xem code mẫu rồi gõ lại giống như vậy. Điều này cũng hoàn toàn bình thường! Học thuộc luôn luôn là một phần của việc thu nạp kiến thức. Đừng ngại ngồi gõ lại code mẫu. Tự mình gõ lại code khác xa với việc ngồi nhìn code và nghĩ rằng mình hiểu. Gõ lại code giúp bạn trải nghiệm lập trình. Khi bạn gõ và chạy code, bạn sẽ tự nhiên ghi nhớ, tư duy và phát hiện các lỗi, các vấn đề trong đoạn lệnh. Bạn sẽ hiểu nó cặn kẽ và từ từ sẽ tự viết được các đoạn code theo ý mình. Internet có thể rất hữu ích. Nếu gặp những lỗi khó hiểu khi lập trình, hoặc nghĩ mãi chưa ra cách lập trình cho một vấn đề, bạn có thể thử google. Nhiều khả năng bạn sẽ tìm được lời giải từ các diễn đàn, các bài viết về lập trình. Tuy nhiên, đừng lạm dụng! Lúc nào cũng tra google trước khi tự mình tìm cách giải quyết, hoặc chỉ copy code mà không hiểu, chắc chắn sẽ có hại cho bạn! Ngoài ra, bạn cũng nên cẩn thận với vấn đề đạo văn (plagiarism). Nên tìm hiểu về quy định bản quyền của đoạn code mà bạn định sử dụng và nhớ phải ghi nguồn. ▪ Thực hành càng nhiều càng tốt! 17 Chương 2 PHÉP TOÁN CƠ BẢN, BIẾN VÀ NHẬP XUẤT TRONG PYTHON Chương này giúp bạn làm quen với các phép toán cơ bản, khái niệm biến (variables) trong lập trình và các hàm cơ bản để nhập xuất dữ liệu trong Python. 2.1 Sử dụng VS Code như một máy tính cầm tay Phần này demo một số cách thực thi lệnh Python với VS Code. Bạn có thể tận dụng những cách này để biến VS Code với Python thành một công cụ tính toán như máy tính cầm tay (calculator). Lưu ý: Trước khi có thể chạy code Python trong VS Code, bạn cần Cài đặt Python (xem mục 1.3 Cài đặt Python). Sau đây giới thiệu ba cách thực thi lệnh Python trong VS Code. Cách 1. Tạo và thực thi cell Để tạo một cell, bạn dùng cú pháp #%% Tên gợi nhớ cho cell như ví dụ sau: Để chạy cell, bạn đặt con nháy vào 1 dòng bất kỳ trong cell (nằm giữa 2 đường kẻ màu xanh như trong hình trên), và nhấn Run Cell (nằm ở ngay phía trên dòng #%%) hoặc sử dụng tổ hợp phím tắt Shift-Enter. Khi bạn chạy cell lần đầu tiên, VS Code sẽ load Python interpreter rồi mới thực thi code nên mất thời gian một chút. Từ lần chạy thứ 2, code sẽ được thực thi ngay vì Python interpreter đã được load vào bộ nhớ rồi. Cách 2. Thực thi code trong cửa sổ interactive Để sử dụng cách này trước tiên bạn phải chạy ít nhất một cell theo cách 1 để VS Code load Python interpreter và khởi tạo cửa sổ interactive (như hình dưới). 18 Trong cửa sổ interactive, bạn có thể nhập code vào dòng lệnh (nằm dưới cùng cửa sổ interactive, tại vị trí có ghi dòng chữ Type ‘python’ code here and press Shift+Enter to run (như hình dưới). Khi nhập xong, nhấn Shift+Enter để thực thi. 2.2 Các phép toán Python hỗ trợ các phép toán cơ bản như mô tả trong bảng sau. Phép toán Cú pháp Code mẫu Kết quả Trị tuyệt đối abs() abs(-2) 2 Cộng + 2+5 7 Trừ - 3-1 2 Nhân * 3*2 6 19 Chia / 5/2 2.5 Chia lấy dư % 7%2 1 Lũy thừa ** 3**2 9 Lũy thừa 10 e 4e3 4*103 = 4000 Số phức j 2 + 5j 2 + 5i Nếu bạn muốn tính những hàm toán học khác như khai căn, lượng giác, thì cần sử dụng package math. Cú pháp như sau: import math math.cos(3.14) Một số hàm toán học được liệt kê trong bảng bên dưới1: Phép toán Cú pháp Code mẫu Kết quả Số π math.pi math.pi 3.1415… Số e math.e math.e 2.7182 Vô cùng math.inf math.inf inf Cosine math.cos() math.cos(3.14) -0.9999 Sine math.sin() math.sin(2*3.14) -0.0031 Arc cosine math.acos() math.acos(1) 0 Arc sine math.asin() math.asin(-1) -1.5707 Tangent math.tan() math.tan(5) -3.3805 Trị tuyệt đối math.fabs() math.fabs(-5.6) 5.6 Ước chung lớn nhất math.gcd() math.gcd(20, 90) 10 Hàm mũ math.exp() math.exp(5) 148.4131 Hàm log math.log() math.log(8) 2.0794 math.log(8, 2) 3 Căn bậc 2 math.sqrt() math.sqrt(9) 3 Ghi chú: Khi cần tra cứu thông tin của một hàm, bạn có thể dùng cú pháp với dấu chấm hỏi, ví dụ: lệnh math.log? sẽ hiện thông tin về hàm log. 1 Xem bảng liệt kê đầy đủ tại https://docs.python.org/3/library/math.html 20 2.3 Biến Biến (variables) là một trong những khái niệm căn bản nhất trong lập trình. Một biến có thể xem như một nơi chứa dữ liệu đơn giản, phục vụ cho các tác vụ được lập trình. Ví dụ, khi viết chương trình vẽ đồ thị của một hàm số bậc 2 có dạng y = ax2 + bx + c, ta có thể tạo ra các biến để lưu giá trị của các hệ số a, b và c. Để tạo ra một biến, bạn cần tuân theo các quy định cú pháp (syntax) tạo biến. Trong Python các quy định về biến như sau: 1. Để tạo biến trong Python, bạn chỉ cần ghi tên biến và gán giá trị cho nó, ví dụ các dòng sau lệnh (chú ý mỗi dòng lệnh phải nằm trên một hàng riêng, dùng phím Enter để xuống dòng): so1 = 4 so2 = 15 đã tạo ra 2 biến có tên so1, so2 với các giá trị là 4, 15 tương ứng. Ghi chú: [Dành cho bạn nào đã biết ngôn ngữ lập trình như C, C++] Python không yêu cầu khai báo biến hoặc khai báo kiểu dữ liệu cho biến. Khi bạn gán giá trị cho biến, Python sẽ tự động xác định kiểu dữ liệu cho biến đó. 2. Tên biến phải bắt đầu bằng chữ cái hoặc dấu _ (dấu dash tạo ra bằng cách nhấn tổ hợp phím Shift -) Ví dụ: các biến so1, _so1 và _1so là hợp lệ, nhưng biến 1so là không hợp lệ vì bắt đầu bằng chữ số 1. 3. Tên biến không được chứa khoảng trắng hoặc ký tự đặc biệt (ký tự đặc biệt là các ký tự không phải chữ cái và chữ số (ngoại trừ dấu dash _), ví dụ @, &, ! là một số ký tự đặc biệt). 21 Ví dụ: các biến phuongtrinh1 và phuong_trinh_1 là hợp lệ, nhưng biến phuong trinh1, phuongtrinh-1, phuongtrinh#1 là không hợp lệ. 4. Tên biến phải khác keywords của Python. Trong Python, cũng như các ngôn ngữ lập trình khác, một số từ đã được dành riêng cho các chức năng của ngôn ngữ, ví dụ if, for, while. Vì vậy bạn không được dùng nhũng từ này đặt tên biến. Ví dụ: các biến có tên if, for, break, import là không hợp lệ vì chúng là các keywords, nhưng các biến if1, for_, break_A một thì hợp lệ vì có chứa thêm các ký tự khác nên không còn là keywords. Ghi chú: để nhận biến một từ có phải keyword không, bạn quan sát màu sắc của nó trong VS Code (VS Code tự chuyển từ thành màu xanh da trời nếu là keyword). Xem danh sách các keywords trong phần Phụ lục. 5. Tên biến có phân biệt chữ hoa và chữ thường. Ví dụ: biến so1 và So1 là 2 biến khác nhau. Ghi chú: mặc dù bạn có thể tạo ra 2 biến so1 và So1 trong một chương trình, tuy nhiên, một kinh nghiệm viết code tốt là bạn nên tránh tạo ra những biến quá giống nhau vì chúng dễ gây nhầm lẫn và dễ dẫn tới lỗi khi lập trình. Ngoài các quy định cú pháp bắt buộc ở trên, khi lập trình bạn cũng nên tuân theo các kinh nghiệm đặt tên biến sau đây sẽ giúp code của bạn được chuyên nghiệp hơn, khó bị mắc lỗi hơn. 1. Tránh đặt tên biến trùng với tên hàm cài sẵn (built-in functions). Hàm cài sẵn là các hàm được cung cấp mặc định trong Python hoặc các packages (xem mục 4.2), ví dụ hàm print, input. Việc đặt tên biến trùng với tên hàm cài sẵn sẽ làm chương trình có thể gặp lỗi khi bạn gọi các hàm này sau khi đã tạo biến. Bạn có thể chạy thử dòng code sau: print("Xin chao") Sau đó, chạy đoạn code sau: print = 5 print("Xin chao") 22 Để ý rằng lần chạy dòng lệnh print thì hàm sẽ hoạt động bình thường. Nhưng khi chạy đoạn code có dòng print = 5 (tức là ta tạo ra biến tên print và gán giá trị 5 cho nó) thì hàm sẽ báo lỗi. Để xử lý lỗi này, bạn chạy lệnh del print để xóa biến print trong bộ nhớ, khi đó hàm print sẽ hoạt động trở lại. Ghi chú: Trong VS Code, để kiểm tra xem một từ có trùng tên hàm cài sẵn không, bạn nhấn tổ hợp phím Ctrl-Space (thanh cách). Nếu là hàm cài sẵn thì sẽ xuất hiện bảng thông tin như hình dưới: 2. Đặt tên biến có ý nghĩa. Kinh nghiệm đơn giản này đã được chứng minh trong thực tế có thể giúp cải thiện đáng kể code của bạn: dễ đọc, dễ bảo trì, sửa lỗi và dễ phát triển hơn. Điều này đặc biệt cần thiết khi bạn viết những chương trình phức tạp và code được viết bởi một nhóm nhiều người. Đừng ngại các tên dài, chỉ ngại các tên khó hiểu! Ví dụ: Thay vì đặt tên biến là a, N, f, g, bạn hãy đặt: he_so_a, so_thiet_bi, ham_loc_du_lieu, ham_phan_tich sẽ giúp chương trình của bạn dễ đọc hơn rất nhiều. Ghi chú: Để liệt kê các tên bạn đã tạo, dùng lệnh whos. Để xóa một biến, dùng lệnh del ten_bien, ví dụ del he_so_a. 2.4 Nhập xuất cơ bản Dữ liệu đưa vào chương trình có thể thông qua việc gán trực tiếp vào biến (như mục trên), hoặc nhập từ bàn phím, từ các thiết bị chuyên dụng (cảm biến, camera), hoặc từ file dữ liệu. Bên dưới giới thiệu cách nhập dữ liệu cơ bản từ bàn phím. Để nhập dữ liệu từ bàn phím bạn có thể dùng hàm input(), như các ví dụ sau: #%% Ví dụ 1: nhập chữ ho_ten = input('Xin nhap ho ten:') 23 #%% Ví dụ 2: nhập số nguyên so_thiet_bi = int(input('Nhap so thiet bi:')) #%% Ví dụ 3: nhập số thực diem_trung_binh = int(input('Nhap diem trung binh:')) Trong ví dụ 1, khi chạy dòng lệnh nhập sẽ xuất hiện ô đợi người dùng nhập tên như sau (trong VS Code ô này xuất hiện ở trên cùng của cửa sổ VS Code): Sau khi người dùng nhập tên và nhấn Enter thì tên được nhập sẽ được lưu vào biến ho_ten. Trong ví dụ 2, để ý trong dòng lệnh nhập có hàm int(), hàm này có công dụng kiểm tra và chuyển đổi dữ liệu được nhập thành kiểu số nguyên. Khi chạy ví dụ này bạn cũng sẽ thấy xuất hiện ô nhập tương tự như trên, nhưng lưu ý rằng, bạn cần nhập số nguyên, nếu không chương trình sẽ báo lỗi. Trong ví dụ 3, đoạn lệnh nhập tương tự ví dụ 2, chỉ khác ở hàm float() thay cho hàm int(). Hàm float() đổi dữ liệu được nhập về kiểu số thực. Lưu ý: Khi không dùng hàm chuyển đổi kiểu dữ liệu (type casting) như hàm int(), float(), thì dữ liệu trả về từ hàm input() sẽ có kiểu chữ (string) và không thể thực hiện các phép toán. Tương tự như việc nhập dữ liệu, việc xuất dữ liệu cũng có nhiều cách khác nhau như gán trực tiếp vào biến (để sử dụng tiếp tục trong chương trình), hoặc in chữ, vẽ hình lên màn hình, hoặc truyền tới các thiết bị xuất (loa, máy in, thiết bị điều khiển), hoặc lưu xuống file. Bên dưới giới thiệu cách in dữ liệu đơn giản ra màn hình. Để in text lên màn hình, bạn có thể dụng hàm print() của Python như các ví dụ bên dưới: #%% Ví dụ 1: in trực tiếp văn bản print("Xin chao") 24 #%% Ví dụ 2: in nội dung 1 biến ho_ten = "Tran An" print(ho_ten) #%% Ví dụ 3: in nội dung 1 biến kèm thông báo print("Ho ten cua ban:", ho_ten) #%% Ví dụ 4: in nội dung nhiều biến x1 = 15.3456 x2 = 26.1234 print("Nghiem 1:", x1, " Nghiem 2:", x2) #%% Ví dụ 5: định dạng biến kiểu số thực print("Nghiem 1: %.2f Nghiem 2: %.2f" % (x1,x2)) # chỉ lấy 2 chữ số thập phân Khi thực thi ví dụ 1, dòng chữ Xin chao! sẽ được in ra màn hình. Khi thực thi ví dụ 2, dòng chữ Tran An (nội dung của biến ho_ten) sẽ được in ra màn hình. Khi thực thi ví dụ 3, dòng chữ Ho ten cua ban: Tran An sẽ được in ra màn hình. Khi thực thi ví dụ 4, dòng chữ Nghiem 1: 15.3456 Nghiem 2: 26.1234 sẽ được in ra màn hình. Khi thực thi ví dụ 5, dòng chữ Nghiem 1: 15.35 Nghiem 2: 26.12 sẽ được in ra màn hình (để ý giá trị của x1, x2 chỉ in ra 2 chữ số thập phân). Chúng ta xem chi tiết dòng lệnh trong ví dụ này: print("Nghiem 1: %.2f Nghiem 2: %.2f" % (x1,x2)) Đoạn code %.2f có nghĩa là sẽ in ra số thực với 2 số thập phân. Để in 3 số thập phân bạn sẽ code thành: %.3f (con số nằm trước chữ f chính là số lượng chữ số thập phân sẽ in ra). 25 Các biến cần in được đặt trong đoạn code % ( ). Ví dụ: % (x1,x2) sẽ in ra giá trị của các biến x1, x2. Lưu ý các biến được in ra theo đúng thứ tự bạn liệt kê trong đoạn code này. Lưu ý: Trong ví dụ 5, dấu # trong đánh dấu đoạn ghi chú (comments). Mọi nội dung đặt sau dấu # sẽ được trình dịch Python bỏ qua, không thực thi. Thông thường trong lập trình, comments được sử dụng để ghi chú cho những đoạn lệnh quan trọng, hoặc để giải thích cho công dụng các hàm hoặc các đoạn lệnh phức tạp. Khi viết comments nên viết rõ ràng nhưng ngắn gọn. Tránh ghi quá nhiều comments cho những đoạn code đơn giản, vì có thể làm code rườm rà, khó đọc hơn. 2.5 Lệnh if Lệnh if là cấu trúc rẽ nhánh trong Python. Nói một cách đơn giản, cấu trúc rẽ nhánh là lựa chọn chạy lệnh này hoặc lệnh khác. Ví dụ khi giải phương trình bậc 2, nếu giá trị biệt số delta ≥ 0 thì ta sẽ chạy lệnh tính nghiệm, còn nếu giá trị biệt số delta < 0 thì ta chạy lệnh in ra thông báo phương trình vô nghiệm. Lệnh if chính là sự diễn đạt “nếu” trong lập trình. Để sử dụng được lệnh if, trước tiên ta cần biết quy tắc cú pháp của nó. Trong Python, lệnh if được quy định cú pháp như sau: if dieu_kien_1: khoi_lenh_1 elif dieu_kien_2: khoi_lenh_2: elif dieu_kien_3: khoi_lenh_3: else: khoi_lenh_N khoi_lenh_sau_if Luồng hoạt động của khối lệnh if trên được mô tả trong lưu đồ ở Hình 2-1. Một cách cụ thể, trong cú pháp if trên, ▪ if, elif, else là các từ khóa (bắt buộc viết chính xác như vậy). Lưu ý: Chỉ khối if là bắt buộc có, còn khối elif và else có thể xuất hiện tùy ý. Cụ thể, khối elif có thể không xuất hiện hoặc xuất hiện 26 1 lần hoặc nhiều lần, còn khối else có thể không xuất hiện hoặc xuất hiện 1 lần. Ví dụ, chúng ta có thể viết lệnh if như sau: if dieu_kien_1: khoi_lenh_1 khoi_lenh_sau_if hoặc viết như sau: if dieu_kien_1: khoi_lenh_1 else: khoi_lenh_N khoi_lenh_sau_if hoặc viết như sau: if dieu_kien_1: khoi_lenh_1 elif dieu_kien_2: khoi_lenh_2: khoi_lenh_sau_if ▪ dieu_kien_1, dieu_kien_2, dieu_kien_3, dieu_kien_N là các mệnh đề logic: có giá trị True hoặc False (đúng hoặc sai) khi chạy code. Ví dụ: 1>2 là một mệnh đề sai (giá trị False), 5<6 là mệnh đề đúng (True), N<10: mệnh đề này đúng hoặc sai tùy thuộc vào giá trị của biến N khi chạy code, ví dụ nếu N = 9 thì mệnh đề đúng (9<10). Bảng sau đây liệt kê các toán tử so sánh và toán tử kết hợp có thể dùng trong khối điều kiện. Toán tử Cú pháp Code mẫu Kết quả So sánh lớn hơn > 5>2 True So sánh nhỏ hơn < 5<2 False So sánh bằng == 5==2 False So sánh khác != 5!=2 True 27 So sánh lớn hơn >= hoặc bằng 5>=2 True So sánh nhỏ hơn <= hoặc bằng 5<=5 True Và and (5<2) and (2<4) False Hoặc or (5<2) or (2<4) True Phủ định not not (5>=2) False Hình 2-1 Lưu đồ của khối lệnh if. Khi được viết đúng cú pháp, lệnh if sẽ hoạt động theo nguyên tắc: điều kiện nào đúng thì chỉ chạy duy nhất khối lệnh ngay dưới điều kiện đó (các khối lệnh khác sẽ bị bỏ qua). Các điều kiện sẽ được kiểm tra 28 lần lượt từ trên xuống: dieu_kien_1 được kiểm tra trước, sau đó đến dieu_kien_2, rồi dieu_kien_3… Ví dụ: ▪ Nếu dieu_kien_1 đúng (giá trị True) thì khối lệnh khoi_lenh_1 sẽ được chạy. Sau khi chạy xong khoi_lenh_1 thì chương trình sẽ nhảy đến khoi_lenh_sau_if, tức là toàn bộ các dòng code còn lại trong lệnh if sẽ bị bỏ qua. ▪ Nếu dieu_kien_1 sai (giá trị False) thì khối lệnh khoi_lenh_1 sẽ bị bỏ qua. Lúc này trình biên dịch Python sẽ kiểm tra dieu_kien_2. Nếu dieu_kien_2 đúng thì sẽ chạy khoi_lenh_2. Sau khi chạy xong khoi_lenh_2 thì chạy đến khoi_lenh_sau_if. Nếu dieu_kien_2 sai thì kiểm tra dieu_kien_3 và thực hiện tương tự như trên. ▪ Khi tất cả các điều kiện đều sai, nếu có khối else thì lệnh trong khối else sẽ được chạy (tức lệnh khoi_lenh_N sẽ chạy). Nếu không có khối else thì sẽ chạy khoi_lenh_sau_if. Lưu ý: Một khối lệnh có thể bao gồm nhiều dòng lệnh. Mỗi dòng lệnh Python được đặt trên 1 dòng. Nghĩa là bạn phải xuống dòng (enter) khi viết xong 1 câu lệnh. Các câu lệnh trong một khối lệnh phải có thụt đầu dòng (indentation) bằng nhau. Tức là nếu dòng lệnh 1 được thụt đầu dòng bằng 3 khoảng trắng (space) thì dòng lệnh 2 (trong khối đó) cũng phải như vậy. Nếu bạn thụt đầu dòng 4 khoảng trắng, hoặc 2 khoảng trắng khi viết dòng lệnh 2 thì sẽ bị lỗi. Đây là quy định cú pháp của Python nhằm đảm bảo chương trình ngăn nắp, dễ đọc. ▪ ▪ ▪ Ví dụ 2.1: Nhập 1 số nguyên và xác định số được nhập là chẵn hay lẻ. #%% Xác định chẵn lẻ N = int(input('Xin nhap 1 so nguyen:')) if N % 2 == 0: print(N, 'la so chan.') else: print(N, 'la so le.') 29 Trong đoạn lệnh trên, phép toán % là phép chia lấy dư (xem mục 2.2). Nếu số được nhập chia lấy dư cho 2 bằng 0 (tức là nó chia hết cho 2) thì đây là số chẵn. Thực hành: Bạn hãy gõ lại đoạn code trên và thực thi (Shift-Enter) rồi nhập vào các số nguyên khác nhau để xem kết quả được in ra. Ví dụ 2.2: Nhập 2 số thực và xác định xem chúng cùng dấu hay trái dấu. #%% Xác định cùng dấu, trái dấu so1 = float(input('Xin nhap so thu nhat:')) so2 = float(input('Xin nhap so thu hai:')) if so1*so2 < 0: print('Hai so duoc nhap trai dau.') elif so1*so2 > 0: print('Hai so duoc nhap trai dau.') else: print('Co mot so bang khong.') Thực hành: Bạn hãy gõ lại đoạn code trên và thực thi (Shift-Enter) rồi nhập vào các cặp số thực khác nhau để xem kết quả được in ra. Lý giải hoạt động của chương trình dựa vào các điều kiện so1*so2 < 0 và so1*so2 > 0. Ví dụ 2.3: Nhập 3 số thực và tìm số lớn nhất trong 3 số này. #%% Tìm số lớn nhất trong 3 số so1 = float(input('Nhap so thu nhat:')) so2 = float(input('Nhap so thu hai:')) so3 = float(input('Nhap so thu ba:')) so_lon_nhat = so1 if so_lon_nhat < so2: so_lon_nhat = so2 if so_lon_nhat < so3: 30 so_lon_nhat = so3 print('So lon nhat:', so_lon_nhat) Thực hành: Bạn hãy gõ lại đoạn code trên và thực thi (Shift-Enter) rồi nhập vào các bộ số thực khác nhau để xem kết quả được in ra. Lý giải hoạt động của chương trình (để ý rằng đoạn code trên sử dụng 2 khối if liên tiếp nhau). Thực hành: Nhập đoạn mã sau và đặt tên file là if_else_demo.py. Sau đó hãy thực thi đoạn mã và cho biết kết quả in ra màn hình. # if-else a = 10 b = 30 print('demo if-elif-else') if (a > 10) or (b > 10): # do something print('(a > 10) or (b > 10)') elif (a != 5) and (b <= 7): # do something print('(a != 5) and (b <= 7)') else: # do something print('else') # nested if if (a == 0) or (b > 20): if b < 50: print('nested-if') else: print('else-nested-if') else: print('if-else') 31 Bài tập có lời giải1 1. Viết chương trình đọc một ký tự từ bàn phím. Nếu ký tự được nhập là nguyên âm gồm các điểm chữ thành điểm số như cách tính điểm ở một số trường đại học trên thế giới. Bảng ánh xạ có thể được thể hiện như sau Chương trình sẽ đọc ký tự từ người sử dụng. Sau đó, chương trình sẽ tính toán và hiển thị con số điểm tương ứng. Ngoài ra, chương trình có thể đưa ra thông báo “yêu cầu nhập lại” trong trường hợp người dùng nhập vào một ký tự không tồn tại trong bảng trên. 2. Viết chương trình đánh giá hiệu quả làm việc của nhân viên. Một công ty muốn đánh giá hiệu quả làm việc của nhân viên công ty theo mỗi năm. Thang đo hiệu quả làm việc sẽ được đánh giá từ 0.0 và giá trị càng cao thì thể hiện năng lực làm việc càng tốt của nhân viên. Giá trị đánh giá n có thể là 0.0, 0.4 hoặc 0.6 hoặc lớn hơn. Trong đó, các giá trị giữa 0.0 và 0.4 hoặc 0.4 và 0.6 thì không được sử dụng. Chi tiết được thể hiện trong bảng sau. Giá trị thưởng cho Các bài tập có lời giải trong giáo trình này đa số được trích từ sách Stephenson, B. (2019). The Python Workbook 2nd. Springer. Lời giải được trình bày trong phần Phụ lục. 1 32 mỗi nhân viên sau khi đánh giá sẽ tương ứng với công thức $2400*n. Hãy viết chương trình đọc vào giá trị n từ người dùng và hãy chỉ ra rằng hiệu quả làm việc tương ứng của nhân viên có giá trị n cho hiệu quả công việc là unacceptable, acceptable và meritorious. Nếu giá trị n không thể hiện đúng như trong bảng thì đưa ra thông báo “vui lòng nhập lại”. 3. Viết chương trình kiểm tra năm nhuận. Thông thường mỗi năm có khoảng 365 ngày. Tuy nhiên, thời gian này phụ thuộc vào thời gian Trái đất hoàn thành một vòng xoay quanh Mặt trời. Điều này dẫn đến có thể có thêm 1 ngày là ngày 29 tháng 2. Những năm có thêm ngày này được xem là năm nhuận. Cách tính năm nhuận sẽ được quyết định bởi một số điều sau: • Số năm chia hết cho 400 là năm nhuận • Các năm còn lại: ▪ nếu chia hết cho 100 thì không là năm nhuận ▪ nếu chia hết cho 4 thì là năm nhuận • Các trường hợp còn lại thì không là năm nhuận Hãy viết một chương trình đọc vào số năm từ người dùng và hiển thị thông báo chỉ ra rằng năm vừa nhập có phải là năm nhuận không. Bài tập thực hành 1. Viết code tính diện tích tam giác có cạnh đáy dài 10 cm và cao 6 cm. 2. Viết code cho người dùng nhập vào đường kính của 1 hình tròn tùy ý và tính diện tích hình tròn đó. 3. Viết code kiểm tra biểu thức sau đúng không (True or False). 𝜋 4𝜋 Kiểm tra với x = 𝜋, 2 , 3 . sin2 (𝑥) + cos2 (𝑥) = 1 33 4. Viết code cho nhập 3 số thực a, b, c (sử dụng hàm input()), và giải phương trình bậc hai ax2 + bx + c = 0. In nghiệm tìm được bằng hàm print(). 5. Viết chương trình cho người dùng nhập 3 số thực rồi tìm in ra các số có trị tuyệt đối nhỏ hơn 10. 6. Viết chương trình cho người dùng nhập 5 số thực rồi tìm max/min của chúng. 7. Viết chương trình xác định thứ trong tuần của ngày 1 tháng 1. Thứ trong tuần của ngày 1 tháng 1 đối với một năm được tính theo công thức sau: Kết quả của công thức trên là một số nguyên thể hiện thứ trong tuần. Việc mã hóa thứ sẽ bắt đầu với ngày Chủ nhật với số mã hóa là 0. Tương tự, ngày thứ Bảy sẽ có giá trị mã hóa là 6. Hãy sử dụng công thức trên và viết một chương trình đọc số năm từ người sử dụng. Chương trình sẽ tính toán và in ra thứ tương ứng với đối ngày 1 tháng 1 của năm mà người dùng nhập. 34 Chương 3 VÒNG LẶP VÀ CẤU TRÚC DỮ LIỆU MẢNG Trong chương trước bạn đã được giới thiệu về lệnh if giúp điều khiển chương trình chạy rẽ nhánh vào các đoạn lệnh khác nhau theo điều kiện. Trong chương này, bạn sẽ được tìm hiểu về vòng lặp – cấu trúc tự động thực thi nhiều lần các đoạn lệnh theo kịch bản bạn muốn. Có hai dạng vòng lặp được hỗ trợ trong Python là vòng lặp while và vòng lặp for. 3.1 Vòng lặp while Cú pháp của vòng lặp while như sau: while dieu_kien: khoi_lenh_lap khoi_lenh_sau_while Luồng hoạt động của vòng lặp while được mô tả trong lưu đồ ở Hình 3-1. Hình 3-1 Lưu đồ hoạt động của vòng lặp while. 35 Nhìn qua khối lệnh while có cú pháp giống với khối if tối giản. Hoạt động của khối while như sau: khi dieu_kien còn đúng (True) thì còn thực thi khoi_lenh_lap (tức là khoi_lenh_lap sẽ được chạy nhiều lần), khi dieu_kien sai (False) thì sẽ chạy khoi_lenh_sau_while. Để dễ hiểu hơn, chúng ta xem ví dụ sau. Ví dụ 3.1: Viết code yêu cầu nhập số N dương. Nếu nhập sai thì cho nhập lại đến khi đúng. #%% Nhập số dương N = -1 while N<=0: N = int(input('Xin nhap so nguyen duong:')) print('So duoc nhap N =', N) Lưu ý: Để vòng lặp while hoạt động được cần thỏa mãn hai điểm sau: 1. Điều kiện dieu_kien phải được khởi tạo trước. Tức là mọi biến trong dieu_kien phải được gán giá trị sẵn. Thông thường ta sẽ khởi tạo biến sao cho dieu_kien có giá trị True ở lần chạy đầu tiên. 2. Khối lệnh khoi_lenh_lap phải có ít nhất 1 dòng lệnh làm thay đổi điều kiện dieu_kien. Nếu không vòng lặp sẽ chạy không bao giờ dừng (do dieu_kien không bị thay đổi, nó sẽ đúng (True) mãi mãi nên vòng lặp sẽ lặp mãi mãi). Trong ví dụ ở trên, lệnh N = -1 đã khởi tạo giá trị cho biến N. Nếu không có lệnh này thì điều kiện N<=0 chưa được khởi tạo, sẽ báo lỗi khi chạy. Ngoài ra, để ý rằng lệnh N = int(input(…)) là câu lệnh có thể làm thay đổi điều kiện bởi vì nó gán giá trị cho biến N. Khi giá trị của N thay đổi thì điều kiện N<=0 có thể thay đổi (từ True thành False). Thực hành: Bạn hãy gõ lại đoạn code trên và thực thi (Shift-Enter) rồi nhập vào các số nguyên âm, sau đó nhập 1 số nguyên dương và xem kết quả được in ra. 36 Ví dụ 3.2: Viết code yêu cầu nhập số N dương và tính N! #%% Tính giai thừa N = -1 while N<=0: N = int(input('Xin nhap so nguyen duong:')) print('So duoc nhap N =', N) giai_thua = 1 i = 1 while i<=N: giai_thua *= i # = giai_thua*i i += 1 print('N! =', giai_thua) Nhắc lại lưu ý viết lệnh trong Python: ▪ ▪ Mỗi dòng lệnh Python được đặt trên 1 dòng. Nghĩa là bạn phải xuống dòng (enter) khi viết xong 1 câu lệnh. Các câu lệnh trong một khối lệnh phải có thụt đầu dòng (indentation) bằng nhau. Tức là nếu dòng lệnh 1 được thụt đầu dòng bằng 3 khoảng trắng (space) thì dòng lệnh 2 (trong khối đó) cũng phải như vậy. Nếu bạn thụt đầu dòng 4 khoảng trắng, hoặc 2 khoảng trắng khi viết dòng lệnh 2 thì sẽ bị lỗi. Đây là quy định cú pháp của Python nhằm đảm bảo chương trình ngăn nắp, dễ đọc. Trong ví dụ trên, các lệnh N = int(input(…)) và i = 1 giúp khởi tạo điều kiện i<=N. Lệnh i += 1 giúp thay đổi điều kiện. Dòng lệnh giai_thua *= i tương đương với lệnh giai_thua = giai_thua*i. Đây là một cách viết tắt phép tính và gán trong Python, ví dụ: a += 2, tương đương a = a + 2 b /= 3, tương đương b = b / 3 Thực hành: Bạn hãy gõ lại đoạn code trên và thực thi (Shift-Enter) rồi nhập vào các số nguyên dương N khác nhau và xem kết quả được in ra. 37 Ví dụ 3.3: Viết code yêu cầu nhập số N dương và tính tổng 1+2+…+N. #%% Tính tổng 1+2+…+N N = -1 while N<=0: N = int(input('Xin nhap so nguyen duong:')) print('So duoc nhap N =', N) tong = 0 i = 1 while i<=N: tong += i i += 1 print('1+2+...+N =', tong) Trong code trên, để ý lệnh tong = 0. Ở đây chúng ta khởi tạo giá trị cho tổng là 0 (so với ví dụ 2 ở trên, ta khởi tạo giai thừa bằng 1 vì phép tính nhân dồn). Thực hành: Bạn hãy gõ lại đoạn code trên và thực thi (Shift-Enter) rồi nhập vào các số nguyên dương N khác nhau và xem kết quả được in ra. Ví dụ 3.4: Viết code tìm max trong 3 số thực nhập bởi người dùng. Sau khi chạy xong thì hỏi người dùng có muốn chạy lại không. Nếu người dùng nhấn phím ‘y’ thì chạy lại, nhấn phím khác thì dừng. #%% Tìm max và lặp lại chay_tiep = 'y' while chay_tiep == 'y' or chay_tiep == 'Y': so1 = float(input('Nhap so thu nhat')) so2 = float(input('Nhap so thu hai')) so3 = float(input('Nhap so thu ba')) so_lon_nhat = so1 if so_lon_nhat<so2: so_lon_nhat=so2 if so_lon_nhat<so3: 38 so_lon_nhat=so3 print('So lon nhat: ', so_lon_nhat) chay_tiep = input('Nhan "y" de chay lai, nhan phim khac de thoat:') print('Da hoan thanh.') Trong đoạn code trên, bạn hãy để ý cách biến chay_tiep được khởi tạo cũng như gán lại giá trị chay_tiep = input(…) để đảm bảo điều kiện sẽ có lúc sai (False) và vòng lặp có thể dừng. Thực hành: Bạn hãy gõ lại đoạn code trên và thực thi (Shift-Enter) rồi nhập vào các số thực, sau đó nhập “y” hoặc “Y” để chạy lại. Nếu nhập phím khác “y” và “Y” thì chương trình sẽ dừng. Ví dụ 3.5: Viết code tính ln(1+x) bằng khai triển Taylor1 với |x|<1. Cho công thức khai triển Taylor của ln(1+x) (với |x|<1) như sau: #%% Tính ln(1+x) bằng khai triển Taylor x = float(input('Nhap x (-1<x<1):')) i = 1 tu_so = x mau_so = i so_hang = tu_so/mau_so tong = so_hang while abs(so_hang)>1e-10: i += 1 tu_so *= -x mau_so = i so_hang = tu_so/mau_so tong += so_hang print('ln( 1 +',x,') =',tong) 1 https://en.wikipedia.org/wiki/Taylor_series 39 Trong code trên, đoạn lệnh x = float(input(…) giả định rằng người dùng sẽ nhập x đúng yêu cầu (|x|<1). Để viết code trên, ta cần phân tích biểu thức của khai triển Taylor: Để ý rằng biểu thức tổng này có các số hạng có tính chất lặp: tử số của số hạng sau bằng tử số của số hạng trước đó nhân với -x. Còn mẫu số của các số hạng thì tăng dần từ 1, 2, 3… Các lệnh: i = 1 tu_so = x mau_so = i so_hang = tu_so/mau_so tong = so_hang là các lệnh khởi tạo cho số hạng và tổng. Khi khởi tạo xong, ta có thể tính số hạng sau bằng theo phân tích lặp ở trên. Ngoài ra, bởi vì khai triển Taylor của ln(1+x) là một dãy hội tụ (khi |x|<1), hay nói cách khác, các số hạng trong dãy càng về sau càng có giá trị tuyệt đối nhỏ (tiến về 0). Vì vậy ta có thể thiết lập điều kiện dừng của vòng lặp khi giá trị của số hạng đã trở nên rất nhỏ, ví dụ 10-10 như trong điều kiện trên code: abs(so_hang)>1e-10. Thực hành: Viết thêm đoạn code bắt buộc người dùng nhập x đúng yêu cầu (|x|<1). Bạn cũng có thể kiểm tra độ chính xác của code được viết bằng cách tính giá trị của ln(1+x) và đối chiếu. Ví dụ đoạn code sau có thể dùng để tính giá trị cho ln(1+x): import math print('ln( 1 +',x,') =',math.log(1+x)) Thực hành: Nhập đoạn mã sau và đặt tên là while_demo.py, sau đó hãy thực thi đoạn mã và cho biết kết quả in ra màn hình. 40 print('demo - iteration while') i = 0 while i < 10: print(i) i += 1 3.2 Cấu trúc dữ liệu mảng Trước khi giới thiệu vòng lặp for, chúng ta xem về một dạng cấu trúc dữ liệu cơ bản rất phổ biến trong Python cũng như các ngôn ngữ lập trình khác: mảng. Nói một cách đơn giản, một biến có kiểu mảng thì có thể lưu trữ đồng thời nhiều giá trị. Biến mà bạn được giới thiệu trong chương trước chỉ có thể lưu 1 giá trị. Khi bạn gán giá trị mới thì giá trị cũ sẽ mất. Mảng thì không như vậy, nó có thể chứa một lúc nhiều giá trị. Ví dụ, bạn sẽ cần dùng mảng nếu muốn lưu danh sách tên của các sinh viên trong 1 lớp. Nếu không dùng mảng, bạn sẽ phải tạo nhiều biến bình thường để lưu các tên sinh viên, vì mỗi biến chỉ chứa được 1 tên. Cách làm này vừa bất tiện vừa khó phát triển chương trình, vì khi cần thêm một sinh viên, bạn phải thêm 1 biến nữa trong code. Một ví dụ khác là khi bạn cần lưu giá trị nhiệt độ trả về từ một cảm biến nhiệt mà bạn dùng để theo dõi một thiết bị nào đó (ví dụ bình đun nước, bình giữ nhiệt). Giả sử mỗi phút cảm biến này sẽ trả về 1 nhiệt độ. Như vậy, nếu không dùng mảng thì cứ mỗi phút nhiệt độ cũ sẽ bị mất (bị thay thế bởi nhiệt độ mới). Để lưu lại tất cả các giá trị nhiệt độ sử dụng cho phân tích về sau (ví dụ để vẽ biểu đồ thay đổi nhiệt độ của thiết bị) thì ta cần lưu lại tất cả nhiệt độ này. Mảng sẽ giúp bạn lưu lại tất cả nhiệt độ này. Trong Python, có nhiều cách khác nhau để tạo ra các biến có kiểu mảng. Trong phần này chúng ta sẽ sử dụng list. Để tạo một list, bạn dùng dấu ngoặc vuông để đánh dấu bắt đầu và kết thúc list như các ví dụ sau: #%% Tạo list list1 = [-2, 3, 5.27, -19, 10] list2 = ['Tam', 'Thien', 'Hoa', 'Binh'] list3 = ['Tam', 10, 'Thien', 7.3, 'Hoa', 8, 'Binh', 9.2] 41 Có thể thấy mảng tạo bằng cú pháp list chứa được các phần tử có kiểu dữ liệu khác nhau (số nguyên, số thực, chữ). Các phần tử trong list được ngăn cách bởi dấu phẩy. Phần tử kiểu chữ phải được đặt trong dấu nháy đơn ' hoặc nháy kép ''. Bạn cũng có thể tạo list bằng cách ghép 2 list đã có với nhau: list4 = list1 + list2 Để xem nội dung của list bạn có thể dùng lệnh print(): print(list4) Để truy xuất các phần tử trong list, ta cần biết các phần tử của list đều được đánh số, gọi là index. Có 2 dạng indexing trong list: index không âm và index âm. Ví dụ, xét list1 = [-2, 3, 5.27, -19, 10] Các phần tử trong list này có index như sau: Phần tử -2 3 5.27 -19 10 Index không âm 0 1 2 3 4 Index âm -5 -4 -3 -2 -1 Index không âm là các số nguyên không âm được đánh từ trái sang phải (phần tử đầu tiên của list có index 0). Index âm là các số nguyên âm được đánh từ phải sang trái (phần tử cuối cùng của list có index -1). Để truy xuất phần tử trong list, ta dùng cú pháp sau: ten_list[index] ví dụ, để truy xuất phần tử 5.27 trong list1 ta dùng code sau: list1[2] hoặc list1[-3] Bạn có thể kết hợp với lệnh print để xem giá trị truy xuất được: print(list1[2]) Ngoài ra, Python còn hỗ trợ truy xuất nhiều phần tử một lúc sử dụng cú pháp sau (thường được gọi là cú pháp slicing): ten_list[start_index : stop_index : step] 42 Trong đó start_index và stop_index là index phần tử đầu và index phần tử cuối bạn muốn truy xuất, còn step là bước nhảy của index khi chạy từ start_index đến stop_index. Các giá trị cho start_index, stop_index, và step đều phải là số nguyên. Lưu ý: Truy xuất slicing stop_index không bao gồm phần tử tại stop_index. Do đó, để lấy được phần tử có index i ta cần phải dùng stop index bằng i+1. Ví dụ 3.6: Để lấy 3 phần tử đầu tiên của list1 ta dùng code sau: list1[0:3:1] hoặc dùng index âm: list1[-5:-2:1] Để lấy 3 phần tử cuối cùng của list1: list1[2:5:1] Trường hợp này nếu dùng index âm bạn sẽ không thể thu được kết quả như mong muốn: list1[-3:0:1]. Lý do là index 0 là phần tử đầu tiên của mảng, trong khi với step bằng 1 thì phép slicing sẽ lấy các phần tử với index tăng dần, tức là lấy các phần tử theo chiều từ trái sang phải. Quan sát list1 chúng ta có thể thấy không có cách nào đi từ trái sang phải xuất phát từ phần tử có index -3 mà đến được phần tử có index 0. Đối với trường hợp này, ta cần sử dụng giá trị mặc định của start_index, stop_index, và step. Giá trị mặc định của các thành phần sẽ được trình biên dịch Python sử dụng khi bạn để khuyết các thành phần này trong lệnh slicing (xem ví dụ bên dưới). ▪ Giá trị mặc định của start_index là: 0 ▪ Giá trị mặc định của stop_index sẽ lấy hết các phần tử đến cuối mảng (nếu step có giá trị dương), hoặc lấy hết các phần tử đến đầu mảng (nếu step có giá trị âm). ▪ Giá trị mặc định của step là: 1 Ví dụ, để lấy 3 phần tử cuối cùng của list1 ta có thể dùng slicing với index âm như sau: list1[-3::1] do stop_index bị khuyết nên trình dịch Python sẽ lấy đến hết mảng theo giá trị mặc định (để ý step bằng 1 là số dương). Ngoài ra, ta cũng có thể 43 bỏ step bằng 1 vì đây là giá trị mặc định. Code sau đây cho kết quả giống với lệnh trên: list1[-3:] Tương tự, bạn có thể dùng code sau để lấy 3 phần tử đầu tiên của mảng: list1[:3] code trên bỏ qua start_index và step Bên cạnh việc truy xuất các phần tử của list, Python cung cấp các hàm sau để thao tác với list: ▪ ▪ ▪ Hàm append(): thêm phần tử vào cuối mảng. Ví dụ, để thêm phần tử 48 vào list1 ta dùng code: list1.append(48). Hàm insert(): chèn phần tử vào mảng. Hàm này yêu cầu 2 tham số (arguments) là vị trí chèn (index chèn) và giá trị chèn. Ví dụ, để thêm phần tử 90 vào list1 ở vị trí có index 2 ta dùng code: list1.insert(2, 90). Lệnh del: xóa phần tử hoặc xóa mảng. Ví dụ, để xóa phần tử ở vị trí có index 5 ta dùng code: del list1[5]. Để xóa toàn bộ list1: del list1. Thực hành: Sử dụng các hàm trên với các list của bạn và in ra kết quả sau khi thực thi. Tìm hiểu thêm hàm pop() với công dụng tương tự lệnh del. Lưu ý: Trong Python còn cung cấp một dạng mảng tương tự như list có tên là tuple. List và tuple có công năng và cách sử dụng giống nhau, ngoại trừ việc tuple là một mảng hằng số, có nghĩa là khi tạo ra tuple, bạn không thể thay đổi các phần tử của nó. Để tạo tuple ta dùng cú pháp sử dụng dấu ngoặc đơn (thay cho ngoặc vuông của list): tuple1 = (5, 3.14, -6, 7) Thực hành: Nhập đoạn mã sau và đặt tên là list_demo.py Sau đó hãy thực thi đoạn mã và cho biết kết quả in ra màn hình. # declare lists print('----declare lists') numbers = [] a = [2, 7, 10, 8] cities = ['Berlin', 'Seattle', 'Tokyo', 'Moscow'] 44 b = [10, 3, 'Apple', 6, 'Strawberry'] c = range(1, 10, 2) # print(lists print('----print(lists') print(a) for city in cities: print(city) print(b) print(c) # get length of lists print('----get length of lists') print(len(a)) print(len(cities)) # add item into list print('----add item') numbers.append(10) numbers.append(5) cities.append('London') for i in numbers: print(i) for city in cities: print(city) # get specific item print('----get item') print(cities[2]) print(a[3]) 45 # sorting print(a.sort()) # edit item print('----edit item') cities[2] = 'new city' for city in cities: print(city) # remove item print('----remove item') a.remove(8) # by value del cities[2] # by index for city in cities: print(city) 3.3 Vòng lặp for Tương tự như vòng lặp while, vòng lặp for cũng có công dụng thực thi lặp lại nhiều lần một khối lệnh. Tuy nhiên, trong Python, vòng lặp for được thiết kế chủ yếu để làm việc với mảng hoặc các cấu trúc tương tự mảng. Do đó, có thể nói vòng lặp while là vòng lặp đa dụng (mọi tác vụ lặp đều có thể dùng vòng lặp này), còn vòng lặp for mang tính chuyên dụng cho mảng. Cú pháp của vòng lặp for: for bien_chay in mang: khoi_lenh_lap khoi_lap_sau_for Luồng hoạt động của vòng lặp for được mô tả trong lưu đồ ở Hình 3-2. Trong cú pháp trên, for và in là các từ khóa, bien_chay là một biến chứa các phần tử trong biến mang. Khi vòng lặp được thực thi, bien_chay 46 sẽ lần lượt chứa các phần tử trong mang. Ví dụ, nếu mang là một list có các phần tử [2, 4, -6, 50] thì khối lệnh lặp sẽ được chạy 4 lần, mỗi lần chạy thì bien_chay sẽ lần lượt có các giá trị 2, 4, -6 và 50. Sau khi chạy lặp 4 lần thì khoi_lap_sau_for sẽ được thực thi. Hình 3-2 Lưu đồ hoạt động của vòng lặp for. Ví dụ 3.7: Cho một list chứa các nhiệt độ tại nhiều thời điểm của một thiết bị. Viết code để truy xuất các nhiệt độ âm chứa trong mảng. #%% Xuất nhiệt độ âm list_nhiet_do = [-2, -5, -8, 3, -1, 6, 0] list_am = [] for phan_tu in list_nhiet_do: if phan_tu<0: list_am.append(phan_tu) print(list_am) 47 Trong code trên, chúng ta khởi tạo list_am bằng một list rỗng (không chứa phần tử nào). Khi chạy vòng lặp, các phần tử thỏa điều kiện (giá trị âm) sẽ lần lược được thêm (append) vào list_am. Thực hành: Thay đổi code trên để truy xuất các nhiệt độ nằm trong khoảng từ 0 đến 10 độ. Ví dụ 3.8: Cho một list chứa các nhiệt độ tại nhiều thời điểm của một thiết bị. Viết code để xác định index của các nhiệt độ âm trong mảng. #%% In index các phần tử âm print('Indices cac phan tu am:') for i in range(len(list_nhiet_do)): if list_nhiet_do[i]<0: print(i) Để ý trong code trên chúng ta sử dụng hàm range(len(list_nhiet_do)). Hàm này có công dụng tạo ra một mảng chứa các index của list_nhiet_do, tức là list [0,1,2,3,4,5,6] (vì list_nhiet_do có 7 phần tử). Khi thực thi, biến i sẽ lần lượt chứa các phần tử của list index, tức là trong mỗi lần lặp, biến i sẽ là index của một phần tử trong list_nhiet_do (lần lượt từ trái sang phải). Thực hành: Thay đổi code để lưu các phần tử âm vào một mảng (tương tự ví dụ bên trên) và lưu index của các phần tử âm vào một mảng khác. Thực hành: Nhập đoạn mã sau và đặt tên là for_demo.py. Sau đó hãy thực thi đoạn mã và cho biết kết quả in ra màn hình. # iteration - for print('demo - iteration for') for i in range(1, 5): print(i) # nested - for print('demo - nested for') 48 for i in range(1, 3): for j in range(5, 10): print(str(i) + '-' + str(j)) 3.4 Break, continue và pass Từ khóa break có thể được sử dụng để dừng đoạn mã hoặc thoát ra khỏi vòng lặp đang chứa break. Ngược lại, từ khóa continue có thể được sử dụng để bỏ qua một số phần mã phía sau continue để tiếp tục thực hiện vòng lặp mới. Còn từ khóa pass đơn giản là một câu lệnh giả (câu lệnh không làm gì cả). Pass có thể được dùng tại những vị trí mà cú pháp yêu cầu phải có câu lệnh nhưng chương trình của chúng ta không có lệnh nào cần làm ở đó. Để minh họa, giả sử có đoạn mã sau, bằng cách sử dụng lệnh break vòng lặp sẽ dừng khi giá trị value=7. Một trường hợp khác, vòng lặp có thể tiếp tục thực hiện vòng lặp mới và có thể bỏ qua một số thành phần trong thân vòng lặp khi sử dụng lệnh continue. Thực hành: Nhập đoạn mã sau và đặt tên là break_continue_demo.py. Sau đó hãy thực thi đoạn mã và cho biết kết quả in ra màn hình. print('demo - break, continue and pass') for i in range(1, 10): if i == 4: continue if i == 7: break print(i) pass # do nothing print('This is the end of program') 49 Bài tập có lời giải 1. Viết chương trình tính chu vi của một đa giác. Chương trình sẽ đọc các tọa độ x, y của các điểm trong một đa giác. Các tọa độ được đọc liên tục cho đến khi người sử dụng nhập khoảng trắng ( ) đối với tọa độ x. Mỗi khi đọc 1 tọa độ mới, chương trình sẽ tính toán khoảng cách của điểm mới nhập so với điểm trước đó. Khi nhập khoảng trắng vào trong tọa độ x thì chương trình sẽ thêm khoảng cách từ điểm cuối đến điểm đầu để tính chu vi. Khi tính toán và hiển thị chu vi thì các tọa độ của mỗi điểm đã nhập cũng được hiển thị trên màn hình. Ví dụ có dạng như sau 2. Parity bit là một cơ chế đơn giản để phát hiện các lỗi trong việc truyền dữ liệu thông qua một đường truyền không đáng tin cậy như đường dây điện thoại. Ý tưởng rất đơn giản đó là một bit sẽ được thêm vào và truyền đi sau mỗi một nhóm 8-bit dữ liệu. Vì thế một bit lỗi trên đường truyền có thể được phát hiện dễ dàng. Bit parity có thể được tính toán cho cả trường hợp parity là chẵn hoặc lẻ. Nếu là parity chẵn thì tổng số bit 1 được truyền, gồm 8 bit dữ liệu và 1 bit parity phải là số chẵn. Ngược lại, khi chọn bit parity lẻ thì tổng số bit 1 truyền đi sẽ là lẻ. Viết chương trình tính bit parity của một nhóm 8 bit nhị phân được nhập bởi người dùng sử dụng parity chẵn. Chương trình sẽ đọc chuỗi nhị phân gồm 8 bit cho đến khi nhập khoảng trắng. Sau mỗi chuỗi được nhập vào bởi người dùng, chương trình cần hiển thị một thông điệp chỉ ra rằng người dùng muốn tính parity chẵn hay lẻ. Một thông điệp cảnh báo sẽ được hiển thị nếu người dùng nhập một chuỗi nhị phân có hơn 8 bit. 3. Viết chương trình thực hiện chuyển đổi số thập phân thành nhị phân. Với số nguyên thập phân do người dùng nhập vào từ bàn 50 phím hãy sử dụng pháp chia được minh họa sau đây để thực hiện việc chuyển đổi. Khi chương trình chuyển đổi được hoàn tất, kết quả in ra màn hình sẽ chứa một chuỗi các bit nhị phân tương ứng với số thập phân mà người dùng nhập. Ban đầu cho chuỗi rỗng result Cho q là thể hiện của số cần chuyển đổi Repeat Cho r bằng với phần dư của phép chia q với 2 Chuyển r thành chuối và cộng r với phần bắt đầu của result Thực hiện chia q với 2, loại bỏ phần dư, và lưu phần nguyên của phép chia vào chính q Until q bằng 0 Bài tập thực hành 1. Sử dụng vòng lặp while tính các tổng sau, với n là một số nguyên dương nhập bởi người dùng: 1 1 1 𝑆(𝑛) = 1 + + + ⋯+ 1+2 1+2+3 1 + 2 + ⋯+ 𝑛 1 1 1 𝑆(𝑛) = + +⋯+ 1×2 2×3 𝑛 × (𝑛 + 1) 1 2 3 𝑛 𝑆(𝑛) = + + + ⋯ + 2 3 4 𝑛+1 1 3 5 2𝑛 + 1 𝑆(𝑛) = + + … + 2 4 6 2𝑛 + 2 2. Cho người dùng nhập số nguyên dương N. Nếu nhập sai thì cho nhập lại đến khi đúng. Sau đó cho người dùng nhập N số nguyên và in ra số lẻ lớn nhất trong những số được nhập. Cuối cùng, hỏi người dùng có muốn tiếp tục không, nếu người dùng nhấn ‘c’ thì tiếp tục, nhấn phím khác thì thoát chương trình. 3. In ra tất cả ước số của một số nguyên N nhập bởi người dùng. 4. Tính tổng các chữ số của số nguyên dương N nhập bởi người dùng. Ví dụ: số N = 205 có tổng các chữ số là 2+0+5 = 8. Gợi ý: với N = 257, ta có 257 % 10 = 7 int(257/10) = 25 51 5. Dùng vòng lặp for tính các tổng sau, với n nguyên dương nhập bởi người dùng. 1 1 1 𝑆(𝑛) = + +⋯+ 1×2 2×3 𝑛 × (𝑛 + 1) 1 3 5 2𝑛 + 1 𝑆(𝑛) = + + … + 2 4 6 2𝑛 + 2 6. Tạo một list có độ dài tùy ý chứa các phần tử tùy ý. Dùng vòng lặp for tìm phần tử chia hết cho 5 lớn nhất trong list. 7. Tạo một list có độ dài tùy ý chứa các phần tử tùy ý. Dùng vòng lặp for xóa các phần tử lớn hơn A trong list, với A là một số thực nhập bởi người dùng. Gợi ý: Có thể tạo list mới chứa phần tử không bị xóa. Nếu muốn xóa bằng lệnh del thì cần dùng vòng lặp while. 8. Tạo một list có độ dài tùy ý chứa các phần tử đã sắp tăng dần. Dùng vòng lặp for chèn một số B vào list sao cho list vẫn tăng dần, với B là một số thực nhập bởi người dùng. Gợi ý: dùng vòng lặp tìm ra vị trí (index) thích hợp cần chèn, sau đó dùng hàm insert() để chèn B vào list. Có thể tìm hiểu từ khóa break trong vòng lặp để sử dụng. 9. Tìm N số nguyên tố đầu tiên và lưu vào 1 list. N là một số nguyên dương nhập bởi người dùng. Yêu cầu: dùng vòng lặp for kết hợp với vòng lặp while. 10. Viết chương trình tính căn bậc hai của một số x. Sử dụng phương pháp Newton để tính toán và hiển thị căn bậc hai của một số x được nhập bởi người dùng. Thuật toán Newton như sau: Đọc số x từ bàn phím Khởi tạo guess bằng x/2 While guess không thỏa điều kiện do Cập nhật giá trị guess bằng với trung bình của guess và x/guess. Khi thuật toán chạy hoàn chỉnh thì guess chứa một giá trị xấp xỉ với căn bậc hai của x, giá trị của phép xấp xỉ sẽ phụ thuộc vào điều kiện thỏa mãn mà người dùng đặt. Trong bài tập này thì guess được xem là thỏa mãn khi giá trị tuyệt đối của guess*guess so với x nhỏ hơn hoặc bằng 10-12. 52 11. Viết chương trình thực hiện chuyển đổi một số nhị phân (binary) sang hệ thập phân (decimal). Chương trình sẽ đọc một chuỗi các số nhị phân. Sau đó, chương trình sẽ tính toán một số hệ thập phân tương đương. Cuối cùng, chương trình sẽ hiển thị số thập phân tương ứng lên màn hình. 53 Chương 4 NUMPY Trong chương trước chúng ta đã tìm hiểu về list – một cấu trúc dữ liệu cho phép lưu trữ mảng. List đơn giản và có đầy đủ chức năng cơ bản của mảng. Tuy nhiên, khi chứa mảng số với nhiều phần tử, list có tốc độ thực thi và hỗ trợ tính toán không bằng numpy arrays – một cấu trúc dữ liệu dạng mảng của thư viện numpy. 4.1 Giới thiệu về Numpy1 NumPy (phát âm là /ˈnʌmpaɪ/) là một thư viện cho Python, hỗ trợ làm việc với các mảng lớn, nhiều chiều, và cung cấp các hàm toán học cấp cao hoạt động trên các mảng này. Phiên bản “tổ tiên” của NumPy: Numeric, được tạo bởi Jim Hugunin và có sự đóng góp của một số nhà phát triển khác. Năm 2005, Travis Oliphant tạo ra NumPy bằng cách kết hợp các tính năng của Numarray vào Numeric với nhiều sửa đổi sâu rộng. Hiện nay, NumPy là phần mềm mã nguồn mở và có nhiều người đóng góp phát triển. NumPy là một dự án được tài trợ về mặt tài chính bởi NumFOCUS. 4.2 Cài đặt thư viện numpy Một trong những thế mạnh nổi trội của Python là hệ thống thư viện (packages) mở đồ sộ của nó. Nếu biết cách sử dụng packages, việc lập trình với Python sẽ trở nên dễ dàng hơn rất nhiều, vì các packages đã cài đặt sẵn rất nhiều chức năng phục vụ đủ loại tác vụ mà chúng ta có thể làm với máy tính. Để sử dụng một package, trước tiên cần cài đặt nó. Đa số các thư viện trong Python có thể cài đặt đơn giản như sau: 1 Nguồn: https://en.wikipedia.org/wiki/NumPy 54 1. Mở command prompt với quyền admin (nếu sử dụng hệ điều hành Windows) hoặc terminal (nếu sử dụng Linux, MacOS). 2. Chạy lệnh: pip install package_name Trong đó package_name là tên của package bạn muốn cài đặt. Ví dụ, để cài đặt numpy: pip install numpy Lưu ý: Lệnh này yêu cầu kết nối internet để tải thư viện về máy. Để xem danh sách các thư viện đã được cài đặt trên máy tính, trong cửa sổ command prompt hoặc terminal, bạn dùng lệnh: pip list. Sau khi cài đặt thư viện xong, cần khai báo thư viện trước khi sử dụng. Cú pháp như sau: import ten_package as ten_viet_tat Trong đó ten_package là tên thư viện cần dùng, còn ten_viet_tat là một tên tùy ý được đặt để gọi thư viện cho tiện. Ví dụ: import numpy as np import matplotlib as mpl Lưu ý: Cần phải cài đặt thư viện trước khi sử dụng, nếu không khi chạy lệnh import sẽ báo lỗi không tìm thấy thư viện. 4.3 Numpy arrays Numpy là một package chuyên hỗ trợ các tác vụ số học. Numpy arrays là cấu trúc hỗ trợ mảng của thư viện numpy. Nó có nhiều tính năng giống với list, như index, truy xuất phần tử, slicing. Trong phần này, chúng ta sẽ nói về những điểm khác biệt của numpy arrays và lists. Để tạo một numpy array ta dùng cú pháp sau: ten_array = np.array(list) Trong đó, list là tên một list hoặc một list tường minh. Xem ví dụ sau: array1 = np.array([1, 4, 3, -6, 2, 4]) Lệnh trên tạo tra một numpy array có tên là array1 với các phần tử 1, 4, 3, -6, 2, 4. Ví dụ khác: list1 = [1, 4, 3, -6, 2, 4] 55 array2 = np.array(list1) Code trên tạo tra một numpy array có tên là array2 với các phần tử của list list1. Lưu ý: Khác với list, numpy array các phần tử của numpy array chỉ có 1 kiểu dữ liệu duy nhất. Ví dụ nếu trong array có các phần tử là kiểu số nguyên và số thực thì tất cả các phần tử sẽ được tự động chuyển đổi thành kiểu số thực. Điều này giúp các phép tính toán trên numpy array nhanh hơn list vì dữ liệu có kiểu đồng nhất. Bạn cũng không cần quan tâm về việc chuyển đổi kiểu dữ liệu, vì nó được numpy thực hiện một cách tự động. Chúng ta chỉ cần biết sử dụng numpy array sẽ có tốc độ tính toán cải hiện so với list đối với dữ liệu số và kích thước mảng lớn. Việc truy xuất phần tử trong numpy array giống với list, tức là bạn có thể dùng index không âm, index âm, slicing, ví dụ: array1[2] array1[-1] array1[:3] Để thêm, xóa các phần tử vào numpy array, ta dùng các hàm sau: ▪ ▪ ▪ append(): thêm phần tử vào cuối mảng. Cách gọi hàm này hơi khác với append() trong list đôi chút. Ví dụ: array1 = np.append(array1, 48) (đối với list (xem mục 3.2) thì hàm append() được gọi đơn giản hơn: list1.append(48)) insert(): chèn phần tử. Ví dụ: array1 = np. insert(array1, 2, 90) (code đối với list: list1.insert(2, 90)) delete(): xóa phần tử. Ví dụ: array1 = np. delete(array1, 5) (code đối với list: del list1[5]) Thư viện numpy cũng hỗ trợ các phép toán tương tự thư viện math, nhưng phong phú và tốc độ thực thi nhanh hơn. Một số hàm tính toán của numpy bao gồm1: 1 Xem danh sách đầy đủ tại https://numpy.org/doc/stable/reference/routines.math.html 56 ▪ ▪ ▪ Các hàm tính toán: np.sqrt(), np.exp(), np.log(), np.cos(), … Các hàm làm tròn: np.round(), np.floor(), np.ceil(), … Các hàm tạo số ngẫu nhiên: np.random.randint(), np.random.rand() Ngoài ra, numpy còn có các hàm hỗ trợ tạo mảng với các phần tử cách đều, như np.arange(), np.linspace(). Ví dụ, code sau tạo ra một numpy array chứa các số nguyên từ 2 đến 9: np.arange(2,10,1) Trong đó, tham số đầu tiên (số 2) là giá trị phần tử đầu tiên cần tạo, tham số kế tiếp (số 10) là giá trị stop (phần tử cuối cùng được tạo luôn luôn nhỏ hơn giá trị stop này), tham số cuối cùng (số 1) là bước nhảy. Một ví dụ khác: để tạo ra một numpy array chứa các số lẻ từ 3 đến 15 ta dùng code sau: np.arange(3,16,2) Để ý rằng step là 2, khi đó hàm arange sẽ tạo các số cách nhau 2 giá trị. Hàm linspace() cũng có công dụng tương tự hàm arange() (tạo mảng với các phần tử cách đều), tuy nhiên thay vì chỉ định bước nhảy, ta chỉ định số lượng phần tử cần tạo. Ví dụ, lệnh sau sẽ tạo ra mảng bao gồm 4 phần tử cách đều nhau có giá trị từ 1 đến 20: [1, 5.75, 10.5, 15.25, 20] np.linspace(1,20,5) Ưu điểm của numpy array so với list là bên cạnh tốc độ thực thi nhanh, numpy array còn hỗ trợ phép toán trên từng phần tử (element-wise operations). Dạng phép toán không chỉ tăng tốc độ thực thi trên mảng kích thước lớn mà còn cho phép viết code rất gọn gàng. Xem ví dụ sau. Ví dụ 4.1: Cho mảng chứa cân nặng và mảng chứa chiều cao của một số sinh viên. Hãy tính Body mass index (BMI) của các sinh viên này. # Cách 1: sử dụng list list_can_nang = [50, 62, 71, 55, 80] list_chieu_cao = [1.65, 1.67, 1.8, 1.53, 1.7] list_BMI = [] for i in range(0, len(list_can_nang)): bmi = list_can_nang[i]/list_chieu_cao[i]**2 list_BMI.append(round(bmi,2)) 57 print(list_BMI) # Cách 2: sử dụng numpy array arr_can_nang = np.array([50, 62, 71, 55, 80]) arr_chieu_cao = np.array([1.65, 1.67, 1.8, 1.53, 1.7]) arr_BMI = arr_can_nang/arr_chieu_cao**2 print(arr_BMI.round(2)) Có thể thấy code với numpy array ngắn gọn và trực quan hơn nhiều vì bạn có thể viết code tính toán sử dụng trực tiếp tên mảng (không cần viết rõ từng phần tử mảng). Ngoài ra, việc loại bỏ được vòng lặp giúp code thực thi nhanh hơn. Lưu ý: Để có thể sử dụng các pháp toán trên từng phần tử thì các numpy array phải có kích thước giống nhau. 58 Bài tập thực hành 1. Tạo numpy array chứa các số lẻ từ 1 đến N, với N là số nguyên dương nhập bởi người dùng. Gợi ý: dùng hàm numpy.arange() (tương tự hàm range() của Python. Sinh viên có thể dùng cú pháp ? hoặc google để biết thêm thông tin về hàm). 2. Tạo numpy array chứa 20 số thực cách đều nhau thuộc khoảng (a, b) với a, b nhập bởi người dùng. Gợi ý: dùng hàm numpy.linspace() (có thể google cách dùng). 3. Tạo numpy array chứa 10 số thực ngẫu nhiên trong khoảng (a, b) với a, b nhập bởi người dùng. Gợi ý: dùng random.rand() (tạo số thực ngẫu nhiên trong [0,1)) và cộng/trừ/nhân/chia với a, b để ra khoảng mong muốn. 4. Cho số lượng hàng hóa bán ra trong tuần của một cửa hàng được lưu trong một mảng 2D có shape (2, 7), với mỗi dòng tương ứng với buổi (sáng, chiều), và mỗi cột tương ứng với ngày trong tuần. Sinh viên tự cho giá trị các phần tử (có thể dùng random.randint()). Hãy viết chương trình cho biết: a. Ngày bán được nhiều nhất tuần (theo tổng số hàng của cả 2 buổi). Gợi ý: dùng for trên cột. b. Thời điểm bán được nhiều nhất (buổi nào, ngày nào). Gợi ý: dùng for lồng nhau. c. Buổi nào có khuynh hướng bán được nhiều hàng hơn. Ví dụ: buổi sáng bán nhiều hơn buổi chiều trong 4 ngày thì kết luận buổi sáng. Còn nếu buổi sáng bán nhiều buổi chiều trong 3 ngày, 1 ngày bán bằng nhau, thì kết luận cả 2 buổi như nhau. Gợi ý: dùng for lồng nhau. 59 Chương 5 SETS VÀ DICTIONARIES Trong các chương trước, chúng ta đã được giới thiệu về cấu trúc mảng (cài đặt bằng lists hoặc numpy arrays). Trong chương này, chúng ta tìm hiểu về 2 cấu khác: sets (tập hợp) và dictionaries (cấu trúc dạng từ điển). Các cấu trúc này cho phép tổ chức dữ liệu khác biệt hơn so với mảng. Sets thì các phần tử không có thứ tự, không có index. Còn dictionaries thì cho phép thiết lập index không phải số nguyên. 5.1 Sets Cấu trúc dữ liệu sets (tập hợp) có thể chứa nhiều phần tử tương tự như mảng. Nhưng khác với mảng, các phần tử của sets không có thứ tự (tức là không thể đánh index). Cấu trúc này có thể coi là cài đặt của khái niệm tập hợp trong toán học. Để tạo ra một set, ta dùng cú pháp với dấu ngoặc nhọn như các ví dụ sau: set_1 = {3,3,2,2,4,5,6,2} set_ho_ten = {'Tam', 'An', 'Hoa', 'Binh'} set_diem = {'Tam', 10, 'An', 8, 'Hoa', 9.1, 'Binh', 5.4} Tập hợp có 2 đặc tính: các phần tử không có thứ tự và không trùng lặp. Thứ nhất, tập hợp không có thứ tự, vì vậy các phần tử không thể được đánh index. Nói cách khác, bạn không thể truy xuất các phần tử trong tập hợp bằng index như mảng. Thực hành: Truy xuất phần tử trong set sử dụng index, ví dụ set_1[2]. Để lấy một phần tử ra khỏi tập hợp, ta làm cách gián tiếp như sau: 1. Kiểm tra xem phần tử có trong set không sử dụng từ khóa in. 2. Nếu có phần tử, thì dùng hàm remove() để xóa phần tử ra khỏi set. 60 Code sau đây thực hiện 2 bước vừa mô tả: phan_tu = 3 if phan_tu in set_1: print('set_1 co chua phan tu',phan_tu) set_1.remove(phan_tu) else: print(('set_1 khong chua phan tu',phan_tu)) Thực hành: Gọi hàm remove() với một phần tử không có trong set_1. Thử in set_1 sau khi đã gọi hàm remove(). Đặc tính thứ hai của tập hợp là các phần tử không trùng lặp. Điều này có nghĩa là nếu bạn đưa vào mảng nhiều phần tử giống nhau thì chỉ có duy nhất một phần tử trong đó được giữ lại. Xem ví dụ sau: set_1 = {3,3,2,2,4,5,6,2} print(set_1) Kết quả in ra của lệnh print(set_1) như sau: {2, 3, 4, 5, 6}. Tức là chỉ có 1 phần tử 3, 1 phần tử 2 được giữ lại trong set_1. Chúng ta có thể sử dụng đặc tính này của tập hợp để lọc lại các phần tử khác nhau của một tập nào đó như các ví dụ sau. Ví dụ 5.1: Cho một đoạn văn bản. Hãy xác định các chữ cái có trong đó. text = "Mississippi river" print(set(text)) Khi chạy đoạn code trên, bạn sẽ thấy các chữ cái cấu thành đoạn text bao gồm: 'p', 'i', 'M', 'e', 's', 'v', ' ', 'r'. Để ý rằng khoảng trắng cũng là một phần tử của tập hợp kết quả, ta có thể loại bỏ nó ra bằng cách xử lý chuỗi (xem chương sau). Ví dụ 5.2: Cho một danh sách người thực hiện một công việc nào đó. Danh sách này lưu dưới dạng mảng: mỗi phần tử của mảng là tên của người thực hiện trong một ngày (mỗi ngày có 1 người thực hiện). Hãy xác định số người đã tham gia vào công việc này. 61 nhat_ky_cong_viec = ['Tam', 'An', 'Tam', 'Hoa', 'An', 'Binh', 'Tam', 'An', 'Tam'] nguoi_tham_gia = set(nhat_ky_cong_viec) print('So luong nguoi tham gia:',len(nguoi_tham_gia)) Thực hành: Hãy xác định danh sách người tham gia trong 4 ngày đầu và 3 ngày cuối. Gợi ý: dùng slicing trên mảng danh sách người thực hiện. Để thao tác với tập hợp, ta có thể dùng các hàm sau: ▪ ▪ ▪ add(): thêm một phần tử vào tập hợp. Ví dụ, set_1.add(9) union(): tìm hợp 2 tập hợp. Ví dụ, nhom1 = {'Tam', 'An'} nhom2 = {'Hoa', 'Binh', 'Tam'} danh_sach_tong_hop = nhom1.union(nhom2) print(danh_sach_tong_hop) intersection(): tìm giao của 2 tập hợp. Ví dụ, ds_tham_gia_ca_2_nhom = nhom1.intersection(nhom2) print(ds_tham_gia_ca_2_nhom) Thực hành: Sử dụng các hàm này trên tập hợp tùy ý và in kết quả sau khi thực hiện. Tìm hiểu (google) và sử dụng hàm issubset() cũng như các hàm khác của tập hợp. 5.2 Dictionaries Dictionaries (cấu trúc dữ liệu dạng từ điển) là cấu trúc cho phép chứa nhiều phần tử tương tự như mảng. Điểm khác biệt của dictionaries là cho phép tạo index tùy ý. Đối với mảng, index luôn là số nguyên (số nguyên âm hoặc không âm). Đối với dictionaries, bạn có thể tạo index bằng số hoặc bằng chữ đều được, miễn là chúng phải khác biệt nhau cho các phần tử (xem các ví dụ bên dưới). Để tạo một dictionary ta dùng cú pháp với dấu ngoặc nhọn: ten_dictinary = {index1: giatri1, index2: giatri2, index3: giatri3} Mỗi phần tử trong một dictionary là một bộ gồm hai phần: index và giá trị, ngăn cách nhau bởi dấu hai chấm. Trong cú pháp trên chúng ta 62 tạo ra một dictionary có ba phần tử. Trong thực tế, số lượng phần tử là tùy ý. Ví dụ 5.3: Tạo một dictionary chứa tên các dụng cụ và số lượng tồn kho của chúng. #%% Tạo dictionary kho_dung_cu = {'Vit': 3, 'May khoan': 2, 'Kem': 4, 'Bua': 1, 'May khoan': 1} # Truy xuất phần tử: print(kho_dung_cu['Kem']) # Thay đổi giá trị phần tử: kho_dung_cu['Kem'] = 3 # Thêm phần tử: kho_dung_cu['Thang'] = 1 kho_dung_cu['Keo'] = 3 Trong ví dụ trên, biến kho_dung_cu là một dictionaries chứa 5 phần tử tương ứng với tên dụng cụ (index) và số lượng của chúng (giá trị), ví dụ: Vít có 3 cái, Máy khoan có 2 cái. Để truy xuất một phần tử trong một dictionary chúng ta đặt index trong dấu ngoặc vuông tương tự như với mảng, chỉ khác mảng ở chỗ index của dictionary không nhất thiết là số nguyên. Ví dụ để truy xuất giá trị của phần tử 'Kem' chúng ta dùng code: kho_dung_cu['Kem']. Để chỉnh sửa giá trị cho phần tử, chúng ta đơn giản dừng phép gán sau khi truy xuất phần tử, ví dụ lệnh sau đây sẽ thay đổi giá trị của phần tử 'Kem' thành 3: kho_dung_cu['Kem'] = 3. Để thêm phần tử vào một dictionary, bạn viết code giống như truy xuất một phần tử trong dictionary, nhưng sẽ đưa vào index mới chưa tồn tại trong dictionary. Ví dụ, trong dictionary kho_dung_cu chưa có phần tử với index 'Thang', khi đó bạn dùng lệnh sau để thêm phần tử 'Thang' với số lượng 1 vào dictionary kho_dung_cu: kho_dung_cu['Thang'] = 1. Lưu ý rằng bạn phải gán giá trị cho phần tử mới, tức là bạn không thể tạo ra một phần tử khuyết giá trị. 63 Lưu ý: Các index trong một dictionary không được trùng nhau, tức là bạn không thể tạo một dictionary với nhiều phần tử có index giống nhau. Lý do là index được sử dụng để truy xuất phần tử, nếu có nhiều index giống nhau thì lệnh truy xuất phần tử sẽ không thể trả về một giá trị duy nhất được. Một lưu ý nữa là không chỉ index của các phần tử trong dictionary có thể có kiểu dữ liệu là chữ hoặc số tùy ý, mà giá trị của các phần tử cũng có thể có kiểu dữ liệu là chữ hoặc số tùy ý. Ví dụ: thoi_khoa_bieu = {'Thu 2': 'Toan, Ve ky thuat', 'Thu 4': 'Xac suat thong ke', 'Thu 5:': 'Tin hoc, The chat'} Code trên tạo ra một dictionary với index và giá trị đều có kiểu chữ (string). Bạn cũng có thể tạo ra giá trị có cấu trúc mảng như ví dụ sau: thoi_khoa_bieu = {'Thu 2': ['Toan', 'Ve ky thuat'], 'Thu 4': ['Xac suat thong ke'], 'Thu 5:': ['Tin hoc', 'The chat']} Bên cạnh việc truy xuất phần tử bằng index, Python còn cung cấp một số hàm giúp bạn làm việc với dictionary thuận tiện hơn. ▪ ▪ ▪ Hàm keys(): trả về các index trong dictionary. Ví dụ: kho_dung_cu.keys() Hàm values(): trả về các giá trị trong dictionary. Ví dụ: kho_dung_cu.values() Hàm items(): trả về các phần tử (bao gồm cả index và giá trị) trong dictionary. Ví dụ: kho_dung_cu.items() Ví dụ sau sử dụng các hàm trên để lấy ra danh sách các dụng cụ có số lượng từ 2 cái trở lên: # Cách 1: dùng hàm keys() ds_dung_cu_hon_2_cai = [] for ten in kho_dung_cu.keys(): if kho_dung_cu[ten]>=2: ds_dung_cu_hon_2_cai.append(ten) 64 # Cách 2: dùng hàm items() ds_dung_cu_hon_2_cai = [] for ten, so_luong in kho_dung_cu.items(): if so_luong>=2: ds_dung_cu_hon_2_cai.append(ten) Điểm khác biệt của hai cách trên là nếu dùng hàm keys() thì ta phải truy xuất số lượng dụng cụ sử dụng index kho_dung_cu[ten]. Còn khi dùng hàm items() thì số lượng dụng cụ đã được lấy sẵn đi kèm với index rồi. Ví dụ 5.4: Cho một dictionary chứa bảng điểm của sinh viên, trong đó index là tên sinh viên còn giá trị là một list chứa điểm các môn học của sinh viên đó (xem code bên dưới). Hãy viết code thực hiện các chức năng sau: 1. Thêm một cột điểm cho các sinh viên. 2. Tính điểm trung bình cho các sinh viên. # Bảng điểm: bang_diem = {'An': [9.5, 7.2, 8.6], 'Binh': [8.2, 9.1], 'Long': [5.4, 7.2, 8.3], 'Hoa': [6.1, 5.6, 7.7, 7.9]} # 1. Thêm một cột điểm cho các sinh viên: for ten in bang_diem.keys(): diem = float(input('Nhap diem cua '+ten)) bang_diem[ten].append(diem) # 2. Tính điểm trung bình cho các sinh viên: diem_tb = {} for ten, list_diem in bang_diem.items(): diem_tb_1_sv = sum(list_diem)/len(list_diem) diem_tb[ten] = round(diem_tb_1_sv,2) Thực hành: Viết code tìm tên sinh viên có điểm trung bình lớn nhất (để nhà trường trao học bổng chẳng hạn). 65 Thực hành: Nhập đoạn mã sau và đặt tên là dictionaries_demo.py Sau đó hãy thực thi đoạn mã và cho biết kết quả in ra màn hình. # declare a = {} b = {2: 'Sea', 3: 'River', 8: 'Mountain'} c = {2: {4: 'abcd', 5: 'hjkl'}, 3: 'vbnm'} d = dict(name='elena', age=30, roles=('manager', 'consultant')) # print print(a) print(b) print(c) print(d) # keys values print(b.keys()) print(b.values()) print(b.items()) # add item a.setdefault(2, 'car') a.setdefault(5, 'train') a.setdefault(7, 'plane') print(a) # check key print(3 in b) print(5 in b) 66 Bài tập có lời giải 1. Mã Morse là một sơ đồ mã hóa sử dụng dấu – và dấu . để thể hiện các số và ký tự. Viết chương trình sử dụng dictionary để lưu trữ bảng ánh xạ các ký tự này sang mã Morse. Bảng ánh xạ được thể hiện dưới đây. Chương trình sẽ đọc một chuỗi từ người dùng sau đó sẽ dịch chuỗi gồm các ký tự và số thành mã Morse, đặt thêm các khoảng trắng giữa chuỗi các dấu gạch ngang – và dấu chấm . Chương trình sẽ bỏ qua bất cứ ký tự nào không được liệt kê ở bảng dưới đây. Dưới đây là mã Morse cho chuỗi Hello, World! 67 Bài tập thực hành 1. Đếm số lần xuất hiện của các chữ cái (không kể dấu câu, ví dụ: . –) được dùng trong một câu, ví dụ: “An eye for an eye makes the whole world blind. – Mahatma Gandhi”1 Câu này có số lần xuất hiện của ‘A’: 1 lần, ‘a’: 6 lần, ‘b’: 1 lần... Gợi ý: Có thể dùng set và dictionary kết hợp với vòng lặp để đếm (dictionary dùng để lưu kết quả). 2. Tạo một chương trình quản lý thời gian trong ngày sử dụng dictionary với keys là các loại hoạt động (ví dụ: Học, Ngủ, Thể dục, Chơi, Di chuyển...) và values là thời gian (tính theo phút) của hoạt động tương ứng. Chương trình cần thực hiện các chức năng sau: a. Nhập thêm thời gian: Hỏi người dùng nhập key hoạt động và số phút rồi cộng dồn vào số phút đang có. b. Thống kê thời gian các hoạt động (tính theo giờ), ví dụ: Học 8.4 giờ, Ngủ 7.2 giờ, Di chuyển 0.8 giờ... c. Cho biết 2 hoạt động được làm nhiều nhất và 2 hoạt động làm ít nhất trong ngày. Mahatma Gandhi (1869 – 1948) là một lãnh tụ của Ấn Độ đã dẫn dắt đất nước với hơn 350 triệu dân (vào những năm 1940) thoát khỏi chế độ thực dân của Đế quốc Anh. Ông nổi tiếng với phương pháp bất bạo lực (nonviolence) đã giúp Ấn Độ tránh được cuộc chiến tranh thương vong lớn mà vẫn giành được độc lập. Ông là một trong những biểu tượng toàn cầu của phong trào bất bạo lực. 1 Câu nói “An eye for an eye makes the whole world blind” (tạm dịch: Một con mắt đổi một con mắt chỉ làm cả thế giới mù lòa) được cho là của ông Gandhi đã nói khi thuyết phục nội các chính phủ Ấn Độ chọn con đường bất bạo lực để đối phó với Đế quốc Anh. Trong bộ phim điện ảnh Gandhi (thắng 8 giải Oscars) kể về cuộc đời ông, câu nói này nằm trong lời thoại: “An eye for an eye only ends up making the whole world blind”. 68 Chương 6 STRINGS Các chương trước đã giới thiệu các các cấu trúc dữ liệu như mảng, dictionaries với các phần tử chủ yếu có kiểu dữ liệu dạng số (số nguyên, số thực). Trong chương này, chúng ta tìm hiểu về kiểu dữ liệu chuỗi (strings) dành cho chữ (text). 6.1 Khái niệm và khởi tạo strings Kiểu dữ liệu string (thường được dịch là chuỗi) là kiểu dữ liệu phục vụ lưu trữ chữ (text). Một cách đơn giản, một chuỗi là một tuple (xem mục 3.2) của các ký tự (characters). Để tạo 1 string bạn dùng cú pháp với dấu nháy đơn hoặc nháy kép: ten_string = 'noi dung string' ten_string = "noi dung string" Cả hai cú pháp trên hoàn toàn tương đương nhau. Lưu ý rằng các strings tạo bởi cú pháp trên chỉ chứa text nằm trên một dòng. Để tạo string có nội dung nằm trên nhiều dòng các bạn dùng cú pháp với 3 dấu nháy đơn liên tiếp: ten_string = '''Dong 1. Dong 2. ''' Ví dụ 6.1: str1 = 'Hom nay la thu ba.' ho_ten = 'Tran Nhan Tong' bai_tho = '''Công cha như núi Thái Sơn, Nghĩa mẹ như nước trong nguồn chảy ra. Một lòng thờ mẹ kính cha, Cho tròn chữ hiếu mới là đạo con.''' Bởi vì string là một tuple của các ký tự nên bạn có thể truy xuất các ký tự của nó giống như cách truy xuất các phần tử của tuple. Ví dụ: 69 print(ho_ten[2]) print(bai_tho[5:8]) for char in ho_ten: print(char) Lưu ý: Như trình bày trong mục 3.2, tuple là kiểu mảng hằng số, tức là các phần tử của tuple không thể bị thay đổi sau khi tuple được khởi tạo. Vì vậy, bạn không thể thay đổi các ký tự trong string bằng phép gán như với mảng. Đây là điểm khác biệt của kiểu string trong Python với các ngôn ngữ khác, như C, C++. Bạn có thể kết hợp 2 string để tạo string mới bằng cú pháp với dấu cộng: ho = 'Trần' ten = 'Hiếu Nhân' ho_ten = ho + ten print(ho_ten) Thực hành: Thử chạy code sau và giải thích kết quả khác biệt với code bên trên. Lưu ý ' ' là một string chứa khoảng trắng (ở giữa 2 dấu nháy đơn có 1 khoảng trắng). ho_ten = ho + ' ' + ten print(ho_ten) Ví dụ 6.2: Đếm số lần xuất hiện của các ký tự trong 1 string. Yêu cầu: chỉ đếm ký tự trong bảng chữ cái tiếng Anh. quote = 'An eye for an eye makes the whole world blind. - Mahatma Gandhi' char_count = {} for char in quote: if 'A'<=char<='Z' or 'a'<=char<='z': if char in char_count.keys(): char_count[char] += 1 else: char_count[char] = 1 print(char_count) 70 Thực hành: Đọc hiểu và giải thích hoạt động của đoạn code trên. Gợi ý: Bạn hãy nhớ lại cách tạo mới và cập nhật giá trị các phần tử trong một dictionary. Ví dụ 6.3: Đếm số từ (word) trong 1 string. Gợi ý: một từ luôn được bắt đầu bằng một khoảng trắng và sau đó là một ký tự. dem = 0 for i in range(len(str1)-1): if (str1[i]==' ') and (str1[i+1]!=' '): dem += 1 if str1[0]!=' ': dem += 1 6.2 Hàm xử lý strings Bên cạnh việc truy xuất các ký tự trong string bằng index (như tuple), Python còn hỗ trợ một số hàm để làm việc với string như sau: ▪ Hàm split(): tách string thành một list các phần tử dựa theo ký tự phân cách (separator, mặc định là khoảng trắng). Ví dụ: list_words = ho_ten.split() ▪ Hàm strip(): xóa khoảng trắng ở đầu và cuối string. Ví dụ: bai_tho = bai_tho.strip() ▪ Hàm replace(): thay thế một string con trong string. Ví dụ: str1 = str1.replace('ba', 'tu') ▪ Hàm isdigit(): trả về True nếu tất cả ký tự trong string là chữ số. Ví dụ: so_dien_thoai.isdigit() Tham khảo thêm các hàm xử lý khác tại https://www.w3schools.com/python/python_ref_string.asp Ví dụ 6.4: Đếm số từ (word) trong 1 string. list_words = str1.split() no_of_words = len(list_words) Ví dụ 6.5: Trích xuất mã OTP từ tin nhắn. tin_nhan = 'Ma OTP cua ban la 12583' list_tu = tin_nhan.split() OTP = '' for tu in list_tu: if tu.isdigit() == True: OTP = tu print('Ma OTP:', OTP) 71 Thực hành: Trích xuất mã OTP trong tin nhắn sau: tin_nhan = 'Ma OTP cua ban la 12583 (ma co hieu luc trong 120 giay).' Gợi ý: OTP là đoạn số có chiều dài 5 chữ số. Thực hành: Trích xuất mã OTP trong tin nhắn sau: tin_nhan = 'Ma OTP cua ban la "12583". Ma co hieu luc trong 120 giay.' Gợi ý: Bạn có thể dùng code sau để xóa các ký tự đặc biệt như dấu câu trước khi dùng hàm split() để tách từ. list_dau = ['.', '"', '?', ',', '!'] for char in list_dau: if char in tin_nhan: tin_nhan = tin_nhan.replace(char, '') Lưu ý: đoạn code '' là một chuỗi rỗng (không chức bất cứ ký tự hay khoảng trắng nào). Ví dụ 6.6: Viết hoa chữ cái đầu từ trong một string. Lưu ý rằng bạn không thể thay đổi các ký tự trong một string. Vì vậy chúng ta sẽ chuyển đổi string thành list các ký tự bằng hàm list() để chỉnh sửa các ký tự. Sau đó sẽ chuyển đổi list trở lại thành string bằng hàm join(). ten = 'daI HOc su phAm ky tHUat' ten = ten.lower() list_ten = list(ten) # chuyển string thành list for i in range(len(list_ten)-1): if list_ten[i]==' ' and list_ten[i+1]!=' ': list_ten[i+1] = list_ten[i+1].upper() list_ten[0] = list_ten[0].upper() ten = ''.join(list_ten) # chuyển list thành string print(ten) 72 Bài tập thực hành 1. Đếm số lần xuất hiện của các từ trong một string (không phân biệt hoa thường). Ví dụ: câu “An eye for an eye makes the whole world blind – Mahatma Gandhi” có 2 từ ‘an’, 2 từ ‘eye’, 1 từ ‘for’... Gợi ý: Đổi string thành chữ thường (lower()) và dùng dictionary chứa các từ và số lần xuất hiện của chúng. 2. Kiểm tra một password có mạnh không, biết rằng password được xem là mạnh nếu nó có dài tối thiểu là 8 và có chứa: chữ cái, chữ số, ký tự đặc biệt (không phải chữ cái và chữ số, ví dụ #%@). Ví dụ: ‘SPKT1122@’ là password mạnh; ‘123456789’, ‘spkttpchm’ hoặc ‘#$#&*^#*($’ không phải là passwords mạnh. 3. Tạo password ngẫu nhiên là một password như mô tả trong câu trên. Gợi ý: Tạo ra một số nguyên ngẫu nhiên nằm trong khoảng giá trị ASCII của chữ cái, chữ số, ký tự đặc biệt, rồi chuyển mã ASCII thành ký tự bằng hàm chr(). 4. Tìm hiểu hàm title() và hàm translate(): nêu công dụng và viết code minh họa. 73 Chương 7 HÀM Trong các chương trước, chúng ta đã tìm hiểu qua nhiều thành phần cơ bản của ngôn ngữ lập trình Python. Với những hiểu biết này, bạn có thể lập trình xử lý nhiều công việc khác nhau. Tuy nhiên, khi chương trình trở nên phức tạp, có nhiều đoạn code bạn cần phải dùng đi dùng lại nhiều lần. Lúc này, hàm chính là công cụ giúp bạn xử lý việc tái sử dụng code hiệu quả. 7.1 Khái niệm và cú pháp Khi viết một chương trình lớn, sẽ có nhiều đoạn code bạn cần phải dùng đi dùng lại nhiều lần. Nếu bạn copy các đoạn code này một cách thủ công sẽ gây ra một số vấn đề chương trình của bạn như: ▪ ▪ Code trở nên dài, khó đọc hơn. Nếu đoạn code được copy có lỗi hoặc cần chỉnh sửa, bạn sẽ mất thời gian dò lại tất cả các chỗ đã copy để chỉnh sửa. Điều này làm cho chương trình của bạn khó đọc, dễ mắc lỗi và khó bảo trì, nâng cấp. Lúc này, hàm chính là công cụ giúp bạn xử lý việc tái sử dụng code hiệu quả. Nói một cách đơn giản, hàm là một đoạn code được đặt tên. Khi đó, bạn chỉ việc gọi tên của nó khi muốn sử dụng đoạn code đó. Có hai loại hàm trong lập trình: ▪ ▪ 74 Hàm có sẵn (built-in functions): đây là những hàm đã được viết sẵn trong Python hoặc các thư viện. Ví dụ: print(), input(), numpy.array(), str1.isdigit(). Để xem thông tin về các hàm này bạn dùng cú pháp với dấu chấm hỏi, ví dụ numpy.array? hoặc bạn có thể google chúng. Hàm tự viết (user-defined functions): đây là hàm do bạn tự viết ra để phục vụ cho chương trình của bạn. Chương này sẽ tập trung vào dạng hàm này. Để viết hàm, bạn dùng cú pháp sau: def ten_ham(tham_so1, tham_so2, tham_so3): ''' Thông tin mô tả hàm ''' # Các dòng lệnh của hàm đặt ở đây return gia_tri1, gia_tri2 Trong cú pháp trên, def và return là các từ khóa. Theo sau tên hàm ten_ham là danh sách các tham số (các giá trị đầu vào của hàm). Trong cú pháp trên liệt kê 3 tham số, nhưng số lượng tham số là tùy ý. Bên trong thân hàm phần đầu tiên là thông tin mô tả hàm đặt trong cặp 3 dấu nháy đơn. Phần này là tùy chọn. Thông thường phần này sẽ mô tả các tham số đầu vào và giá trị trả về của hàm. Bên dưới phần mô tả là các dòng lệnh của hàm. Phần cuối cùng là dòng return. Đây là nơi liệt kê các giá trị trả về của hàm. Số lượng giá trị trả về là tùy ý (trong cú pháp trên liệt kê 2 giá trị trả về). Lưu ý: Có hai kinh nghiệm tuy đơn giản nhưng hiệu quả giúp hàm của bạn dễ viết, dễ bảo trì, nâng cấp hơn: 1. Tên hàm nên dễ hiểu. Đừng ngại một tên hàm dài, hãy ngại một tên hàm khó hiểu. Ví dụ: thay vì đặt tên hàm s(), gpt(), min(), bạn hãy đặt tên: TinhDienTich(), GiaiPhuongTrinh(), TimSoNhoNhat(). 2. Mỗi hàm chỉ thực hiện một việc. Thay vì viết một hàm lớn nhiều chức năng, ta nên viết nhiều hàm nhỏ: mỗi hàm thực hiện một việc. Điều này giúp các hàm ngắn gọn, dễ sửa lỗi (nếu có). Ví dụ: thay vì viết hàm TimSoNhoNhatVaSapXepMang(), ta nên viết thành 2 hàm: TimSoNhoNhat() và SapXepMang(). Để gọi hàm, bạn chỉ cần ghi tên hàm và đưa vào các tham số: gia_tri_tra_ve = ten_ham(tham_so1, tham_so2, tham_so) Đừng lo lắng nếu cảm thấy cú pháp hàm khó hiểu. Đây là điều bình thường nếu bạn chưa bao giờ viết hàm. Hãy xem qua một số ví dụ sau và thực hành viết hàm, bạn sẽ dần hiểu rõ và sử dụng hàm dễ dàng hơn. 75 7.2 Một số ví dụ Ví dụ 7.1: Viết hàm in ra phí ship với tham số là độ dài quãng đường. def TinhPhiShip(quang_duong): ''' Ham tinh va in ra thong bao phi ship theo cong thuc: quang_duong (km) * 5000 (dong). Input: quang duong (km) Output: [khong co] ''' phi_ship = quang_duong*5000 print('Phi ship la:', phi_ship) TinhPhiShip(5) Trong ví dụ trên, để ý tên hàm TinhPhiShip() được đặt dễ hiểu (nên tránh những tên như: TPS(), ship(), phi() vì chúng khó hiểu). Hàm TinhPhiShip() nhận vào 1 tham số duy nhất quang_duong và không trả về (return) giá trị nào cả. Nó chỉ in ra kết quả tính toán bằng lệnh print(), nên khi bạn gọi hàm này sẽ nhận được thông báo: 'Phi ship la: …' Ví dụ 7.2: Viết hàm tính diện tích hình chữ nhật. Hàm nhận vào 2 tham số là chiều rộng và chiều dài của hình chữ nhật def DienTichHinhChuNhat(dai, rong): ''' Ham in ra dien tich hinh chu nhat. Input: do dai va do rong Output: [khong co] ''' dien_tich = dai*rong print('Dien tich hinh chu nhat:', dien_tich) DienTichHinhChuNhat(5, 20) 76 Hàm trên cũng không trả về giá trị nào. Nhưng khác với hàm TinhPhiShip() ở chỗ hàm này yêu cầu 2 tham số đầu vào. Nếu bạn chỉ truyền vào 1 tham số thì khi gọi hàm sẽ bị báo lỗi thiếu tham số. Ví dụ 7.3: Viết hàm tính diện tích hình tròn. Yêu cầu: trả về (return) diện tích hình tròn. def DienTichTron(ban_kinh): ''' Ham tra ve dien tich hinh tron. Input: ban kinh Output: dien tich ''' import math dien_tich = math.pi * ban_kinh**2 return dien_tich area = DienTichTron(5) print('Dien tich hinh tron:', area) Hàm trên nhận vào 1 tham số. Điểm khác biệt của nó so với các ví dụ trước là nó trả về (return) giá trị. Khi đó, lúc gọi hàm bạn cần có một biến để lưu lại giá trị trả về của hàm (trong ví dụ trên là biến area). Thực hành: Viết lại hàm DienTichHinhChuNhat() trả về diện tích hình chữ nhật. Ví dụ 7.4: Viết hàm tính diện tích của nhiều hình tròn. Hàm này nhận vào 1 tham số là 1 list chứa các bán kính của các hình tròn cần tính. def DienTichNhieuHinhTron(list_ban_kinh): ''' Ham tra ve list dien tich cua nhieu hinh tron. Input: list cac ban kinh Output: list cac dien tich ''' import numpy as np list_dien_tich = np.zeros(len(list_ban_kinh)) for i in range(len(list_ban_kinh)): 77 dien_tich = DienTichTron(list_ban_kinh[i]) list_dien_tich[i] = round(dien_tich,2) return list_dien_tich list_dien_tich = DienTichNhieuHinhTron([4, 2, 1, 7]) print('Dien tich cac hinh tron:', list_dien_tich) Trong code trên chúng ta sử dụng hàm np.zeros() để khởi tạo list_dien_tich: một numpy array chứa toàn phần tử là số 0, số lượng phần tử của list_dien_tich bằng với kích thước list_ban_kinh. Bạn có thể sử dụng cách này để khởi tạo mảng khi biết trước số lượng phần tử. Cách làm này giúp code hoạt động hiệu quả hơn. Để ý trong hàm trên chúng ta gọi hàm DienTichTron() để tính diện tích của 1 hình tròn. Nói cách khác, bạn có thể tùy ý gọi các hàm tự viết bên trong một hàm tự viết khác. Ví dụ 7.5: Cho một list các nhiệt độ thu được bằng cảm biến trong 1 thiết bị. Viết hàm nhận vào 1 tham số là list các nhiệt độ và trả về các nhiệt độ cao hơn 70 độ C. def LocNhietDoCao(list_temp): ''' Ham tra ve list cac nhiet do > 70. Input: list cac nhiet do Output: list cac nhiet do cao ''' list_high_temp = [] for temp in list_temp: if temp > 70: list_high_temp.append(temp) return list_high_temp list_nhiet_do = [48, 56, 72, 81, 64, 53, 78] print('Cac nhiet do cao:', LocNhietDoCao(list_nhiet_do)) 78 Thực hành: Sửa hàm trên để trả về nhiệt độ lớn hơn một ngưỡng bất kỳ (thay vì cố định là 70 độ C). Ngưỡng này là 1 tham số của hàm. Ví dụ 7.6: Cho một list các nhiệt độ thu được bằng cảm biến trong một thiết bị. Cứ mỗi phút thì ghi nhận một nhiệt độ. Viết hàm trả về những nhiệt độ lớn hơn 70 độ C và thời điểm (phút) xuất hiện những nhiệt độ này. Gợi ý: Mỗi phút ghi nhận một nhiệt độ tức là mỗi phút sẽ có 1 phần tử trong list nhiệt độ. Hay nói cách khác, index của các phần tử chính là thời điểm chúng được ghi nhận (index 0, 1, 2… tương ứng với phút thứ 0, 1, 3…). def LocNhietDoCao(list_temp): ''' Ham tra ve list nhiet do va indices cua cac nhiet do > 70. Input: list cac nhiet do Output: list nhiet do va ids cac nhiet do cao ''' list_high_temp = [] list_ids_high_temp = [] for i in range(len(list_temp)): if list_temp[i] > 70: list_ids_high_temp.append(i) list_high_temp.append(list_temp[i]) return list_high_temp, list_ids_high_temp list_nhiet_do = [48, 56, 72, 81, 64, 53, 78] list_temp, list_ids = LocNhietDoCao(list_nhiet_do) print('Cac nhiet do cao:', list_temp) print('Cac ids nhiet do cao:', list_ids) Để ý rằng hàm LocNhietDoCao() trả về hai giá trị: list_high_temp, list_ids_high_temp. Vì vậy khi gọi hàm chúng ta cũng cần có hai biến để lưu giá trị trả về (trong code trên ta dùng hai biến list_temp, list_ids). Các biến nhận giá trị trả về không nhất thiết phải cùng tên với biến ở dòng return trong hàm. 79 Thực hành: Chỉnh sửa hàm trên để trả về thời điểm xuất hiện các nhiệt độ nằm trong khoảng [temp_min, temp_max] với temp_min, temp_max là các tham số của hàm. Ví dụ temp_min = 50, temp_max = 80: hàm sẽ trả về index của các nhiệt độ nằm trong khoảng [50, 80]. Thực hành: Nhập đoạn mã sau và đặt tên là defined_function_demo.py. Sau đó hãy thực thi đoạn mã và cho biết kết quả in ra màn hình. def foo(): print('foo()') def calculate(val_a, val_b): val = val_a * val_b return val def perform(num): d = num * 5 return d, d + 5, d - 2 def fibonacci(n): if n == 0: return 0 elif n == 1: return 1 else: return fibonacci(n-1) + fibonacci(n-2) foo() m = calculate(10, 5) print(m) a, b, c = perform(5) print(a) print(b) print(c) res = fibonacci(10) print(res) 80 7.3 Biến đoạn code bất kỳ thành hàm Để biến một đoạn code bất kỳ thành hàm bạn cần xác định: ▪ ▪ Đầu vào của đoạn code này Giá trị trả về của đoạn code này Khi đó, bạn chỉ việc đưa chuyển các biến chứa đầu vào của đoạn code thành các tham số của hàm. Còn giá trị trả về thì sẽ đặt ở dòng return của hàm. Ví dụ 7.7: Cho đoạn code kiểm tra số N có phải số nguyên số không như sau. N = 7 i = 2 la_nguyen_to = True while i <= N/2: if N%i == 0: la_nguyen_to = False break i += 1 if la_nguyen_to: print(N, 'la so nguyen to.') else: print(N, 'khong phai so nguyen to.') Để biến code này thành một hàm kiểm tra số nguyên tố, ta để ý rằng biến N là đầu vào còn biến la_nguyen_to là đầu ra. Vì vậy chúng ta có thể biến đoạn code thành hàm như sau: def CheckPrime(N): ''' Ham kiem tra so nguyen duong N co phai so nguyen to khong. Input: so nguyen duong N Output: True neu N la so nguyen to, False neu nguoc lai. ''' i = 2 la_nguyen_to = True while i <= N/2: 81 if N%i == 0: la_nguyen_to = False break i += 1 return la_nguyen_to ket_qua = CheckPrime(7) if ket_qua: print(N, 'la so nguyen to.') else: print(N, 'khong phai so nguyen to.') Ví dụ 7.8: Cho đoạn code truy xuất mã OTP trong một tin nhắn. Hãy biến code này thành một hàm trích mã OTP từ tin nhắn. tin_nhan = 'Ma OTP cua ban la 46457 (ma co hieu luc trong 2 phut).' list_tu = tin_nhan.split() OTP = '' for tu in list_tu: if tu.isdigit() == True and len(tu)==5: OTP = tu print('Ma OTP:', OTP) Trong đoạn code trên, biến tin_nhan là đầu vào, còn biến OTP là đầu ra. Vì vậy ta có thể biến nó thành hàm như sau. def GetOTP(tin_nhan): list_tu = tin_nhan.split() OTP = '' for tu in list_tu: if tu.isdigit() == True and len(tu)==5: OTP = tu return OTP tin_nhan = 'Ma OTP cua ban la 46457 (ma co hieu luc trong 2 phut).' maOTP = GetOTP(tin_nhan) print('Ma OTP:', maOTP) 82 Bài tập có lời giải 1. Viết chương trình tính giá tiền dịch vụ taxi. Giá thành dịch vụ taxi bao gồm chi phí cơ sở là 4USD và cộng thêm 0.25USD cho mỗi 140m di chuyển. Viết một chương trình có đầu vào là quãng đường di chuyển (tính theo km) sau đó trả về giá dịch vụ cho quãng đường đó. Viết một chương trình chính (main) để minh họa hàm vừa xây dựng. Lưu ý: vì giá vé có thể thay đổi, hãy định nghĩa một hằng số qui định giá cơ sở để có thể linh hoạt cho các trường hợp giá thành tăng. 2. Viết chương trình tính trung vị của 3 điểm. Hãy định nghĩa một hàm nhận 3 tham số ngõ vào và trả về kết quả là trung vị của các điểm đã nhận. Viết chương trình chính (main) để đọc 3 giá trị này từ người dùng và hiển thị kết quả lên màn hình. 3. Số nguyên tố là số nguyên lớn hơn 1 và chỉ chia hết cho 1 và chính số đó. Viết một hàm với chức năng là kiểm tra xem tham số đầu vào của hàm đó có phải là số nguyên tố không. Hàm này sẽ tính toán và trả về True nếu đó là số nguyên tố và ngược lại thì trả về False. Viết chương trình chính (main) để đọc một số nguyên từ người dùng và hiển thị thông điệp chỉ ra số vừa nhập có phải là số nguyên tố không. 4. Định nghĩa một hàm nextPrime nhằm tìm và trả về kết quả là số nguyên tố xuất hiện đầu tiên mà lớn hơn số n. Giá trị số n sẽ được gán như là tham số ngõ vào duy nhất của hàm. Viết chương trình chính (main) để đọc một số nguyên từ người dùng và hiển thị số nguyên tố đầu tiên và lớn hơn số người dùng vừa nhập. 5. Viết chương trình kiểm tra xem mật khẩu được khai báo là đạt tính bảo mật cao hay không? Mật khẩu được định nghĩa là có tính bảo mật cao khi có ít nhất 8 ký tự đối với độ dài, chứa ít nhất 1 ký tự viết thường, một ký tự viết hoa, và một con số. Định nghĩa một hàm kiểm tra mật khẩu và hàm này sẽ trả về True nếu mật khẩu thỏa các điều kiện ở trên. Ngược lại thì hàm sẽ trả về False. Viết chương trình chính (main) để đọc mật khẩu từ người dùng và thông báo mật khẩu đó có tính bảo mật cao hay không. 83 6. Viết chương trình cho phép người dùng có thể chuyển đổi giá trị giữa các hệ thống số như hệ 2, hệ 10 và hệ 16. Nếu người dùng lựa chọn hệ thống số không phù hợp với các hệ mà chương trình hỗ trợ thì chương trình sẽ in ra 1 dòng thông báo. Hãy định nghĩa một số hàm chức năng khác nhau để thực hiện chuyển đổi giá trị từ sang hệ thập phân, và hệ thập phân sang nhị phân hoặc thập lục. Chương trình chính (main) sẽ đọc các hệ cơ bản và giá trị số muốn chuyển đổi từ người sử dụng. Bài tập thực hành 1. Viết hàm nhận vào một tham số là điện năng tiêu thụ mỗi ngày của một thiết bị. Hàm kiểm tra thiết bị này có tiết kiệm điện hay không và in ra thông báo. Biết rằng nếu điện năng tiêu thụ mỗi ngày < 10 kWh được gọi là tiết kiệm. 2. Viết hàm nhận vào một tham số là điện năng tiêu thụ mỗi ngày của một thiết bị. Hàm trả về số sao tiết kiệm năng lượng của thiết bị, với số sao tiết kiệm được quy định như sau: Điện năng tiêu thụ P (kWh) Số sao tiết kiệm năng lượng P<2 5 2≤P<4 4 4≤P<6 3 6 ≤ P < 10 2 P ≥ 10 1 3. Viết hàm nhận vào một tham số là điện năng tiêu thụ mỗi ngày của một thiết bị. Hàm in ra thông báo thiết bị có tiết kiệm điện không. Yêu cầu: hàm này gọi hàm trong câu 2 để tính số sao tiết kiệm. Thiết bị có số sao nhỏ hơn 3 được gọi là không tiết kiệm điện. 84 4. Viết hàm nhận vào hai tham số: một list chứa tốc độ quay của một động cơ nào đó (list số nguyên), và một giá trị min. Hàm trả về list các tốc độ quay nhỏ hơn min và indices của các tốc độ này. 5. Viết hàm giải phương trình bậc hai a𝑥 2 + 𝑏𝑥 + 𝑐 = 0, với a, b, c là các tham số của hàm. Hàm trả về list chứa các nghiệm (list rỗng nếu vô nghiệm). 6. Viết hàm tính cos(x) bằng Taylor series. Gợi ý: sử dụng vòng lặp while (xem nội dung tuần 3). Công thức khai triển Taylor của cos(x) xem tại: https://en.wikipedia.org/wiki/Taylor_series#Trigonometric_functi ons 7. Biến đoạn code viết hoa các chữ cái đầu từ trong tuần 7 thành một hàm nhận vào một string và trả về một string đã được viết hoa các chữ cái đầu từ. Ví dụ: “nGuyeN hiEu NGhiA” → “Nguyen Hieu Nghia”. 85 Chương 8 LỖI VÀ SỬA LỖI Khi học đến đây thì gần như chúng ta đã nắm đủ các thành phần căn bản của Python để có thể tự phát triển các chương trình từ đơn giản đến tương đối phức tạp. Vấn đề phát sinh là có thể xảy ra lỗi (bugs) làm chương trình bị đổ vỡ (crashed) hoặc hoạt động không chính xác như mong muốn của chúng ta. Khi đó, hiểu được các dạng lỗi và cách gỡ lỗi (debug) trở nên vô cùng cần thiết. 8.1 Các dạng lỗi trong lập trình Các lỗi trong lập trình thường được chia thành ba loại chính: ▪ Lỗi cú pháp (syntax errors): đây là dạng lỗi dễ xử lý nhất. Khi bạn viết code sai cú pháp, ví dụ thụt đầu dòng không thẳng hàng, thiếu dấu đóng ngoặc… thì sẽ tạo ra lỗi này. Dạng lỗi này được đa số các IDE, editor phát hiện cho bạn. Ví dụ trong VS Code, lỗi cú pháp được đánh dấu bằng đường răng cưa đỏ như hình sau (lỗi thụt đầu dòng ở dòng code thứ hai: ▪ Lỗi logic (logical errors): dạng lỗi này là code được viết sai thuật toán, sai logic, dẫn tới kết quả không đúng. Ví dụ khi viết code giải phương trình bậc 2, bạn ghi sai điều kiện: khi biệt số delta dương mà lại in ra thông báo phương trình vô nghiệm. Ví dụ khác là khi code tìm đường đi ngắn nhất, bạn đã ghi sai một lệnh tính toán quãng đường nào đó, dẫn tới kết quả trả về không phải đường đi ngắn nhất. Lỗi này dễ xuất hiện khi bạn viết các chương trình lớn, phức tạp hoặc code được viết bởi một nhóm và các mô tả hàm không rõ ràng, dẫn tới các thành viên cài đặt không đúng ý nhau. Để xử lý dạng lỗi này, chúng ta sử dụng các kỹ thuật gỡ lỗi (debug) như mô tả trong chương này. ▪ 86 ▪ Lỗi khi chạy (runtime errors, hay còn được gọi là exceptions): dạng lỗi này khá thú vị, đôi khi khó phát hiện, vì không phải lúc nào nó cũng xuất hiện khi bạn chạy một đoạn code. Hình dung bạn có một đoạn code viết đúng cú pháp, đúng logic, nhưng khi chạy code, có một vài lần chạy sẽ gặp lỗi, còn những lần chạy khác lại không bị. Ví dụ đoạn code sau: N = int(input('Nhap 1 so nguyen duong:')) N_nghich_dao = 1/N Đoạn code trên chỉ bị lỗi khi người dùng nhập N = 0 (còn khi người dùng nhập N khác 0 thì không có lỗi phát sinh). Xem thêm các ví dụ khác ở phần dưới. Trong các lỗi trên, lỗi cú pháp dễ xử lý. Bạn chỉ cần học cú pháp và viết đúng là được. Trường hợp bạn viết sai, IDE và code editor sẽ đánh dấu cho bạn. Đối với lỗi logic bạn cần dùng các kỹ thuật debug (xem mục 8.3). Còn lỗi runtime thì trong Python hỗ trợ cú pháp bắt lỗi try/except (xem mục kế tiếp). 8.2 Xử lý lỗi runtime Python cung cấp khối lệnh try/except để hỗ trợ quản lý các lệnh có nguy cơ gây ra lỗi runtime. Khi sử dụng try/except, nếu có lỗi runtime thì chương trình của bạn cũng sẽ không bị đổ vỡ và có thể được xử lý gọn gàng. Cú pháp của khối lệnh try/except: try: #Đặt các dòng lệnh có lỗi runtime ở đây except ten_exception1: #Lệnh xử lý exception 1 except ten_exception2: #Lệnh xử lý exception 2 except: #Lệnh xử lý các exception khác Trong cú pháp trên, khối try chứa các lệnh có thể bị lỗi runtime, các khối except bên dưới chứa các lệnh xử lý các exception. Tùy theo 87 exception xuất hiện mà các lệnh tương ứng sẽ được thực thi để xử lý. Số lượng các khối except là tùy ý (theo số lỗi runtime có thể xảy ra của các lệnh ở khối try), ngoại trừ khối except cuối cùng (khối except không đi kèm tên exception) thì chỉ xuất hiện tối đa một lần trong một khối try/except. Để hiểu rõ hơn về khối lệnh này chúng ta hãy xem các ví dụ bên dưới. Ví dụ 8.1: Cho một list chứa các nhiệt độ được đo hằng ngày (mỗi phần tử là nhiệt độ của một ngày: phần tử có index 0 là nhiệt độ ngày 0, phần tử có index 1 là nhiệt độ ngày 1…). Đoạn code bên dưới cho phép người dùng nhập vào 1 ngày (số nguyên dương) và in ra nhiệt độ của ngày đó. # Khi không dùng try/except: # Nếu người dùng nhập ngày lớn hơn 4 sẽ bị lỗi không có phần tử (index out of range) temperatures_each_day = [30, 29, 32, 34, 31] i = int(input('Enter the day:')) print('Temperature:', temperatures_each_day[i]) # Khi dùng try/except: # Nếu người dùng nhập ngày lớn hơn 4 sẽ hiện ra thông báo không tìm thấy dữ liệu và chương trình không bị đổ vỡ. temperatures_each_day = [30, 29, 32, 34, 31] i = int(input('Enter the day:')) try: print('Temperature:', temperatures_each_day[i]) except IndexError: print('Cannot find data for the input day. May try a smaller value for day.') Ví dụ 8.2: Đoạn code bên dưới cùng chức năng như ví dụ bên trên nhưng xử lý nhiều loại lỗi runtime hơn: lỗi index out of range, lỗi nhập liệu sai kiểu (yêu cầu số nhưng nhập chữ), và lỗi khác. 88 temperatures_each_day = [30, 29, 32, 34, 31] try: i = int(input('Enter the day:')) print('Temperature:', temperatures_each_day[i]) except IndexError: print('Cannot find data for the input day. May try a smaller value for day.') except ValueError: # Lỗi không nhập số print('Please enter a number for day.') except: # Lỗi khác print('An unknown error happened.') Ví dụ 8.3: Đoạn code sau cho phép người dùng (ví dụ nhân viên thu ngân) nhập liên tiếp giá sản phẩm cho đến khi người dùng không nhập nữa (để trống ô input và nhấn enter) thì sẽ dừng và trả tổng giá trị của đơn hàng. Trong code này, lỗi runtime có thể xuất hiện khi người dùng nhập sai kiểu dữ liệu (không nhập số). Khi đó chương trình có thể bị crashed và toàn bộ các giá sản phẩm đã nhập sẽ bị mất. Vì vậy, chúng ra cần dùng khối try/except để xử lý lỗi nhập liệu này. # Khi không dùng try/except: # Hãy thử nhập dữ liệu không phải số. sum_val = 0 while True: user_input = input('Enter an item price:') if user_input == '': print('Done.') break else: num = float(user_input) sum_val += num print('Current total amount:',sum_val) 89 # Khi dùng try/except: # Nếu bạn nhập dữ liệu không phải số sẽ nhận được thông báo nhập lại và chương trình vẫn hoạt động tiếp tục. sum_val = 0 while True: user_input = input('Enter an item price:') if user_input == '': print('Done.') break else: try: num = float(user_input) except ValueError: print('Please input a number.') continue sum_val += num print('Current total amount:',sum_val) 8.3 Xử lý lỗi logic Lỗi logic không thể được xử lý tự động như kiểu dùng khối try/except khi xử lý lỗi runtime. Các công cụ hỗ trợ debug của IDE có thể hỗ trợ bạn dò tìm lỗi dễ dàng hơn là ngồi đọc code thủ công bằng mắt. Phần bên dưới trình bày cách debug với VS Code. Đoạn code bên dưới được viết để tính giai thừa của một số nguyên dương N. Tuy nhiên đoạn code này chứa các lỗi logic. Chúng ta sẽ sử dụng tính năng Debug Cell của VS Code để dò tìm ra lỗi này. #%% Tính giai thừa N = int(input('Nhap so nguyen duong')) giai_thua = 0 i = 1 while i<=N: giai_thua *= N print('N! =', giai_thua) 90 Để kích hoạt tính năng Debug Cell bạn nhấn vào “Debug Cell” nằm ngay phía trên mỗi cell: Sau khi nhấn “Debug Cell”, VS Code sẽ chuyển code sang chế độ chạy từng dòng. Để ý trong hình bên dưới dòng 3 được highlight: đây chính là dòng lệnh sẽ được chạy tiếp theo. Để chạy lệnh này, bạn nhấn nút Step Over (F10) như hình dưới: Khi nhấn Step Over lệnh input sẽ được thực thi và yêu cầu nhập liệu. Giả sử ta nhập 5 như hình dưới: Sau khi Enter, VS Code sẽ quay lại cửa sổ lệnh và highlight lệnh tiếp theo: 91 Lúc này ta tiếp tục nhấn Step Over (F10) để chạy dòng lệnh 4. Khi chạy xong dòng 4 thì dòng 5 sẽ được highlight. Lúc này, nếu bạn rê chuột lên biến giai_thua sẽ thấy xuất hiện ô ghi giá trị hiện tại của biến (giá trị 0). Cứ tiếp tục thực hiện nhấn Step Over như trên ta sẽ lần lượt thấy các màn hình như sau: 92 93 Có thể thấy vòng lặp đang được thực thi. Tuy nhiên, sau khi nhấn Step Over 6 lần hoặc hơn, bạn vẫn thấy chương trình của chúng ta dừng ở vòng lặp. Đáng lẽ lệnh lặp này chỉ chạy 5 lần vì chúng ta đang tính N! với N = 5. Như vậy vòng lặp đang bị lặp quá nhiều lần. Kiểm tra giá trị của biến i (bằng cách rê chuột lên trên biến i) bạn sẽ thấy giá trị của nó luôn luôn là 1. Như vậy, vòng lặp đã thiếu dòng lệnh thay đổi giá trị cho i. Đây là một lỗi. Khi đã phát hiện ra lỗi, bạn dừng trình debug bằng cách nhấn Disconnect (Shift-F5) như hình sau: Lúc này trình debug sẽ dừng và bạn có thể chỉnh code. Giả sử chúng ta chỉnh code thành như sau: #%% Tính giai thừa N = int(input('Nhap so nguyen duong')) giai_thua = 0 i = 1 while i<=N: giai_thua *= N i += 1 print('N! =', giai_thua) Khi thực thi code này thì vòng lặp đã dừng sau 5 lần chạy. Tuy nhiên, giá trị của giai_thua lại bằng 0. Ta có thể thực hiện Debug Cell như đoạn trên và kiểm tra giá trị của biến giai_thua trong mỗi lần lặp thì thấy giá trị của nó không thay đổi (luôn là 0): 94 Lúc này bạn có thể phát giác là biến giai_thua đã được khởi tạo nhầm bằng 0 nên giá trị của nó khi nhân lên không thay đổi. Khi phát hiện lỗi này ta nhấn Disconnect (Shift-F5) và chỉnh sửa code như sau: #%% Tính giai thừa N = int(input('Nhap so nguyen duong')) giai_thua = 1 i = 1 while i<=N: giai_thua *= N i += 1 print('N! =', giai_thua) Thực hành: Code trên vẫn còn lỗi làm cho giá trị N! chưa được tính đúng. Hãy tiếp tục chạy debug để dò tìm và chỉnh sửa lỗi này. 8.4 Các lưu ý khi viết code để hạn chế lỗi Phần trên đã giới thiệu những cách phát hiện và xử lý lỗi. Tuy nhiên, tốt nhất vẫn là “phòng bệnh hơn chữa bệnh”. Dưới đây liệt kê một số kinh nghiệm đơn giản có thể giúp bạn hạn chế các lỗi khi lập trình: ▪ ▪ Đối với lỗi cú pháp: học kỹ cú pháp và thực hành viết code nhiều, tự nhiên bạn sẽ ít mắc và dễ dàng xử lý khi gặp dạng lỗi này. Đối với lỗi logic: bạn nên thực hiện 1. Chia nhỏ code: trước khi lập trình một bài toán lớn, bạn nên dành thời gian chia nhỏ các chức năng, thành phần của code và lên kế 95 ▪ 96 hoạch viết chúng có thứ tự để dễ dàng viết và kiểm thử trong quá trình viết. 2. Viết code sạch: đừng quên áp dụng các kinh nghiệm viết code sạch như đặt tên biến, tên hàm dễ hiểu, sử dụng comments để làm rõ code, viết các hàm nhỏ, mỗi hàm một chức năng. 3. Kiểm thử thường xuyên: đừng đợi đến lúc viết xong hết chương trình mới chạy thử và kiểm tra kết quả. Đặc biệt đối với những chương trình lớn, mỗi khi viết xong một đoạn hoặc một hàm, bạn nên chạy thử chúng để xem kết quả. Nếu có sai sót chúng ta sẽ dễ dàng điều chỉnh vì mỗi lần chỉ chỉnh 1 đoạn code hoặc 1 hàm. 4. Thực hành nhiều: muốn viết code giỏi hãy luyện tập viết code càng nhiều càng tốt. Tự bạn sẽ tích lũy được các kinh nghiệm mà đôi khi đọc cả quyển sách dày cũng không có được. Đối với lỗi runtime: chúng ta đơn giản chỉ cần dùng try/except. Tuy nhiên, vấn đề nằm ở chỗ bạn phải có kinh nghiệm để biết dòng code nào có thể sinh lỗi runtime hoặc phải có bộ test case đầy đủ để phát hiện các trường hợp này. Như vậy việc luyện tập viết code nhiều một lần nữa phát huy tác dụng trong việc cải thiện chất lượng lập trình của bạn. Bài tập thực hành 1. Đọc hiểu và viết lại đoạn code sau cho “clean” hơn (đổi tên hàm, tên biến, thêm chú thích...): def f1(a, b, c, d): e = [] for i in a: if i%b==0 and c<=i<=d: e.append(i) return e L1 = [2, 1, 4, -4, 6, 10, 5, 8] f2 = f1(L1, 2, 5, 10) print(f2) 2. Đọc hiểu và viết lại đoạn code sau cho “clean” hơn (sửa lời nhắn input, đổi tên biến, thêm chú thích...). Yêu cầu: Dùng try / except để bắt exeptions (run time errors). import numpy as np a = int(input('Nhap a:')) b = int(input('Nhap b:')) n = [] i = 0 while i<a: c = np.random.randint(0,100) if c%b==0: n.append(c) i += 1 print(n) 3. Tìm hiểu hàm isinstance(), viết code minh họa. 4. Đọc hiểu và tìm lỗi trong đoạn code kiểm tra số nguyên tố sau, biết rằng đoạn code phải trả về: 97 list2 = [True, False, False, True, False, True, True] # True nằm ở vị trí số nguyên tố Yêu cầu: Sử dụng break point. Sửa lỗi và viết lại code cho “clean” hơn. def kt(a): b = 1 while(b<a/2): if a%b == 0: return False return True def kt2(a): b = [] for c in a: b.append(kt(c)) return b list1 = [2, 1, 4, 7, 8, 19, 13] list2 = kt2(list1) print(list2) 98 Chương 9 VẼ VỚI MATPLOTLIB Chương này giới thiệu về thư viện matplotlib hỗ trợ các thao tác vẽ (plotting) trong Python. Bạn có thể sử dụng matplotlib để trực quan hóa dữ liệu hoặc đơn giản là vẽ các đồ thị hàm số cho mục đích minh họa. 9.1 Cách thức vẽ trên màn hình kỹ thuật số Màn hình kỹ thuật số (digital screens) hiển thị các hình ảnh kỹ thuật số (digital images). Về cơ bản, các hình ảnh kỹ thuật số là một mảng 2 chiều của các pixels, với mỗi pixel là một điểm ảnh. Như hình bên dưới1: ký tự ‘a’ và hình ảnh phóng to của nó cho thấy các pixels. Do đó, để vẽ hình ảnh kỹ thuật số, hoặc để hiển thị hình ảnh trên màn hình kỹ thuật số nói chung, chúng ta cần phải chấm liên tiếp các pixels này. Trong Python có một thư viện tên matplotlib cho phép chúng ta vẽ các hình ảnh bằng cách chấm các điểm ảnh lên cửa sổ vẽ. Sau đó, matplotlib sẽ nối các điểm ảnh này lại tạo thành hình ảnh cuối cùng. Hãy xem các ví dụ bên dưới. 1 Nguồn: https://pippin.gimp.org/image_processing/chap_dir.html 99 9.2 Vẽ với thư viện matplotlib Lưu ý: Cũng như các packages khác, trước khi sử dụng matplotlib, bạn cần cài đặt thư viện này sử dụng lệnh pip (xem mục 4.2). Sau khi cài đặt xong, bạn cần import các thư viện như sau để vẽ các hình bên dưới: import matplotlib.pyplot as plt import numpy as np Thư viện tên matplotlib cho phép chúng ta vẽ các hình ảnh bằng cách chấm các điểm ảnh lên cửa sổ vẽ. Sau đó, matplotlib sẽ nối các điểm ảnh này lại. Ví dụ: để vẽ một đoạn thẳng, chúng ta cần chấm 2 điểm đầu mút của nó như hình dưới: Sau đó matplotlib sẽ tự nối 2 điểm này lại tạo thành đoạn thẳng như sau: 100 Đoạn code tối giản để vẽ ra đoạn thẳng ở trên là1: x = np.array([2, 7]) y = np.array([1, 9]) plt.plot(x,y) plt.show() Trong code trên, x, y là 2 mảng chứa tọa độ của các điểm đầu mút, x là mảng chứa các hoành độ x, còn y là mảng chứa các tung độ y. Với 2 điểm có tọa độ (2, 1) và (7, 9) thì mảng x, y tương ứng là: [2, 7] và [1, 9]. Lưu ý rằng thứ tự các điểm quyết định hình ảnh được vẽ ra. Xem ví dụ sau. Lưu ý rằng đoạn code trên là đoạn code được đơn giản hóa để giúp bạn nắm bắt được ý chính của việc vẽ hình với matplotlib. Do đó, bạn đừng lo ngại nếu hình ảnh được vẽ ra bằng đoạn code này không giống hoàn toàn với hình minh họa. Để vẽ ra hình giống như hình minh họa, bạn thêm đoạn code sau vào dưới lệnh plt.plot(): 1 plt.xticks(np.arange(0,11,1)) plt.yticks(np.arange(0,11,1)) plt.gca().set_aspect('equal') plt.grid() Các mục sau của chương này sẽ dần dần giải thích đoạn code trên. 101 Ví dụ 9.1: Vẽ 2 đoạn thẳng liên tiếp. #%% Code 01: x = np.array([2, 8, 4]) y = np.array([1, 3, 9]) plt.plot(x,y) plt.show() #%% Code 02: x = np.array([2, 4, 8]) y = np.array([1, 9, 3]) plt.plot(x,y) plt.show() Khi thực thi các đoạn code trên, ta thu được kết quả của Code 01 là: 102 Còn kết quả của Code 02 là: Để ý rằng 3 điểm ảnh đầu mút của 2 đoạn thẳng này là như nhau: (2,1), (8,3), (4,9). Tuy nhiên, do mảng x, y chứa các điểm này với thứ tự khác nhau nên sinh ra các đoạn thẳng khác nhau. Thực hành: Hãy vẽ hình tam giác như bên dưới. Gợi ý: Bạn cần thêm điểm đầu vào cuối mảng x, y (tức là mảng x, y bây giờ sẽ chứa 4 điểm) để vẽ thêm đoạn thẳng thứ 3. 103 Thực hành: Viết code vẽ hình như sau: Ví dụ 9.2: Vẽ đồ thị hàm số y = 3x2 + 4x + 9. x = np.arange(-4, 3, 0.1) y = 3*x**2 + 4*x + 9 plt.plot(x,y) plt.show() Trong đoạn code trên chúng ta sử dụng hàm np.arange() (từ thư viện numpy) để tạo ra mảng tọa độ x với giá trị trải đều từ -4 đến 3, bước nhảy 0.1 (tức tạo ra được 70 tọa độ x: -4, -3.9, -3.8, -3.7…). Khi đó, lệnh tính giá trị tọa độ y: y = 3*x**2 + 4*x + 9 sẽ sinh ra các tọa độ y tương ứng với các tọa độ x sử dụng phương trình của hàm số y = 3x2 + 4x + 9. Khi thực thi đoạn code ta sẽ thu được kết quả đồ thị như sau: 104 Để hiểu rõ hơn về cách vẽ đồ thị trên của matplotlib, chúng ta thử điều chỉnh đoạn code vẽ ở trên thành như sau: x = np.arange(-4, 3, 2) y = 3*x**2 + 4*x + 9 plt.plot(x,y) plt.show() So với đoạn code trước, đoạn code này chỉ thay đổi ở bước nhảy của hàm np.arange() dùng tạo tọa độ x từ 0.1 thành 2. Như vậy số tọa độ x được sinh ra sẽ giảm từ 70 tọa độ xuống còn 4 tọa độ: -4, -2, 0, 2. Vì vậy, khi tính tọa độ y ta cũng chỉ thu được 4 tọa độ y tương ứng. Do đó, đồ thị sinh ra chỉ còn là hình nối của 4 điểm ảnh như hình sau: 105 Thực hành: Vẽ đồ thị hàm số y = cos(5x + π). 9.3 Vẽ đồ thị trong tọa độ cực Mục trên trình bày cách vẽ với matplotlib. Điểm cốt yếu để vẽ là tạo ra các tọa độ của điểm ảnh cần vẽ trong hệ tọa độ Descartes. Để vẽ trong hệ tọa độ cực, bạn chỉ cần thêm một bước chuyển đổi tọa độ từ hệ tọa độ cực thành tọa độ Descartes. Xem ví dụ sau. 106 Ví dụ 9.3: Vẽ hình xoắn ốc y = 0.5θ. theta = np.arange(0, 4*np.pi ,0.1) r = 0.5*theta x = r*np.cos(theta) y = r*np.sin(theta) plt.plot(x,y) plt.show() Trong đoạn code trên, 2 dòng lệnh: theta = np.arange(0, 4*np.pi ,0.1) r = 0.5*theta tạo ra tọa độ (θ, r) của các điểm ảnh cần vẽ trong tọa độ cực. Hai dòng lệnh sau x = r*np.cos(theta) y = r*np.sin(theta) chuyển đổi từ tọa độ cực (θ, r) thành tọa độ Descartes (x, y). Phần còn lại là của code tương đồng với các đoạn code vẽ ở trên. Kết quả đồ thị thu được như sau: 107 Thực hành: Thay đổi các giá trị bước nhảy và giá trị đầu cuối của hàm np.arange() tạo tọa độ θ để xem sự thay đổi của đồ thị được vẽ. Thực hành: Vẽ đồ thị hàm số r = cos(4, θ). 9.4 Tùy chỉnh hình vẽ Matplotlib cung cấp nhiều tùy chỉnh giúp bạn thể hiện ảnh được vẽ theo ý muốn, ví dụ thay đổi màu sắc, thay đổi nét vẽ, thu phóng hình. Mục này sẽ giới thiệu một số tùy chỉnh thường dùng. Các tùy chỉnh khác bạn có thể tìm thấy trên trang web của matplotlib https://matplotlib.org/. 108 Để điều chỉnh màu của nét vẽ chúng ta dùng thuộc tính color của hàm plot() như ví dụ sau: x = np.arange(-4, 3, .1) y = 3*x**2 + 4*x + 9 plt.plot(x,y, color = 'green') plt.show() Lưu ý: Matplotlib cung cấp sẵn một số màu cơ bản bằng các từ tiếng Anh như red, green, blue, black. Bạn có thể sử dụng màu hex để chỉ định một màu tùy ý, ví dụ: x = np.arange(-4, 3, .1) y = 3*x**2 + 4*x + 9 plt.plot(x,y, color = '#04a900') plt.show() 109 Để thay đổi độ dày của nét vẽ, ta dùng thuộc tính linewidth: x = np.arange(-4, 3, .1) y = 3*x**2 + 4*x + 9 plt.plot(x,y, linewidth=4) plt.show() Lưu ý: Bạn có thể kết hợp các tùy chỉnh với nhau để tạo ra nét vẽ như ý muốn. Ví dụ để chỉnh màu và độ dày nét vẽ ta kết hợp thuộc tính color và linewidth của hàm plot(): 110 x = np.arange(-4, 3, .1) y = 3*x**2 + 4*x + 9 plt.plot(x,y, color = '#04a900', linewidth=4) plt.show() Để điều chỉnh giới hạn của tọa độ vẽ, ta dùng phương thức axis() với cú pháp: plt.axis([xmin, xmax, ymin, ymax]) Ví dụ: x = np.arange(-4, 3, .1) y = 3*x**2 + 4*x + 9 plt.plot(x,y) plt.axis([-6, 4, 0, 50]) plt.show() Để điều chỉnh cho tỉ lệ của trục tung và hoành bằng nhau, ta dùng lệnh: plt.axis('equal') Ví dụ 9.4: Vẽ hình tròn khi không dùng axis('equal'). 111 theta = np.arange(0, 2*np.pi ,0.01) r = 5 x = r*np.cos(theta) y = r*np.sin(theta) plt.plot(x,y) plt.show() Khi thực thi đoạn code trên sẽ thu được kết quả như hình dưới: Trong hình trên, do các trục tọa độ có tỷ lệ không đồng nhất nên hình tròn được vẽ ra trông giống như hình ellipse. Khi dùng axis('equal') sẽ cho kết quả tròn đều như hình bên dưới. theta = np.arange(0, 2*np.pi ,0.01) r = 5 x = r*np.cos(theta) y = r*np.sin(theta) plt.axis('equal') plt.plot(x,y) plt.show() 112 Để thêm lưới tọa độ, ta dùng: plt.grid(True) Ví dụ 9.5: theta = np.arange(0, 2*np.pi ,0.01) r = 5 x = r*np.cos(theta) y = r*np.sin(theta) plt.axis('equal') plt.plot(x,y) plt.grid(True) plt.show() 113 Trong trường hợp vẽ nhiều hình trên cùng một cửa sổ vẽ (figure), bạn có thể dùng phương thức legend() để thêm chú thích. Xem ví dụ bên dưới. Ví dụ 9.6: Vẽ 2 vòng tròn với bán kính khác nhau. theta = np.arange(0, 2*np.pi ,0.01) r = 4 x = r*np.cos(theta) y = r*np.sin(theta) plt.plot(x,y) r = 5 x = r*np.cos(theta) y = r*np.sin(theta) plt.plot(x,y) plt.legend(['r1 = 4','r2 = 5']) plt.axis('equal') plt.grid(True) plt.show() 114 Để thêm chú thích cho trục x, trục y và tên cho hình vẽ, ta dùng lần lượt các phương thức sau: plt.xlabel() plt.ylabel() plt.title() theta = np.arange(0, 2*np.pi ,0.01) r = 4 x = r*np.cos(theta) y = r*np.sin(theta) plt.plot(x,y) r = 5 x = r*np.cos(theta) y = r*np.sin(theta) plt.plot(x,y) plt.xlabel('Trục x') 115 plt.ylabel('Trục y') plt.title('Hình tròn đồng tâm') plt.legend(['r1 = 4','r2 = 5']) plt.axis('equal') plt.grid(True) plt.show() 9.5 Vẽ trên nhiều phân vùng với subplotlib Để tách một cửa sổ vẽ (figure) thành nhiều phân vùng, ta có thể dùng hàm subplotlib với cú pháp như sau: subplot(so_hang, so_cot, vi_tri) Trong đó so_hang và so_cot là số lượng hàng và cột cần chia tách, còn vị trí là số thứ tự của ô cần vẽ. Xem các ví dụ sau. 116 vi_tri = 1 vi_tri = 2 vi_tri = 3 vi_tri = 4 Để chia cửa sổ vẽ thành 4 ô như hình trên (2 hàng, 2 cột) và vẽ vào ô ở vị trí thứ nhất (ô có gạch chéo), ta dùng lệnh: subplot(2,2,1) Để chia cửa sổ vẽ thành 4 ô như hình dưới (2 hàng, 3 cột) và vẽ vào ô ở vị trí thứ 5 (ô có gạch chéo), ta dùng lệnh: subplot(2,3,5) vi_tri = 1 vi_tri = 2 vi_tri = 3 vi_tri = 4 vi_tri = 5 vi_tri = 6 Để chia cửa sổ vẽ thành 4 ô như hình dưới (2 hàng, 3 cột) và vẽ vào ô ở vị trí thứ 5 (ô có gạch chéo), ta dùng lệnh: subplot(2,3,(3, 6)) vi_tri = 1 vi_tri = 2 vi_tri = (3, 6) vi_tri = 4 vi_tri = 5 117 Ví dụ 9.8: Đoạn code sau vẽ 3 đồ thị của các hàm số trên các phân vùng khác nhau. Để ý cách dùng hàm subplot(): phải được đặt trước khi gọi hàm plot(). Ngoài ra lưu ý chỉ để lệnh show() sau khi plot hình cuối cùng. # Plot a rose plt.subplot(2,2,1) a= 4 n= 4 theta = np.arange(0,4*np.pi,0.01) r = a * np.cos(n*theta) x = r*np.cos(theta) y = r*np.sin(theta) plt.plot(x,y) # Plot a spiral plt.subplot(2,2,2) a = 2 theta = np.arange(0, 4*np.pi, 0.1) r = a*theta x = r*np.cos(theta) y = r*np.sin(theta) plt.plot(x,y) # Plot a parabola plt.subplot(2,2,(3,4)) x = np.arange(-6, 5, 0.2) y = 5*x**2 + 4*x - 1 plt.plot(x,y) plt.show() Kết quả khi chạy đoạn code trên: 118 119 Bài tập thực hành 1. Vẽ đồ thị hàm bậc 5: 𝑦 = 3𝑥 5 + 20𝑥 4 – 10𝑥 3 – 240𝑥 2 – 250𝑥 + 200 Yêu cầu: giống như hình về màu, style, đầy đủ xlabel, ylabel, title, và axis có khoảng giá trị như hình. 2. Vẽ các đồ thị hàm số sau, với phương trình được cho trong legend. Yêu cầu: đơn vị trên trục Ox, Oy bằng nhau, giống như hình sau về màu, style, có đầy đủ legend, grid. 120 3. Tìm hiểu hàm plt.bar() và viết code minh họa vẽ một biểu đồ cột tùy ý. 4. Tìm hiểu hàm plt.hist() và viết code minh họa vẽ một histogram của một dữ liệu tùy ý. 5. Tìm hiểu vẽ 3D và vẽ hyperboloid có phương trình −0.3𝑥 2 − 0.3𝑦 2 + 𝑧 2 = 1. Lưu ý: + Biến đổi phương trình của hàm số để có giá trị của z theo x, y. + Dùng colormap ‘jet’. + Trong các hàm ax.plot_surface(): ghi thêm tham số vmin=-5, vmax=5 để có dãy màu như hình. 121 PHỤ LỤC Mã nguồn bài tập có lời giải chương 2 Bài 1: Viết chương trình kiểm tra ký tự người dùng nhập là nguyên âm hay phụ âm. Lưu ý chương trình này chỉ được sử dụng để nhận đầu vào là các ký tự thường không viết hoa. Để xử lý các ký tự viết hoa hãy thêm vào các giá trị so sánh đối với chữ viết hoa. # Read a letter from the user letter = input("Enter a letter: ") # Classify the letter and report the result if letter == "a" or letter == "e" or \ letter == "i" or letter == "o" or \ letter == "u": print("It’s a vowel.") elif letter == "y": print("Sometimes it’s a vowel... Sometimes it’s a consonant.") else: print("It’s a consonant.") Bài 2: Viết chương trình xác định số ngày của một tháng cho trước # Display the number of days in a month. # Read the month name from the user month = input("Enter the name of a month: ") # Compute the number of days in the month days = 31 if month == "April" or month == "June" or \ month == "September" or month == "November": days = 30 122 elif month == "February": days = "28 or 29" # Display the result print(month, "has", days, "days in it.") Bài 3: Viết chương trình chuyển đổi điểm chữ thành điểm số. Lưu ý hàm letter = letter.upper() dùng để chuyển đổi bất kỳ ký tự thường nào được nhập từ người dùng trở thành ký tự hoa và lưu lại vào cùng biến. # Convert from a letter grade to a number of grade points. A = 4.0 A_MINUS = 3.7 B_PLUS = 3.3 B = 3.0 B_MINUS = 2.7 C_PLUS = 2.3 C = 2.0 C_MINUS = 1.7 D_PLUS = 1.3 D = 1.0 F = 0 INVALID = -1 # Read the letter grade from the user letter = input("Enter a letter grade: ") letter = letter.upper() # Convert from a letter grade to a number of grade points using -1 grade points as a sentinel 123 # value indicating invalid input if letter == "A+" or letter == "A": gp = A elif letter == "A-": gp = A_MINUS elif letter == "B+": gp = B_PLUS elif letter == "B": gp = B elif letter == "B-": gp = B_MINUS elif letter == "C+": gp = C_PLUS elif letter == "C": gp = C elif letter == "C-": gp = C_MINUS elif letter == "D+": gp = D_PLUS elif letter == "D": gp = D elif letter == "F": gp = F else: gp = INVALID # Report the result if gp == INVALID: print("That wasn’t a valid letter grade.") else: 124 print("A(n)", letter, "is equal to", gp, "grade points.") Bài 4: Viết chương trình đánh giá hiệu quả làm việc của nhân viên # Report whether an employee’s performance is unacceptable, acceptable or meritorious based on the rating entered by the user. RAISE_FACTOR = 2400.00 UNACCEPTABLE = 0 ACCEPTABLE = 0.4 MERITORIOUS = 0.6 # Read the rating from the user rating = float(input("Enter the rating: ")) # Classify the performance if rating == UNACCEPTABLE: performance = "Unacceptable" elif rating == ACCEPTABLE: performance = "Acceptable" elif rating >= MERITORIOUS: performance = "Meritorious" else: performance = "" # Report the result if performance == "": print("That wasn’t a valid rating.") else: print("Based on that rating, your performance is %s." % performance) 125 print("You will receive a raise of $%.2f." % (rating * RAISE_FACTOR)) Bài 5: Viết chương trình xác định năm nhuận # Read the year from the user year = int(input("Enter a year: ")) # Determine if it is a leap year if year % 400 == 0: isLeapYear = True elif year % 100 == 0: isLeapYear = False elif year % 4 == 0: isLeapYear = True else: isLeapYear = False # Display the result if isLeapYear: print(year, "is a leap year.") else: print(year, "is not a leap year.") 126 Mã nguồn bài tập có lời giải chương 3 Bài 1: Viết chương trình tính chu vi của một đa giác # Compute the perimeter of a polygon constructed from points entered by the user. A blank line will be entered for the x-coordinate to indicate that all of the points have been entered. from math import sqrt # Store the perimeter of the polygon perimeter = 0 # Read the coordinate of the first point first_x = float(input("Enter the first x-coordinate: ")) first_y = float(input("Enter the first y-coordinate: ")) # Provide initial values for prev x and prev y prev_x = first_x prev_y = first_y # Read the remaining coordinates line = input("Enter the next x-coordinate (blank to quit): ") while line != "": # Convert the x-coordinate to a number and read the y coordinate x = float(line) y = float(input("Enter the next y-coordinate: ")) # Compute the distance to the previous point and add it to the perimeter 127 dist = sqrt((prev_x - x) ** 2 + (prev_y - y) ** 2) perimeter = perimeter + dist # Set up prev x and prev y for the next loop iteration prev_x = x prev_y = y # Read the next x-coordinate line = input("Enter the next x-coordinate (blank to quit): ") # Compute the distance from the last point to the first point and add it to the perimeter dist = sqrt((first_x - x) ** 2 + (first_y - y) ** 2) perimeter = perimeter + dist # Display the result print("The perimeter of that polygon is", perimeter) Bài 2: Viết chương trình tính bit parity của một nhóm 8 bit nhị phân được nhập bởi người dùng sử dụng parity chẵn. # Compute the parity bit using even parity for sets of 8 bits entered by the user. # Read the first line of input line = input("Enter 8 bits: ") # Continue looping until a blank line is entered while line != "": # Ensure that the line has a total of 8 zeros and ones and exactly 8 characters 128 if line.count("0") + line.count("1") != 8 or len(line) != 8: # Display an appropriate error message print("That wasn’t 8 bits... Try again.") else: # Count the number of ones ones = line.count("1") # Display the parity bit if ones % 2 == 0: print("The parity bit should be 0.") else: print("The parity bit should be 1.") # Read the next line of input line = input("Enter 8 bits: ") Bài 3: Viết chương trình chuyển đổi số từ hệ thập phân sang nhị phân. # Convert a number from decimal (base 10) to binary (base 2). NEW_BASE = 2 # Read the number to convert from the user num = int(input("Enter a non-negative integer: ")) # Generate the binary representation of num, storing it in result result = "" q = num # Perform the body of the loop once 129 r = q % NEW_BASE result = str(r) + result q = q // NEW_BASE # Keep on looping until q is 0 while q > 0: r = q % NEW_BASE result = str(r) + result q = q // NEW_BASE # Display the result print(num, "in decimal is", result, "in binary.") 130 Mã nguồn bài tập có lời giải chương 5 Bài 1: Chương trình sẽ đọc một chuỗi từ người dùng sau đó sẽ dịch chuỗi gồm các ký tự và số thành mã Morse. # Python program to implement Morse Code Translator ''' VARIABLE KEY 'cipher' -> 'stores the morse translated form of the english string' 'decipher' -> 'stores the english translated form of the morse string' 'citext' -> 'stores morse code of a single character' 'i' -> 'keeps count of the spaces between morse characters' 'message' -> 'stores the string to be encoded or decoded' ''' # Dictionary representing the morse code chart MORSE_CODE_DICT = { 'A':'.-', 'B':'-...', 'C':'-.-.', 'D':'-..', 'E':'.', 'F':'..-.', 'G':'--.', 'H':'....', 'I':'..', 'J':'.---', 'K':'-.-', 'L':'.-..', 'M':'--', 'N':'-.', 'O':'---', 'P':'.--.', 'Q':'--.-', 'R':'.-.', 'S':'...', 'T':'-', 'U':'..-', 'V':'...-', 'W':'.--', 'X':'-..-', 'Y':'-.--', 'Z':'--..', '1':'.----', '2':'..---', '3':'...--', '4':'....-', '5':'.....', '6':'-....', '7':'--...', '8':'---..', '9':'----.', 131 '0':'-----', ', ':'--..--', '.':'.-.-.-', '?':'..--..', '/':'-..-.', '-':'-....-', '(':'-.--.', ')':'-.--.-'} # Function to encrypt the string # according to the morse code chart def encrypt(message): cipher = '' for letter in message: if letter != ' ': # Looks up the dictionary and adds the # corresponding morse code # along with a space to separate # morse codes for different characters cipher += MORSE_CODE_DICT[letter] + ' ' else: # 1 space indicates different characters # and 2 indicates different words cipher += ' ' return cipher # Function to decrypt the string # from morse to english def decrypt(message): # extra space added at the end to access the # last morse code message += ' ' 132 decipher = '' citext = '' for letter in message: # checks for space if (letter != ' '): # counter to keep track of space i = 0 # storing morse code of a single character citext += letter # in case of space else: # if i = 1 that indicates a new character i += 1 # if i = 2 that indicates a new word if i == 2 : # adding space to separate words decipher += ' ' else: # accessing the keys using their values (reverse of encryption) decipher += list(MORSE_CODE_DICT.keys())[list(MORSE_CODE_DICT .values()).index(citext)] 133 citext = '' return decipher # Hard-coded driver function to run the program def main(): message = "GEEKS-FOR-GEEKS" result = encrypt(message.upper()) print (result) message = "--. . . -.- ... -....- ..-. --- .-. ....- --. . . -.- ... " result = decrypt(message) print (result) # Executes the main function if __name__ == '__main__': main() 134 Mã nguồn bài tập có lời giải chương 7 Bài 1: Viết chương trình tính giá taxi #The start point of the program def main(): #Get the information of the distance traveled by the customer distanceTraveled=int(input("The distance traveled by the customer (in KM): ")) #calculate the taxi fare taxiFare=calculateTaxiFare(distanceTraveled) #display it to the passenger for the payment to be received. print(f"The taxi fare is: ${taxiFare}") #This function allows to calculate the taxi fare def calculateTaxiFare(distanceTraveled): #The taxi fare consists of a base fare of RM 4.00 #plus charges of RM 0.25 for every 140 meters traveled. return 4.00+(((distanceTraveled*1000)//140)*0.25) main() Bài 2: Tính trung vị của 3 điểm ## Compute statements the median of three values using if # @param a the first value # @param b the second value # @param c the third value # @return the median of values a, b and c 135 def median(a, b, c): if a < b and b < c or a > b and b > c: return b if b < a and a < c or b > a and a > c: return a if c < a and b < c or c > a and b > c: return c ## Compute the median of three values using the min and max functions and a little bit of # arithmetic # @param a the first value # @param b the second value # @param c the third value # @return the median of values a, b and c def alternateMedian(a, b, c): return a + b + c - min(a, b, c) - max(a, b, c) # Display the median of 3 values entered by the user def main(): x = float(input("Enter the first value: ")) y = float(input("Enter the second value: ")) z = float(input("Enter the third value: ")) print("The median value is:", median(x, y, z)) print("Using the alternative method, it is:", \ alternateMedian(x, y, z)) # Call the main function main() 136 Bài 3: Chương trình xác định số nguyên tố. ## Determine whether or not a number is prime # @param n the integer to test # @return True if the number is prime, False otherwise def isPrime(n): if n <= 1: return False # Check each number from 2 up to but not including n to see if it divides evenly into n for i in range(2, n): if n % i == 0: return False return True # Determine if a number entered by the user is prime def main(): value = int(input("Enter an integer: ")) if isPrime(value): print(value, "is prime.") else: print(value, "is not prime.") # Call the main function if the file has not been imported if __name__ == "__main__": main() Bài 4: Xác định số nguyên tố liền kề sau số n def next_prime(n: int) -> int: if n < 0: 137 raise ValueError('Negative numbers can not be primes') # Base case if n <= 1: return 2 # For i as every odd number between n + 1 and n + 200 for i in range(n + 1 + (n % 2), n + 200, 2): # For every odd number from 3 to i (3 because we covered base case) for j in range(3, i, 2): # If remained is equals to 0 if not i % j: # break current loop break # If loop j didn't break [nobreak: ] else: return i raise RuntimeError('Failed to compute next prime number :c') def main(): while True: try: num = int(input('Enter positive number: ')) print(f'Next prime is: {next_prime(num)}') break except ValueError: print('Please enter a positive integer!') if __name__ == '__main__': main() 138 Bài 5: Chương trình kiểm tra tính bảo mật của mật khẩu # Check whether or not a password is good. A good password is at least 8 characters and # contains an uppercase letter, a lowercase letter and a number. # @param password the password to check # @return True if the password is good, False otherwise def checkPassword(password): has_upper = False has_lower = False has_num = False # Check each character in the password and see which requirement it meets for ch in password: if ch >= "A" and ch <= "Z": has_upper = True elif ch >= "a" and ch <= "z": has_lower = True elif ch >= "0" and ch <= "9": has_num = True # If the password has all 4 properties if len(password) >= 8 and has_upper and has_lower and has_num: return True # The password is missing at least one property return False # Demonstrate the password checking function 139 def main(): p = input("Enter a password: ") if checkPassword(p): print("That’s a good password.") else: print("That isn’t a good password.") # Call the main function only if the file has not been imported into another program if __name__ == "__main__": main() Bài 6: Chương trình chuyển đổi giá trị giữa các hệ thống số # Convert a number from one base to another. Both the source base and the destination base must be between 2 and 16. from hex_digit import * ## Convert a number from base 10 to base new base # @param num the base 10 number to convert # @param new base the base to convert to # @return the string of digits in new base def dec2n(num, new_base): # Generate the representation of num in base new base, storing it in result result = "" q = num # Perform the body of the loop once r = q % new_base result = int2hex(r) + result q = q // new_base 140 # Continue looping until q is 0 while q > 0: r = q % new_base result = int2hex(r) + result q = q // new_base # Return the result return result ## Convert a number from base b to base 10 # @param num the base b number, stored in a string # @param b the base of the number to convert # @return the base 10 number def n2dec(num, b): decimal = 0 # Process each digit in the base b number for i in range(len(num)): decimal = decimal * b decimal = decimal + hex2int(num[i]) # Return the result return decimal # Convert a number between two arbitrary bases def main(): # Read the base and number from the user from_base = int(input("Base to convert from (216): ")) if from_base < 2 or from_base > 16: print("Only bases between 2 and 16 are supported.") print("Quitting...") quit() 141 from_num = input("Sequence of digits in that base: ") # Convert to base 10 and display the result dec = n2dec(from_num, from_base) print("That’s %d in base 10." % dec) # Convert to the new base and display the result to_base = int(input("Enter the base to convert to (2-16): ")) if to_base < 2 or to_base > 16: print("Only bases between 2 and 16 are supported.") print("Quitting...") quit() to_num = dec2n(dec, to_base) print("That's %s in base %d." % (to_num, to_base)) # Call the main function main() 142 Keywords của Python1 1 Keyword Mô tả and Phép và as Tạo tên thay thế assert Dành cho debug break Dừng vòng lặp class Định nghĩa lớp continue Chạy vòng lặp tiếp theo def Định nghĩa hàm del Xóa một đối tượng elif Dùng trong khối lệnh if else Dùng trong khối lệnh if except Dùng trong khối lệnh try/except False Giá trị false finally Dùng trong khối lệnh try/except for Vòng lặp for from Dùng trong lệnh import global Khai báo biến global if Dùng trong khối lệnh if Nguồn: https://www.w3schools.com/python/python_ref_keywords.asp 143 144 import Lệnh import in Kiểm tra xem phần tử có nằm trong mảng không is Kiểm tra xem 2 biến có bằng nhau không lambda Tạo hàm lambda None Giá trị null nonlocal Khai báo biến non-local not Phép toán logic or Phép toán logic pass Lệnh null (không thực thi gì cả) raise Tạo một exception return Trả về giá trị trong hàm True Giá trị true try Dùng trong lệnh try/except while Tạo vòng lặp while with Khối try/except đơn giản yield Tạo ra giá trị cho hàm Cài đặt các thuật toán sắp xếp bằng Python Nguồn: https://realpython.com/sorting-algorithms-python/ # THUẬT TOÁN SẮP XẾP NỔI BỌT (BUBBLE SORT): def bubble_sort(array): n = len(array) for i in range(n): # Create a flag that will allow the function to terminate early if there's nothing left to sort already_sorted = True # Start looking at each item of the list one by one, comparing it with its adjacent value. With each iteration, the portion of the array that you look at shrinks because the remaining items have already been sorted. for j in range(n - i - 1): if array[j] > array[j + 1]: # If the item you're looking at is greater than its adjacent value, then swap them array[j], array[j+1] = array[j + 1], array[j] # Since you had to swap two elements, set the `already_sorted` flag to `False` so the algorithm doesn't finish prematurely already_sorted = False # If there were no swaps during the last iteration, the array is already sorted, and you can terminate 145 if already_sorted: break return array # THUẬT TOÁN SẮP XẾP CHÈN (INSERTION SORT): def insertion_sort(array): # Loop from the second element of the array until the last element for i in range(1, len(array)): # This is the element we want to position in its correct place key_item = array[i] # Initialize the variable that will be used to find the correct position of the element referenced by `key_item` j = i - 1 # Run through the list of items (the left portion of the array) and find the correct position of the element referenced by `key_item`. Do this only if `key_item` is smaller than its adjacent values. while j >= 0 and array[j] > key_item: # Shift the value one position to the left and reposition j to point to the next element (from right to left) array[j + 1] = array[j] j -= 1 # When you finish shifting the elements, you can position `key_item` in its correct location 146 array[j + 1] = key_item return array # THUẬT TOÁN SẮP XẾP TRỘN (MERGE SORT): def merge(left, right): # If the first array is empty, then nothing needs to be merged, and you can return the second array as the result if len(left) == 0: return right # If the second array is empty, then nothing needs to be merged, and you can return the first array as the result if len(right) == 0: return left result = [] index_left = index_right = 0 # Now go through both arrays until all the elements make it into the resultant array while len(result) < len(left) + len(right): # The elements need to be sorted to add them to the resultant array, so you need to decide whether to get the next element from the first or the second array if left[index_left] <= right[index_right]: result.append(left[index_left]) index_left += 1 else: result.append(right[index_right]) index_right += 1 147 # If you reach the end of either array, then you can add the remaining elements from the other array to the result and break the loop if index_right == len(right): result += left[index_left:] break if index_left == len(left): result += right[index_right:] break return result # THUẬT TOÁN SẮP XẾP NHANH (QUICK SORT): from random import randint def quicksort(array): # If the input array contains fewer than two elements, then return it as the result of the function if len(array) < 2: return array low, same, high = [], [], [] # Select your `pivot` element randomly pivot = array[randint(0, len(array) - 1)] for item in array: # Elements that are smaller than the `pivot` go to the `low` list. Elements that are larger than `pivot` go to the `high` list. Elements that are equal to `pivot` go to the `same` list. 148 if item < pivot: low.append(item) elif item == pivot: same.append(item) elif item > pivot: high.append(item) # The final result combines the sorted `low` list with the `same` list and the sorted `high` list return quicksort(low) + same + quicksort(high) # THUẬT TOÁN TIMSORT: def timsort(array): min_run = 32 n = len(array) # Start by slicing and sorting small portions of the input array. The size of these slices is defined by your `min_run` size. for i in range(0, n, min_run): insertion_sort(array, i, min((i + min_run 1), n - 1)) # Now you can start merging the sorted slices. Start from `min_run`, doubling the size on each iteration until you surpass the length of the array. size = min_run while size < n: # Determine the arrays that will be merged together for start in range(0, n, size * 2): 149 # Compute the `midpoint` (where the first array ends and the second starts) and the `endpoint` (where the second array ends) midpoint = start + size - 1 end = min((start + size * 2 - 1), (n-1)) # Merge the two subarrays. # The `left` array should go from `start` to `midpoint + 1`, while the `right` array should go from `midpoint + 1` to `end + 1`. merged_array = merge( left=array[start:midpoint + 1], right=array[midpoint + 1:end + 1]) # Finally, put the merged array back into your array array[start:start+len(merged_array)] = merged_array # Each iteration should double the size of your arrays size *= 2 return array 150 Cài đặt các thuật toán tìm kiếm trên mảng bằng Python Nguồn: https://www.geeksforgeeks.org/searching-algorithms/#algo # THUẬT TOÁN TÌM KIẾM TUYẾN TÍNH (LINEAR SEARCH) # If x is present then return its location, otherwise return -1 def search(arr, n, x): for i in range(0, n): if (arr[i] == x): return i return -1 # Driver Code arr = [2, 3, 4, 10, 40] x = 10 n = len(arr) # Function call result = search(arr, n, x) if(result == -1): print("Element is not present in array") else: print("Element is present at index", result) # THUẬT TOÁN TÌM KIẾM NHỊ PHÂN (BINARY SEARCH) # Returns index of x in arr if present, else -1 def binarySearch(arr, l, r, x): # Check base case if r >= l: 151 mid = l + (r - l) // 2 # If element is present at the middle itself if arr[mid] == x: return mid # If element is smaller than mid, then it can only be present in left subarray elif arr[mid] > x: return binarySearch(arr, l, mid-1, x) # Else the element can only be present in right subarray else: return binarySearch(arr, mid+1, r, x) else: # Element is not present in the array return -1 # Driver Code arr = [2, 3, 4, 10, 40] x = 10 # Function call result = binarySearch(arr, 0, len(arr)-1, x) if result != -1: print("Element is present at index % d" % result) else: print("Element is not present in array") # THUẬT TOÁN TÌM KIẾM NHẢY (JUMP SEARCH) # This code is contributed by "Sharad_Bhardwaj". import math def jumpSearch( arr , x , n ): # Finding block size to be jumped step = math.sqrt(n) 152 # Finding the block where element is present (if it is present) prev = 0 while arr[int(min(step, n)-1)] < x: prev = step step += math.sqrt(n) if prev >= n: return -1 # Doing a linear search for x in block beginning with prev. while arr[int(prev)] < x: prev += 1 # If we reached next block or end element is not present. of array, if prev == min(step, n): return -1 # If element is found if arr[int(prev)] == x: return prev return -1 # Driver code to test function arr = [ 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610 ] x = 55 n = len(arr) 153 # Find the index of 'x' using Jump Search index = jumpSearch(arr, x, n) # Print the index where 'x' is located print("Number" , x, "is at index" ,"%.0f"%index) # THUẬT TOÁN TÌM KIẾM NỘI SUY (INTERPOLATION SEARCH) # This code is contributed by Hardik Jain # If x is present in arr[0..n-1], then returns index of it, else returns -1. def interpolationSearch(arr, lo, hi, x): # Since array is sorted, an element present in array must be in range defined by corner if (lo <= hi and x >= arr[lo] and x <= arr[hi]): # Probing the position with keeping distribution in mind. uniform pos = lo + ((hi - lo) // (arr[hi] - arr[lo]) * (x - arr[lo])) # Condition of target found if arr[pos] == x: return pos # If x is larger, x is in right subarray if arr[pos] < x: return interpolationSearch(arr, pos + 1, hi, x) # If x is smaller, x is in left subarray 154 if arr[pos] > x: return interpolationSearch(arr, lo, pos - 1, x) return -1 # Driver code # Array of items in which search will be conducted arr = [10, 12, 13, 16, 18, 19, 20, 21, 22, 23, 24, 33, 35, 42, 47] n = len(arr) # Element to be searched x = 18 index = interpolationSearch(arr, 0, n - 1, x) if index != -1: print("Element found at index", index) else: print("Element not found") # THUẬT TOÁN TÌM KIẾM HÀM MŨ (EXPONENTIAL SEARCH): # This code is contributed by Harshit Agrawal # A recursive binary search function returns location of x in given array arr[l..r] is present, otherwise -1 def binarySearch( arr, l, r, x): if r >= l: mid = l + ( r-l ) // 2 155 # If the element is present at the middle itself if arr[mid] == x: return mid # If the element is smaller than mid, it can only be present in the left subarray then if arr[mid] > x: return binarySearch(arr, l, mid - 1, x) # Else he element can only be present in the right return binarySearch(arr, mid + 1, r, x) # We reach here if the element is not present return -1 # Returns the position of first occurrence of x in array def exponentialSearch(arr, n, x): # IF x is present at first location itself if arr[0] == x: return 0 # Find range for binary search j by repeated doubling i = 1 while i < n and arr[i] <= x: i = i * 2 156 # Call binary search for the found range return binarySearch( arr, i // 2, min(i, n-1), x) # Driver Code arr = [2, 3, 4, 10, 40] n = len(arr) x = 10 result = exponentialSearch(arr, n, x) if result == -1: print ("Element not found in the array") else: print ("Element is present at index %d" %(result)) 157 Cài đặt các thuật toán tìm kiếm trên chuỗi bằng Python # THUẬT TOÁN NAIVE PATTERN SEARCHING # Nguồn: https://www.geeksforgeeks.org/naivealgorithm-for-pattern-searching/ # This code is contributed by PrinciRaj1992 def search(pat, txt): M = len(pat) N = len(txt) # A loop to slide pat[] one by one */ for i in range(N - M + 1): j = 0 # For current index i, check # for pattern match */ while(j < M): if (txt[i + j] != pat[j]): break j += 1 if (j == M): print("Pattern found at index ", i) # Driver's Code if __name__ == '__main__': txt = "AABAACAADAABAAABAA" pat = "AABA" # Function call 158 search(pat, txt) # THUẬT TOÁN KMP # Nguồn: https://www.geeksforgeeks.org/kmp-algorithmfor-pattern-searching/ # This code is contributed by Bhavya Jain def KMPSearch(pat, txt): M = len(pat) N = len(txt) # create lps[] that will hold the longest prefix suffix # values for pattern lps = [0]*M j = 0 # index for pat[] # Preprocess the pattern (calculate lps[] array) computeLPSArray(pat, M, lps) i = 0 # index for txt[] while (N - i) >= (M - j): if pat[j] == txt[i]: i += 1 j += 1 if j == M: print ("Found pattern at index " + str(i-j)) j = lps[j-1] 159 # mismatch after j matches elif i < N and pat[j] != txt[i]: # Do not match lps[0..lps[j-1]] characters, # they will match anyway if j != 0: j = lps[j-1] else: i += 1 def computeLPSArray(pat, M, lps): len = 0 # length of the previous longest prefix suffix lps[0] # lps[0] is always 0 i = 1 # the loop calculates lps[i] for i = 1 to M-1 while i < M: if pat[i]== pat[len]: len += 1 lps[i] = len i += 1 else: # This is tricky. Consider the example. # AAACAAAA and i = 7. The idea is similar # to search step. if len != 0: len = lps[len-1] 160 # Also, note that we do not increment i here else: lps[i] = 0 i += 1 txt = "ABABDABACDABABCABAB" pat = "ABABCABAB" KMPSearch(pat, txt) 161 INDEX Biến, 16 break, 58 cell, 12 chuỗi, 81 cửa sổ interactive, 13 đạo văn, 11 debug, 105 dictionary, 72 function, 89 hàm, 88 index, 44 index âm, 44 index không âm, 44 Lệnh if, 22 list, 44 lỗi, 105 mảng, 43 Numpy, 62 numpy array, 62 package, 61 Python, 1 set, 69 slicing, 45 string, 81 tập hợp, 69 thư viện, 61 từ điển, 72 tuple, 48 vòng lặp, 35 vòng lặp for, 50 vòng lặp while, 35 162 TÀI LIỆU THAM KHẢO [1] Kong, Q., Siauw, T., & Bayen, A. M. (2021). Python Programming and Numerical Methods - A Guide for Engineers and Scientists. Elsevier Inc. [2] Swaroop, H. (2019). A Byte of Python. [3] Python Software Foundation. (2022). Python documentation. https://docs.python.org/3/ [4] Hill, C. (2020). Learning scientific programming with Python. Cambridge University Press. [5] Stephenson, B. (2019). The Python Workbook 2nd. Springer. [6] Pine, D. J. (2019). Introduction to Python for science and engineering. CRC Press. [7] NumPy Developers. (2022). https://numpy.org/doc/stable/reference/ NumPy manual. [8] Kurniawan, A. (2015). Python Programming by Example. PE Press. 163 Giáo trình Lập trình Python căn bản Trần Nhật Quang, Phạm Văn Khoa Trường Đại học Sư phạm Kỹ thuật Thành phố Hồ Chí Minh NHÀ XUẤT BẢN ĐẠI HỌC QUỐC GIA THÀNH PHỐ HỒ CHÍ MINH Trụ sở: Phòng 501, Nhà Điều hành ĐHQG-HCM, P. Linh Trung, TP Thủ Đức, TP.HCM. ĐT: 028 62726361 E-mail: vnuhp@vnuhcm.edu.vn Website: www.vnuhcmpress.edu.vn Chịu trách nhiệm xuất bản và nội dung TS ĐỖ VĂN BIÊN Biên tập LÊ THỊ MINH HUỆ Sửa bản in THANH HÀ Trình bày bìa TRƯỜNG ĐẠI HỌC SƯ PHẠM KỸ THUẬT THÀNH PHỒ HỒ CHÍ MINH Đối tác liên kết TRƯỜNG ĐẠI HỌC SƯ PHẠM KỸ THUẬT THÀNH PHỒ HỒ CHÍ MINH Xuất bản lần thứ 1. Số lượng in: 250 cuốn, khổ 16 x 24cm. Số XNĐKXB: 1508-2023/CXBIPH/6-24/ĐHQGTPHCM. QĐXB số: 88/QĐNXB cấp ngày 19/5/2023. In tại: Công ty TNHH In & Bao bì Hưng Phú. Địa chỉ: 162A/1, KP1A, phường An Phú, TP Thuận An, tỉnh Bình Dương. Nộp lưu chiểu: Năm 2023. ISBN: 978-604-73-9874-4. Bản quyền tác phẩm đã được bảo hộ bởi Luật Xuất bản và Luật Sở hữu trí tuệ Việt Nam. Nghiêm cấm mọi hình thức xuất bản, sao chụp, phát tán nội dung khi chưa có sự đồng ý của tác giả và Nhà xuất bản. ĐỂ CÓ SÁCH HAY, CẦN CHUNG TAY BẢO VỆ TÁC QUYỀN! NXB ĐHQG-HCM ISBN: 978-604-73-9874-4 9 786047 398744