Site icon Yellow Code Books

Java Bài 9: Câu Lệnh Lặp

Java Bài 9: Câu Lệnh Lặp

Hacker using laptop. Lots of digits on the computer screen.

Advertisements

Được chỉnh sửa ngày 06/12/2022.

Chào mừng các bạn đến với bài học Java số 9, bài học về các câu lệnh lặp trong Java. Bài học này nằm trong chuỗi bài học lập trình ngôn ngữ Java của Yellow Code Books.

Bài trước chúng ta đã làm quen với phần một của các câu lệnh điều khiển luồng (control flow), đó là các câu lệnh điều kiện (hay câu lệnh rẽ nhánh). Bài hôm nay chúng ta đi tiếp phần còn lại đó là các câu lệnh lặp.

Khái Niệm Lặp

Lặp (tiếng Anh gọi là loop) trong lập trình là một hành động lặp đi lặp lại một khối lệnh nào đó khi mà một điều kiện nào đó còn thỏa (thỏa – hay còn hiểu là kết quả của biểu thức đó là true). Nếu như với các câu lệnh điều kiện giúp bạn rẽ nhánh các dòng code, thì các câu lệnh lặp bài này lại giúp bạn lặp lại các dòng code nào đó.

Mình ví dụ có một yêu cầu bắt bạn in ra màn hình 1000 con số từ 1 đến 1000, chẳng lẽ bạn lại gọi 1000 lần câu lệnh System.out.println()?

Ví dụ thực tế hơn, nếu như có yêu cầu muốn bạn in ra tên tất cả sinh viên của trường bạn (giả sử bạn đã biết câu lệnh đọc một thông tin sinh viên lên từ cơ sở dữ liệu), chẳng lẽ bạn lại viết code đọc dữ liệu của từng sinh viên và in ra màn hình?

Các ví dụ trên cho chúng ta thấy khái niệm thực tế rõ ràng và sự cần thiết khi sử dụng đến các câu lệnh lặp trong bài hôm nay.

Các Câu Lệnh Lặp

Chúng ta có 3 loại câu lệnh lặp cần làm rõ trong bài hôm nay, đó là: while, do whilefor.

while

Cú pháp cho câu lệnh while như sau.

while (điều_kiện_lặp) {
     các_câu_lệnh;
}

Cú pháp của câu lệnh while khá đơn giản, ban đầu chương trình sẽ kiểm tra điều_kiện_lặp, nếu điều kiện này trả về kết quả true, thì các_câu_lệnh trong khối lệnh của while sẽ được thực thi, rồi sau đó chương trình sẽ lại kiểm tra điều_kiện_lặp. Vòng lặp while chỉ được kết thúc khi điều_kiện_lặp trả về kết quả false.

Ví dụ.

int i = 0;
while (i < 10) {
    System.out.println("Hello!");
    i++;
}

Bạn thấy ví dụ trên đây phải khởi tạo biến i (người ta gọi đây là biến đếm, vì đây là biến dùng để điều khiển số lần lặp của vòng while). Bắt đầu vào while, bạn thấy điều_kiện_lặp được đưa ra là nếu biến i còn nhỏ hơn 10 thì các_câu_lệnh bên trong được thực hiện, ở ví dụ này chỉ là hàm in ra màn hình câu chào “Hello!”. Bạn chú ý một điều, trong thân hàm while bạn luôn phải thay đổi giá trị của biến đếm, trong ví dụ này i++; giúp tăng biến đếm lên 1 đơn vị, để sao cho đến một lúc nào đó điều_kiện_lặp phải bị phá vỡ, trường hợp này là i bằng 10, thì hàm while mới kết thúc.

Nếu lấy can đảm bỏ dòng i++; ở ví dụ trên đi rồi chạy lại, bạn sẽ thấy dòng in ra màn hình được gọi mãi mãi, người ta gọi trường hợp này là lặp vô tận.

Bài Thực Hành Số 1

Bạn thử áp dụng vòng lặp while để thực hiện yêu cầu sau: hãy in ra console tổng các số chẵn từ dãy số nguyên có độ lớn từ 1 đến 10.

Gợi ý: bạn có thể xác định một số là chẵn bằng cách thực hiện phép chia dư số đó với 2, nếu kết quả phép chia dư là 0 thì đó là số chẵn.

Bạn hãy thử code, rồi so sánh với kết quả sau nhé.

int i = 1;
int sumEven = 0;
while (i <= 10) {
    if (i % 2 == 0)
        sumEven += i;
    i++;
}
 
System.out.println("Sum: " + sumEven);

Bài Thực Hành Số 2

Hãy dùng vòng lặp while để tìm ra các số nguyên tố trong dãy số nguyên từ 1 đến 100 và in chúng ra console.

Gợi ý: số nguyên tố là các số chỉ chia hết cho 1 và chính nó. Ví dụ như số 23571113,…

Bạn hãy thử code trước rồi so sánh với kết quả sau nhé.

int number = 1; // Các số tăng dần từ 1 đến 100 để kiểm tra
while (number <= 100) {
    int count = 0; // Đếm số lượng số mà number chia hết, luôn phải khởi tạo là 0
    int j = 1; // Biến chạy từ 1 đến number để kiểm tra
    while (j <= number) {
        if (number % j == 0) {
            // Tìm thấy một số mà number chia hết, tăng biến đếm lên 1 để đếm
            count++;
        }
        j++; // Nhớ dòng này
    }
    if (count == 2) {
        // Nếu count là 2, tức là số đó chỉ chia hết cho 2 số là 1 và chính nó
        System.out.println(number);
    }
    number++; // Nhớ tăng number để kiểm tra số tiếp theo
}

do while

Cú pháp cho câu lệnh do while như sau.

do {
     các_câu_lệnh;
} while (điều_kiện_lặp);

Bạn có để ý thấy là cú pháp do while khác với while chỗ nào không? Đó là nếu với while, hệ thống phải kiểm tra điều_kiện_lặp trước, nếu thỏa thì mới thực hiện các_câu_lệnh. Còn với do while, hệ thống sẽ thực hiện các_câu_lệnh trước rồi mới kiểm tra điều_kiện_lặp xem có cần thực hiện việc lặp lại hay không.

Như vậy với do while thì các_câu_lệnh được thực hiện ít nhất 1 lần. Còn với while thì các_câu_lệnh có thể sẽ không được thực hiện bao giờ nếu điều_kiện_lặp không thỏa.

do while sẽ ít khi được dùng hơn là while. Bạn không cần phải xem ví dụ cho do while, hãy bắt tay vào thử vài bài tập như sau.

Bài Thực Hành Số 3

Bạn hãy in ra console thông báo kêu người dùng nhập vào một con số từ console, rồi sau đó cho biết các thứ trong tuần tương ứng. Với 1 thì in ra “Monday”,… 7 in ra “Sunday”. Chương trình sẽ hỏi người dùng nhập số hoài cho đến khi họ nhập vào một số không phải giá trị từ 1 đến 7.

Code tham khảo như sau.

Scanner scanner = new Scanner(System.in);
int getNumber = 0;
do {
    System.out.print("Enter a number: ");
    getNumber = scanner.nextInt();
    switch (getNumber) {
    case 1:
        System.out.println("Monday");
        break;
    case 2:
        System.out.println("Tuesday");
        break;
    case 3:
        System.out.println("Wednesday");
        break;
    case 4:
        System.out.println("Thursday");
        break;
    case 5:
        System.out.println("Friday");
        break;
    case 6:
        System.out.println("Saturday");
        break;
    case 7:
        System.out.println("Sunday");
        break;
    default:
        System.out.println("Invalid number!");
        break;
    }
} while (getNumber >= 1 && getNumber <= 7);

for

Cú pháp cho câu lệnh for như sau.

for (khởi_tạo_biến_đếm; điều_kiện_lặp; điều_chỉnh_biến_đếm) {
     các_câu_lệnh;
}

Câu lệnh for sẽ lặp các_câu_lệnh bên trong nó dựa trên việc kiểm tra 3 thành phần truyền vào, các thành phần này được phân cách bởi các dấu ;. Trong đó.

khởi_tạo_biến_đếm cũng giống như bạn khai báo biến và khởi tạo (gán cho nó một giá trị) mà bạn đã học ở bài 4. biến_đếm này sẽ là căn cứ để chương trình lặp lại dòng code của bạn bao nhiêu lần.
điều_kiện_lặp là điều kiện đặt ra cho vòng lặp kiểm tra xem có nên lặp tiếp một vòng các_câu_lệnh nữa hay không.
điều_chỉnh_biến_đếm giúp thay đổi giá trị của biến_đếm mỗi khi các_câu_lệnh được thực hiện xong một lần lặp. Tại sao? Vì cũng giống như các câu lệnh lặp trên đây, nếu không có việc điều chỉnh lại biến đếm, thì các biến đếm sẽ không bao giờ bị thay đổi giá trị, và điều_kiện_lặp sẽ luôn luôn trả về cùng một giá trị, và vì vậy vòng lặp có thể sẽ bị lặp vô tận, hoặc sẽ không thực hiện bất kỳ lần lặp nào.

Bạn sẽ hiểu thêm câu lệnh for qua ví dụ sau đây.

for (int i = 0; i < 10; i++) {
    System.out.println("Hello!");
}

Ví dụ trên rất đơn giản, khởi_tạo_biến_đếmint i = 0 , giúp khai báo và khởi tạo cho biến i giá trị ban đầu là 0. điều_kiện_lặp i < 10 sẽ lặp lại khối lệnh của vòng for khi mà biến i còn nhỏ hơn 10. điều_chỉnh_biến_đếm i++ sẽ tăng i lên 1 đơn vị sau khi thực hiện các_câu_lệnh. các_câu_lệnh trong vòng for này chỉ là hàm in ra console dòng chữ “Hello!”. Vậy bạn thử nghĩ xem có bao nhiêu dòng “Hello!” được in ra màn hình ở ví dụ trên?

Bài Thực Hành Số 4

Bạn hãy viết lại Bài Thực Hành Số 1 trên kia bằng vòng lặp for nhé.

int sumEven = 0;
for(int i = 1; i <= 10; i++) {
    if (i % 2 == 0)
        sumEven += i;
}
 
System.out.println("Sum: " + sumEven);

Bạn có thể thấy là với for chúng ta có thể khai báo biến đếm i vào trong vòng lặp và khởi tạo cho nó trong đó, khác với while phải khởi tạo bên ngoài. Và việc tăng biến i được để ở thành phần thứ 3 của vòng for chứ không nằm trong thân hàm như với while nữa.

Bài Thực Hành Số 5

Bạn hãy viết lại Bài Thực Hành Số 2 trên kia bằng vòng lặp for nhé.

for (int number = 1; number <= 100; number++) {
    int count = 0; // Đếm số lượng số mà number chia hết, luôn phải khởi tạo là 0
    for(int j = 1; j <= number; j++) {
        if (number % j == 0) {
            // Tìm thấy một số mà number chia hết, tăng biến đếm lên 1 để đếm
            count++;
        }
    }
    if (count == 2) {
        // Nếu count là 2, tức là số đó chỉ chia hết cho 2 số là 1 và chính nó
        System.out.println(number);
    }
}

for Và Một Vài Mở Rộng

Đa số các bạn sẽ gặp khó khăn khi bước đầu làm quen với for, mình cũng vậy, từ lúc bắt đầu biết đến for cho đến một thời gian về sau mình mới dùng tốt for. Trước đó mình thường dùng while thay cho forwhile dễ tiếp cận và dễ nhớ hơn. Nhưng như bạn thấy while lại chiếm nhiều dòng code hơn, cái nào cũng có giá của nó 🙂

Mục mở rộng này dành cho bạn nào đã am hiểu về for rồi và muốn xem thêm for có sức mạnh nào khác không nhé.

Khai Báo Biến Đếm Bên Ngoài for

Với các cách sử dụng trên đây của for, bạn được giới thiệu là khai báo và khởi tạo biến đếm ở thành phần thứ nhất bên trong vòng for. Tuy nhiên cách này bạn sẽ không dùng được biến đếm này ở bên ngoài for.

Lấy lại ví dụ ở Bài Thực Hành Số 5 trên đây, giả sử bạn muốn kiểm tra lại thực sự sau khi kết thúc for, biến number có đúng đếm đến số 100 không, bạn viết như sau. Bạn sẽ thấy chương trình bị báo lỗi ngay ở dòng được tô sáng.

for (int number = 1; number <= 100; number++) {
    // ... Code của bài thực hành số 5 ở đây
}
System.out.println("Check the final number: " + number);

Để chương trình có thể chạy được, bạn hoàn toàn có thể mang khai báo của biến đến number ra ngoài vòng for. Việc khởi tạo biến đếm vẫn để nguyên trong for như sau.

int number;
for (number = 1; number <= 100; number++) {
    // ... Code của bài thực hành số 5 ở đây
}
System.out.println("Check the final number: " + number);

Ứng dụng chạy tốt, nhưng dòng in ra cuối cùng cho thấy number sau khi ra khỏi vòng for mang giá trị 101, không phải 100!!!

Check the final number: 101.

Bạn có thể tự giải thích được không?

for Bị Khuyết Các Thành Phần

Mặc dù for được thiết kế để truyền vào 3 thành phần, nhưng bạn vẫn có thể khai báo các for bị khuyết một thành phần. Chẳng hạn code dưới đây khuyết điều_chỉnh_biến_đếm, khi đó bạn có quyền điều khiển biến đếm bên trong thân hàm for.

for (int i = 1; i <= 10; ) {
    System.out.println("Hello!");
    i++;
}

Hay có thể khuyết khởi_tạo_biến_đếm.

int i = 1;
for (; i <= 10; i++) {
    System.out.println("Hello!");
}

Bạn có thể dùng vòng for bị khuyết cả 2 thành phần nào đó, và bạn có quyền điều khiển sự khiếm khuyết này thông qua các điều khiển bên trong và ngoài for. Như ví dụ sau.

int i = 1;
for (; i <= 10; ) {
    System.out.println("Hello!");
    i++;
}

Dã man hơn, bạn có thể khuyết luôn 3 thành phần, người ta hay dùng kiểu vòng for này để xây dựng nhanh một chức năng lặp vô tận, dùng cho một số tình huống không xác định rõ khi nào cần dừng vòng lặp, lặp sẽ chạy đến điều kiện nào đó cần dừng thì có các hàm “nhảy” ra khỏi vòng lặp mà chúng ta sẽ cùng làm quen ở bài kế tiếp. Bạn hãy xem ví dụ về for vô tận này ở code bên dưới. Và nhớ, “đừng thử vòng for vô tận này ở nhà” nha.

for (; ; ) {
    System.out.println("Please don't try at home");
}

Sức Mạnh Của Việc Điều Chỉnh Biến Đếm

Bạn đã thấy việc tăng biến đếm ở thành phần thứ 3 của for lên một đơn vị. Và tất nhiên bạn vẫn có thể tăng nó lên mấy đơn vị cũng được. Ví dụ sau viết lại Bài Thực Hành Số 4 trên đây nhưng với ít dòng code hơn khi tận dụng việc tăng biến đếm lên 2 đơn vị và bỏ qua việc kiểm tra số chẵn.

int sumEven = 0;
for(int i = 2; i <= 10; i += 2) {
    sumEven += i;
}
System.out.println("Sum: " + sumEven);

Hơn nữa, nếu bạn có thể chỉ định việc tăng biến đếm, thì bạn hoàn toàn có thể giảm biến đếm để tạo thành một vòng for đếm ngược như sau.

for(int i = 10; i >= 0; i--) {
    System.out.println("Counting down..." + i);
}

Hết Sức Cẩn Thận Với Biến Đếm Là Số Thực

Bạn đã biến biến đếm sử dụng dễ dàng và hiệu quả như thế nào nếu nó là kiểu số nguyên như tất cả các ví dụ trên đây. Nhưng nếu bạn có ý định sử dụng số thực cho biến đếm? Mình khuyên bạn là đừng bao giờ suy nghĩ đến tình huống này.

Nếu bạn nhất quyết muốn thử, hãy xem code sau.

for(double x = 0; x <= 1; x += 0.1) {
    System.out.println("See x: " + x);
}

Bạn mong muốn vòng lặp sẽ in ra mỗi giá trị tăng dần 0.1 đơn vị của số thực x, nhưng bạn thấy việc tính toán nhị phân với số thực sẽ không hoàn toàn chính xác tuyệt đối, do đó sẽ không có các giá trị đẹp đẽ 0, 0.1, 0.2, 0.3,… được in ra đâu nhé. Nó sẽ như thế này.

See x: 0.0
See x: 0.1
See x: 0.2
See x: 0.30000000000000004
See x: 0.4
See x: 0.5
See x: 0.6
See x: 0.7
See x: 0.7999999999999999
See x: 0.8999999999999999
See x: 0.9999999999999999

Tai hại hơn nếu bạn thử thực thi code như dưới đây, ứng dụng sẽ không bao giờ kết thúc do không thể có một con số 1 tròn trĩnh để so sánh với x để kết thúc vòng for.

for(double x = 0; x != 1; x += 0.1) {
    System.out.println("See x: " + x);
}

Vậy nên, tốt nhất là không nên dùng số thực cho biến đếm trong vòng for sẽ giúp ứng dụng của bạn an toàn hơn.

Kết Luận

Bạn vừa cùng mình đi qua phần còn lại trong tổng thể câu lệnh điều khiển luồng, đó là các câu lệnh lặp. Bạn sẽ được nói đến một ý nhỏ liên quan đến các câu lệnh này, đó là các lệnh dùng để thoát khỏi vòng lặp trong khi điều kiện lặp vẫn còn, sẽ rất hữu ích trong nhiều trường hợp thực tế.

Cảm ơn bạn đã đọc các bài viết của Yellow Code Books. Bạn hãy ủng hộ blog bằng cách:
– Đánh giá 5 sao ở mỗi bài viết nếu thấy thích.
– Comment bên dưới mỗi bài viết nếu có thắc mắc.
– Để lại địa chỉ email của bạn ở thanh bên phải để nhận được thông báo sớm nhất khi có bài viết mới.
– Chia sẻ các bài viết của Yellow Code Books đến nhiều người khác.
– Ủng hộ blog theo hướng dẫn ở thanh bên phải để blog ngày càng phát triển hơn.

Bài Kế Tiếp

Bạn sẽ làm quen với các câu lệnh “nhảy”, chúng là return, breakcontinue.

Exit mobile version