Learning Perl - Chương 4: Cấu trúcđiềukhiển 1. Khối câu lệnh 2. Câu lệnh if/else 3. Câu lệnh while/until 4. Câu lệnh do {} while/until 5. Câu lệnh for 6. Câu lệnh foreach 7. Bài tập 4.1 Khối câu lệnh Khối câu lệnh là một dãy các câu lệnh, được bao trong cặp dấu ngoặc nhọn ({ và }). Nó trông tựa như thế này: { cau lenh thu nhat; cau lenh thu hai; cau lenh thu ba; . cau lenh cuoi; } Perl thực hiện từng câu lệnh theo trình tự, từ đầu đến cuối. Về mặt cú pháp, một khối các câu lệnh được chấp nhận ở mọi vị trí của một câu lệnh. 4.2 Câu lệnh if/unless Độ phức tạp tiếp theo trong các câu lệnh là câu lệnh if. Kết cấu này trông rất giống kết cấu trong C: một biểu thức điềukhiển (được tính theo tính đúng đắn của nó), và hai khối. Nói cách khác, nó trông tựa như thế này: if (mot bỉeu thuc nao do) { cau lenh 1 trong truong hop dung ; cau lenh 2 trong truong hop dung ; cau lenh 3 trong truong hop dung ; . } else { cau lenh 1 trong truong hop sai ; cau lenh 2 trong truong hop sai ; cau lenh 3 trong truong hop sai ; . } Dấu ngoặc móc { và } ở đây là cần thiết (cho dù bạn chỉ có một lệnh trong phần if hoặc else), nếu bạn thiếu thì Perl sẽ báo lỗi cú pháp. Trong khi thực hiện, Perl sẽ tính biểu thức điều khiển. Nếu biểu thức này là đúng thì khối thứ nhất (các câu lệnh trong trường hợp đúng trên) sẽ được thục hiện. Nếu biểu thức là sai thì khối thứ hai (các câu lệnh trong trường hợp sai trên) sẽ được thực hiện. Nhưng đúng sai là như thế nào? Trong Perl, các qui tắc có đôi chút hơi huyền ảo, nhưng chúng cho bạn kết quả như dự kiến. Biểu thức điềukhiển được tính cho một giá trị xâu (nếu nó đã là xâu, thì chẳng có thay đổi gì, nhưng nếu nó là số thì nó sẽ được chuyển thành xâu). Nếu xâu này hoặc là xâu rỗng (chiều dài không), hoặc là một xâu có chứa một kí tự "0" (không), thì giá trị của biểu thức là sai (False). Mọi thứ khác đều được tự động coi như là đúng (True). Tại sao lại có cái qui tắc buồn cười này vậy? Vì điều ấy làm cho dễ dàng nhẩy theo cái rỗng so với một xâu khác rỗng, cũng như số không so với số khác không, không cần phải tạo ra hai cách hiểu về các giá trị đúng và sai. Sau đây là những thí dụ một số cách hiểu về đúng(True) và sai(False): 0 # chuyển thành “0”, cho nên là sai 1-1 # chuyển thành 0, rồi chuyển thành "0", cho nên là sai 1 # chuyển thành "1", nên là đúng "" # xâu rỗng, cho nên là sai "1" # khong phải là "" hay "0", cho nên đúng "00" # không phải là "" hay "0", cho nên là đúng (chú ý: trường hợp này có huyền ảo) "0.000" # cũng đúng với cùng lí do (đồng thời bạn sẽ nhận đuợc 1 warning từ Perl) undef # được xem là "", cho nên sai Về mặt thực hành mà nói, cách hiểu các giá trị đúng sai thì khá trực giác. Sau đây là một thí dụ về câu lệnh if đầy đủ: print "Ban bao nhieu tuoi roi?" $a = <STDIN>; chop($a); if ($a < 18) { print "Nay, bạn chua du tuoi bau cu dau nhe!\n"; } else { print "Du tuoi roi! Hay binh than! Vay di bau cu di!\n"; $votes++; # den so phieu } Bạn có thể cắt bỏ khối else như trong: print "Ban bao nhieu tuoi roi?" $a = <STDIN>; chop($a); if ($a < 18) { print "Nay, bạn chua du tuoi bau cu dau nhe!\n"; } Đôi khi, bạn muốn bỏ đi phần if mà chỉ có phần else, vì sẽ tự nhiên hơn để nói "hãy làm điều đó nếu điều này sai" so với "làm điều đó nếu điều không điều này là đúng". Perl giải quyết điều này với biến thể unless: print "Ban bao nhieu tuoi roi?" $a = <STDIN>; chop($a); unless ($a < 18) { print "Du tuoi roi! Hay binh than! Vay di bau cu di!\n"; $votes++; # den so phieu } Việc thay thế if bằng unless là có hiệu quả khi nói "Nếu biểu thức điềukhiển là không đúng thì hãy làm ." (một unless cũng có thể có một else, như if). Nếu bạn có nhiều hơn hai chọn lựa thì bạn có thể thêm một nhánh elsif vào câu lệnh if, giống như: if (bieu thuc so 1) { cau lenh 1 trong truong hop dung 1; cau lenh 2 trong truong hop dung 1; cau lenh 3 trong truong hop dung 1; . } elsif (bieu thuc so 2) { cau lenh 1 trong truong hop dung 2; cau lenh 2 trong truong hop dung 2; cau lenh 3 trong truong hop dung 2; . } elsif (bieu thuc so 3) { cau lenh 1 trong truong hop dung 3; cau lenh 2 trong truong hop dung 3; cau lenh 3 trong truong hop dung 3; . } else { cau lenh 1 trong truong hop sai tat ca; cau lenh 2 trong truong hop sai tat ca; cau lenh 3 trong truong hop sai tat ca; . } Mỗi biểu thức (ở đây, bieu thuc so 1, bieu thuc so 2, và bieu thuc so 3) đều được tính lần lượt. Nếu một biểu thức là đúng thì nhánh tương ứng sẽ được thực hiện, và tất cả phần còn lại của biểu thức điềukhiển cũng các nhánh câu lệnh sẽ bị bỏ qua. Nếu tất cả các biểu thức này đều sai thì nhánh else sẽ được thực hiện (nếu có). Bạn có thể có nhiều nhánh else-if (elsif) tuỳ ý. 4.3 Câu lệnh while/until Không một ngônngữ thuật toán nào lại hoàn chỉnh mà không có một dạng lặp nào đó (thực hiện lặp lại một khối các câu lệnh). Perl có thể lặp bằng việc dùng câu lệnh while: while (bieu thuc) { cau lenh 1; cau lenh 2; cau lenh 3; . } Để thực hiện câu lệnh while này, Perl tính biểu thức điều khiển. Nếu giá trị này là đúng (bằng việc dùng ý tưởng về cái đúng của câu lệnh if), thì thân của câu lệnh while sẽ được tính một lần. Điều này được lặp lại cho tới khi biểu thức điềukhiển trở thành sai, tại điểm đó Perl chuyển sang câu lệnh tiếp sau while. Chẳng hạn: print "Ban bao nhieu tuoi roi?" $a = <STDIN>; chop($a); whileless ($a > 0) { print "Vao luc nay ban moi $a tuoi.\n"; $a--; } Đôi khi nói "hãy làm việc đó trong khi điều này sai" lại dễ hơn là nói "hãy làm việc đó trong khi không điều này là đúng”. Một lần nữa, Perl lại có câu trả lời. Thay cho while là until, cũng cho kết quả mong muốn: until (bieu thuc) { cau lenh 1; cau lenh 2; cau lenh 3; . } Chú ý rằng trong cả hai dạng while và until, các câu lệnh thân sẽ bị bỏ qua hoàn toàn nếu biểu thức điềukhiển là giá trị kết thúc ngay từ lúc bắt đầu. Chẳng hạn, nếu người dùng đưa vào một độ tuổi bé hơn 0 cho đoạn chương trình trên (chương trình while) thì Perl sẽ bỏ qua thân chu trình. Có thể là biểu thức điềukhiển sẽ chẳng bao giờ để cho chu trình thoá ra được. Điều này hoàn toàn hợp pháp, và đôi khi cũng là mong muốn nữa, và do vậy không bị coi như một lỗi. Chẳng hạn, bạn có thể muốn một chu trình cứ lặp lại mãi chừng nào bạn còn chưa phạm phải lỗi, và rồi có một đoạn trình giải quyết lỗi đi theo sau chu trình. 4.4 Câu lệnh do {} while/until Câu lệnh while/until mà bạn vừa thấy ở trên sẽ kiểm tra điều kiện trước khi bắt đầu vào vòng lặp của chu trình. Nếu như điều kiện sai ngay từ đầu thì phần thân của chu trình sẽ không bao giờ được thực hiện. Tuy nhiên thỉnh thoảng bạn không muốn điều kiện được kiểm tra ngay từ đầu. Thay vào đó bạn muốn kiểm tra điều kiện ở cuối vòng lặp. Để đáp ứng điều này, Perl cung cấp cho bạn câu lệnh do {} while, nó tương tự như câu lệnh while ở trên nhưng nó chỉ bắt đầu kiểm tra điều kiện khi vòng lặp được thực hiện ít nhất 1 lần. do { cau lenh 1; cau lenh 2; cau lenh 3; . } while (bieu thuc); Perl thực hiện các câu lệnh trong phần thân của do. Sau khi câu lệnh cuối cùng kết thúc, Perl sẽ thực hiện và kiểm tra biểu thức. Nếu như biểu thức là sai, chu trình sẽ kết thúc, nếu là đúng, chu trình sẽ tiếp tục được thực hiện lại một lần nữa và Perl lại tiếp tục kiểm tra giá trị của biểu thức. Tương tự như với while, bạn có thể chuyển do while thành do until. Biểu thức điều kiện vẫn được kiểm tra ở cuối vòng lặp. Nhưng chu trình sẽ kết thúc nếu như điều kiện là đúng, còn nếu sai vòng lặp sẽ tiếp tục (nghĩa là ngược với do while). do { cau lenh 1; cau lenh 2; cau lenh 3; . } until (bieu thuc); 4.5 Câu lệnh for Một kết cấu lặp khác của Perl là câu lệnh for, mà trông giống như câu lệnh for của C, và làm việc thì đại thể cũng giống thế. Sau đây là nó: for (bieu thuc khoi dau; bieu thuc kiem tra; bieu thuc tang bien dem) { cau lenh 1; cau lenh 2; cau lenh 3; } Gỡ ra theo dạng ta đã thấy trước đây, điều này trở thành bieu thuc khoi dau; while (bieu thuc kiem tra) { cau lenh 1; cau lenh 2; cau lenh 3; bieu thuc tang; } Trong cả hai trường hợp, biểu thức khởi đầu đều được tính trước. Biểu thức này về điển hình chỉ gán giá trị ban đầu cho một biến lặp, nhưng cũng chẳng có hạn chế nào về việc nó có thể chứa cái gì - trong thực tế nó có thể rỗng. Rồi biểu thức kiểm tra sẽ được tính để xác định đúng sai. Nếu giá trị tính được là đúng thì thân chu trình sẽ được thực hiện, tiếp theo đó là tính biểu thức tăng (mà điển hình là được dùng để tăng bộ lặp). Perl tiếp đó sẽ tính lại biểu thức kiểm tra, lặp lại khi còn cần. Thí dụ này in ra các số từ 1 đến 10, mỗi số đều có sau nó một dấu cách: for ($i = 1; $i <= 10; $i++) print "$i "; } Ban đầu, biến $i được đặt là 1. Rồi, biến này được so sánh với 10, mà thực sự nó đang bé hơn hay bằng. Thân của chu trình (mỗi câu lệnh print) được thực hiện, và rồi biểu thức tăng (biểu thức tự tăng $i++) sẽ được thực hiện, thay đổi giá trị trong $i thành 2. Vì 2 vẫn còn bé hơn hay bằng 10 nên ta lặp lại tiến trình, cho tới khi lần lặp cuối mà giá trị 10 của $i đổi thành 11. Rồi 11 không còn bé hơn hay bằng 10 nữa, cho nên chu trình thoát (với $i có giá trị 11). 4.6 Câu lệnh foreach Vẫn còn một kết cấu lặp khác là câu lệnh foreach. Câu lệnh này rất giống như câu lệnh foreach của C-shell: nó nhận một danh sách các giá trị và mỗi lần lại gán chúng cho một biến vô hướng, rồi thực hiện một khối mã cùng với việc gán đó. Nó trông tựa như thế này: foreach $i (@danh_sach_nao_do) { cau lenh 1; cau lenh 2; cau lenh 3; } Không giống với C-shell, giá trị nguyên gốc của biến vô hướng được tự động khôi phục khi chu trình đi ra - nói cáhc khác, biến vô hướng là cục bộ cho chu trình. Sau đây là một thí dụ về foreach: @a = (1,2,3,4,5); foreach $b (reverse @a) { print $b; } Mẩu chương trình này in ra 54321. Chú ý rằng danh sách được foreach sử dụng có thể là một biểu thức danh sách bất kì, không chỉ là một biến mảng. (đây là điển hình cho phần lớn các kết cấu Perl có yêu cầu một danh sách). Bạn có thể bỏ tên của biến vô hướng, trong trường hợp đó Perl giả thiết rằng bạn đã xác định dùng tên biến $_. Bạn sẽ thấy rằng biến $_ được dùng như mặc định cho nhiều phép toán Perl (Tất cả các phép toán có dùng $_ theo mặc định cũng đều có thể dùng một biến vô hướng thông thường). Chẳng hạn, toán tử print in ra giá trị của $_ nếu không có giá trị nào khác được xác định, cho nên thí dụ sau đây sẽ làm việc như thí dụ trước: @a = (1,2,3,4,5); foreach (reverse @a) { print ; } Bạn xem việc dùng biến $_ làm đơn giản hơn bao nhiêu không? (Hay ít nhất thì cũng ngắn hơn). Nếu danh sách mà bạn đang lặp được tạo nên từ một tham khảo biến mảng đơn, thay vì một toán tử nào đó mà cho lại một giá trị danh sách, thì biến vô hướng đang được dùng cho lặp trong thực tế lại là tham khảo tới từng phần tử của mảng đó, thay vì là bản sao của các giá trị đó. Điều này ngụ ý gì theo nghĩa thông thường? Nó có nghĩa là nếu bạn thay đổi biến vô hướng thì bạn cũng thay đổi phần tử đặc biệt trong mảng mà biến đó đang đại diện cho. Chẳng hạn: @a = (3,5,7,9); foreach $one (@a) { $one *= 3; } # @a bay gio la (9, 15, 21, 27) Bạn hãy chú ý đến việc thay đổi $one trong thực tế làm thay đổi từng phần tử của @a, đây là một tính năng của Perl, không phải là lỗi. 4.7 Bài tập 1. Bạn hãy viết một chương trình hỏi người dùng nhập vào nhiệt độ trong phòng, sau đó in ra dòng chữ "nóng quá" nếu nhiệt độ lớn hơn 72 o C, và in ra "lạnh quá" trong trường hợp ngược lại. 2. Hãy sửa lại chương trình bạn vừa mới viết 1 chút để nó in ra "nóng quá" nếu nhiệt độ lớn hơn 75 o C, "lạnh quá" nếu nhiệt độ nhỏ nhơn 68 o C và in ra "được rồi" nếu nhiệt độ từ 68 o C đến 75 o C. 3. Bạn hãy viết chương trình nhập vào một danh sách các số (mỗi số trên 1 dòng) cho đến khi số 999 được đọc vào. Sau đó in ra tổng các số vừa đọc được (không tính số 999). Ví dụ: nếp bạn nhập 1,2,3 và 999 thì chương trình sẽ in ra 6. 4. Bạn hãy viết 1 chương trình đọc vào một danh sách các xâu (mỗi xâu một dòng) và in ra danh sách các xâu đó theo thứ tự đảo ngược (không dùng toán tử reverse). 5. Bạn hãy viết 1 chương trình in ra 1 bảng các số từ 0 đến 32 và bình phương của số đó. Nghĩa là chương trình sẽ in ra: 0 0 1 1 2 4 . Để in số được đẹp hơn, bạn có thể dùng hàm printf: printf "%3$a %5$b\n", $a, $b Tương tự như trong C, lệnh printf trên sẽ in ra 2 số $a và $b trên 1 dòng với $a được canh lề 3 ký tự và $b là 5 ký tự . để nói "hãy làm điều đó nếu điều này sai" so với "làm điều đó nếu điều không điều này là đúng". Perl giải quyết điều này với biến thể. Learning Perl - Chương 4: Cấu trúc điều khiển 1. Khối câu lệnh 2. Câu lệnh if/else 3. Câu lệnh while/until 4. Câu