Mục lục bài họcĐang ở d07-b2
← IGCSE Computer Science
0/18 bài đã học xong
Chương 7 · Programming · Bài 2/2 của chương · bài 14/18 của IGCSE Computer Science

Arrays, subroutines and files

Mảng, chương trình con và tệp
← Mục lục bài học
Lý thuyết · English

Arrays

An array stores many values of the same data type under one name, each reached by an index.

A one-dimensional array is a list: $\text{Marks}[1]$, $\text{Marks}[2]$, … A two-dimensional array is a table with rows and columns: $\text{Grid}[3,5]$ means row $3$, column $5$.

Why arrays matter: without one, storing $100$ marks needs $100$ separately named variables, and there is no way to process them in a loop. With an array, one loop handles all of them:

$\textbf{FOR } i \leftarrow 1 \textbf{ TO } 100$

$\quad \text{total} \leftarrow \text{total} + \text{Marks}[i]$

$\textbf{NEXT } i$

That is the real reason arrays exist: they make the index a variable, so a loop can walk through the data.

Procedures and functions

procedurea named block of code that carries out a task and does not return a value
functiona named block of code that returns a single value to the point where it was called

The distinction is exactly that: a function returns a value; a procedure does not.

Because a function returns a value, it can be used inside an expression: $\text{x} \leftarrow \text{Square}(5) + 2$. A procedure cannot — it is called as a statement on its own.

A parameter is a value passed into a subroutine so it can work on different data each time. $\text{Square}(5)$ and $\text{Square}(9)$ use the same code with a different parameter.

Why subroutines are worth using

  • reuse — write once, call from many places, instead of copying the code;
  • easier testing — each subroutine is tested on its own;
  • easier maintenance — a change is made in one place, not in every copy;
  • shorter, clearer programs — the main program reads as a list of named steps;
  • teamwork — different people write different subroutines at the same time.

Library routines are ready-written subroutines supplied with the language — $\text{ROUND}$, $\text{LENGTH}$, $\text{RANDOM}$. They save time and are already tested and reliable.

File handling

Programs store data in files so it survives after the program closes. Without a file, everything in memory is lost.

The sequence is always the same:

  1. open the file, stating whether to read or write;
  2. read from it or write to it;
  3. close it.

Closing matters more than it looks. Data being written is held in a temporary buffer in memory and only pushed out to disk when the file is closed. Forgetting to close means the data may never reach the disk — and again, nothing reports this.

Closing also releases the file so other programs can use it.

Giải thích tiếng Việt

Mảng. Mảng lưu nhiều giá trị cùng một kiểu dữ liệu dưới một tên duy nhất, mỗi giá trị được truy cập bằng một chỉ số.

Mảng một chiều là một danh sách: $\text{Diem}[1]$, $\text{Diem}[2]$, … Mảng hai chiều là một bảng có hàng và cột: $\text{Luoi}[3,5]$ nghĩa là hàng $3$, cột $5$.

Vì sao mảng quan trọng: không có mảng, lưu $100$ điểm cần $100$ biến tên khác nhau, và không có cách nào xử lý chúng bằng vòng lặp. Với mảng, một vòng lặp lo hết:

$\textbf{FOR } i \leftarrow 1 \textbf{ TO } 100$

$\quad \text{tong} \leftarrow \text{tong} + \text{Diem}[i]$

$\textbf{NEXT } i$

Đó mới là lý do thật sự mảng tồn tại: nó biến chỉ số thành một biến, nhờ đó vòng lặp đi được qua toàn bộ dữ liệu.

Thủ tục và hàm.

  • Thủ tục — một khối lệnh có tên, thực hiện một nhiệm vụ và không trả về giá trị nào;
  • Hàm — một khối lệnh có tên, trả về một giá trị cho chỗ đã gọi nó.

Khác biệt đúng là ở đó: hàm trả về một giá trị; thủ tục thì không.

Vì hàm trả về giá trị, nó dùng được bên trong một biểu thức: $\text{x} \leftarrow \text{BinhPhuong}(5) + 2$. Thủ tục thì không — nó được gọi như một lệnh đứng riêng.

Tham số là một giá trị truyền vào chương trình con để nó làm việc trên dữ liệu khác nhau mỗi lần. $\text{BinhPhuong}(5)$ và $\text{BinhPhuong}(9)$ dùng cùng một đoạn mã với tham số khác nhau.

Vì sao nên dùng chương trình con.

  • dùng lại — viết một lần, gọi từ nhiều chỗ, thay vì chép đi chép lại đoạn mã;
  • dễ kiểm thử — mỗi chương trình con được kiểm riêng;
  • dễ bảo trì — muốn sửa thì sửa ở một chỗ, không phải sửa từng bản sao;
  • chương trình ngắn và rõ hơn — chương trình chính đọc như một danh sách các bước có tên;
  • làm việc nhóm — nhiều người viết các chương trình con khác nhau cùng lúc.

Hàm thư viện là các chương trình con viết sẵn kèm theo ngôn ngữ — $\text{ROUND}$, $\text{LENGTH}$, $\text{RANDOM}$. Chúng tiết kiệm thời gian và đã được kiểm thử kỹ nên đáng tin.

Làm việc với tệp. Chương trình lưu dữ liệu vào tệp để dữ liệu còn lại sau khi chương trình đóng. Không có tệp, mọi thứ trong bộ nhớ đều mất.

Trình tự luôn giống nhau:

  1. mở tệp, nêu rõ để đọc hay để ghi;
  2. đọc từ nó hoặc ghi vào nó;
  3. đóng nó lại.

Việc đóng tệp quan trọng hơn vẻ ngoài của nó. Dữ liệu đang được ghi nằm tạm trong một vùng đệm trong bộ nhớ và chỉ được đẩy xuống đĩa khi tệp được đóng. Quên đóng nghĩa là dữ liệu có thể không bao giờ tới được đĩa — và một lần nữa, không có gì báo về chuyện đó.

Đóng tệp cũng giải phóng nó để chương trình khác dùng được.

Hàm TRẢ VỀ giá trị — thủ tục thì KHÔNG THỦ TỤC VeKhungTranh() làm xong việc rồi thôi gọi như một LỆNH đứng riêng HÀM BinhPhuong(5) → 25 TRẢ VỀ một giá trị dùng được TRONG BIỂU THỨC x ← BinhPhuong(5) + 2 Làm việc với tệp — ba bước, không bỏ bước nào 1. MỞ 2. ĐỌC / GHI 3. ĐÓNG ← hay quên Dữ liệu ghi nằm tạm trong VÙNG ĐỆM, chỉ xuống ĐĨA khi ĐÓNG tệp. Quên đóng = dữ liệu có thể MẤT SẠCH, và không có gì báo.
Quên đóng tệp là lỗi im lặng: chương trình chạy xong bình thường, nhưng tệp trên đĩa rỗng hoặc thiếu.
Ví dụ — vì sao dùng mảng thay vì nhiều biến rời

Giải thích vì sao một chương trình xử lý điểm của $100$ học sinh nên dùng mảng thay vì $100$ biến riêng lẻ, và nêu khác biệt giữa mảng một chiều và mảng hai chiều.

Giải.

Vấn đề với $100$ biến riêng lẻ.

Khó khăn một — phải khai báo $100$ tên. Lập trình viên phải viết $\text{diem1}$, $\text{diem2}$, … cho tới $\text{diem100}$. Riêng phần khai báo đã dài $100$ dòng.

Khó khăn hai, và đây mới là điểm chí mạng — không thể dùng vòng lặp.

Muốn cộng tổng, phải viết $100$ dòng: $\text{tong} \leftarrow \text{diem1} + \text{diem2} + \dots$

Vòng lặp không giúp được gì, vì mỗi biến có một tên khác nhau và không có cách nào để vòng lặp “đoán” ra tên biến tiếp theo.

Khó khăn ba — không mở rộng được. Nếu năm sau có $120$ học sinh, phải sửa khắp nơi trong chương trình.

Mảng giải quyết thế nào.

Mảng lưu tất cả dưới một tên duy nhất, và mỗi giá trị được truy cập bằng một chỉ số: $\text{Diem}[1]$, $\text{Diem}[2]$, …

Điểm mấu chốt: chỉ số có thể là một biến. Nhờ đó một vòng lặp đi được qua toàn bộ dữ liệu:

$\textbf{FOR } i \leftarrow 1 \textbf{ TO } 100$

$\quad \text{tong} \leftarrow \text{tong} + \text{Diem}[i]$

$\textbf{NEXT } i$

Ba dòng thay cho $100$ dòng. Và muốn đổi sang $120$ học sinh thì chỉ sửa một con số.

Đó là lý do thật sự mảng tồn tại: nó biến chỉ số thành một biến.

Mảng một chiều và mảng hai chiều.

Mảng một chiều là một danh sách — chỉ cần một chỉ số: $\text{Diem}[7]$ là điểm của học sinh thứ $7$.

Mảng hai chiều là một bảng có hàng và cột — cần hai chỉ số: $\text{Diem}[7, 3]$ là điểm của học sinh thứ $7$ ở môn thứ $3$.

Khi nào cần loại hai chiều: khi mỗi phần tử có nhiều giá trị liên quan. Điểm của $100$ học sinh ở $5$ môn hợp thành một bảng $100 \times 5$, và một mảng hai chiều mô tả nó gọn hơn hẳn năm mảng một chiều rời rạc.

Duyệt mảng hai chiều cần hai vòng lặp lồng nhau: vòng ngoài đi theo hàng, vòng trong đi theo cột.

Ví dụ — thủ tục, hàm và việc đóng tệp

(a) Nêu khác biệt giữa thủ tục và hàm kèm ví dụ. (b) Nêu ba lợi ích của việc dùng chương trình con. (c) Mô tả ba bước làm việc với tệp và giải thích vì sao bước đóng tệp lại quan trọng.

Giải.

(a) Thủ tục và hàm.

Thủ tục là một khối lệnh có tên, thực hiện một nhiệm vụ rồi không trả về giá trị nào.

Ví dụ: $\text{VeKhungTranh}()$ — nó vẽ xong cái khung rồi thôi. Không có kết quả nào để mang về.

Hàm là một khối lệnh có tên, trả về một giá trị cho chỗ đã gọi nó.

Ví dụ: $\text{BinhPhuong}(5)$ trả về $25$.

Hệ quả thực tế của khác biệt này. Vì hàm trả về giá trị, nó dùng được bên trong một biểu thức:

$\text{x} \leftarrow \text{BinhPhuong}(5) + 2$

Thủ tục không làm được vậy — nó được gọi như một lệnh đứng riêng một dòng, vì không có giá trị nào để đặt vào biểu thức.

Cả hai đều nhận tham số: giá trị truyền vào để chương trình con làm việc trên dữ liệu khác nhau mỗi lần.

(b) Ba lợi ích của chương trình con.

Dùng lại. Viết một lần, gọi từ nhiều chỗ. Không phải chép đi chép lại cùng một đoạn mã.

Dễ bảo trì. Đây là hệ quả quan trọng nhất của việc dùng lại: khi cần sửa, chỉ sửa một chỗ. Nếu chép mã ra $10$ chỗ thì phải nhớ sửa cả $10$ — và chỉ cần bỏ sót một chỗ là có lỗi.

Dễ kiểm thử. Mỗi chương trình con được kiểm riêng, nên khi có lỗi ta biết ngay lỗi nằm ở phần nào.

(Hai lợi ích nữa: chương trình chính ngắn và rõ hơn vì đọc như danh sách các bước có tên; và nhiều người làm song song các chương trình con khác nhau.)

(c) Ba bước làm việc với tệp.

Bước 1 — mở tệp, nêu rõ mở để đọc hay để ghi.

Bước 2 — đọc dữ liệu từ tệp, hoặc ghi dữ liệu vào tệp.

Bước 3 — đóng tệp.

Vì sao bước đóng tệp quan trọng.

Lý do một — dữ liệu có thể chưa xuống đĩa. Khi chương trình ghi, dữ liệu không đi thẳng xuống đĩa. Nó nằm tạm trong một vùng đệm trong bộ nhớ, và chỉ được đẩy xuống đĩa khi tệp được đóng.

Quên đóng nghĩa là dữ liệu trong vùng đệm có thể mất sạch khi chương trình kết thúc.

Điều làm nó nguy hiểm: chương trình chạy xong hoàn toàn bình thường, không báo lỗi gì cả. Chỉ khi mở tệp ra xem mới phát hiện nó rỗng hoặc thiếu dữ liệu.

Lý do hai — giải phóng tệp. Tệp đang mở thường bị khoá, nên chương trình khác không dùng được. Đóng tệp trả nó lại cho hệ thống.

Bẫy hay mất điểm — Nói thủ tục trả về một giá trị. Khác biệt giữa hai thứ này chỉ nằm ở đúng một điểm: hàm trả về một giá trị cho chỗ đã gọi nó, còn thủ tục thì không. Cách kiểm nhanh và chắc: hỏi “gọi nó xong thì có nhận lại được thứ gì để dùng tiếp không?” $\text{BinhPhuong}(5)$ đưa về $25$ nên nó là hàm, và ta viết được $\text{x} \leftarrow \text{BinhPhuong}(5) + 2$. $\text{VeKhungTranh}()$ vẽ xong rồi thôi, không đưa về gì cả — nó là thủ tục, và nó chỉ được gọi như một lệnh đứng riêng một dòng. Bẫy thứ hai, thuộc loại im lặng nên nguy hiểm hơn: quên đóng tệp sau khi ghi. Dữ liệu ghi ra không đi thẳng xuống đĩa mà nằm tạm trong vùng đệm trong bộ nhớ, và chỉ được đẩy xuống đĩa khi tệp được đóng. Quên đóng thì chương trình vẫn chạy xong bình thường, không báo lỗi gì — nhưng tệp trên đĩa rỗng hoặc thiếu.
Phải nhớ — Mảng lưu nhiều giá trị cùng kiểu dưới một tên; giá trị của nó nằm ở chỗ chỉ số là một biến, nhờ đó vòng lặp đi được qua toàn bộ dữ liệu. Mảng một chiều là danh sách, mảng hai chiều là bảng cần hai chỉ sốhai vòng lặp lồng nhau. Hàm trả về một giá trị nên dùng được trong biểu thức; thủ tục không. Tham số là giá trị truyền vào để cùng một đoạn mã làm việc trên dữ liệu khác nhau. Chương trình con cho dùng lại, dễ kiểm thử, dễ bảo trì. Với tệp: mở → đọc/ghi → đóng, và quên đóng có thể mất sạch dữ liệu mà không báo gì.

Đọc xong rồi — làm thử ngay

Bài tập của chương Programming gồm 14 câu trắc nghiệm và 5 đề tự luận. Đáp án hiện ngay khi chọn, miễn phí.

Làm bài tập chương →