Cấu trúcđiềukhiển 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. Nó trông tựa nh thế này: { câu lệnh thứ nhất; câu lệnh thứ hai; câu lệnh thứ ba; . câu lệnh cuối; } Perl thực hiện từng câu lệnh theo trình tự, từ đầu đến cuối. (Về sau, tôi sẽ chỉ cho bạn cách thay đổi trình tự thực hiện này bên trong khối, nhng hiện tại thì thế là đủ.) 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. 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 (biểu thức nào đó) { câu lệnh 1 trong trờng hợp đúng ; câu lệnh 2 trong trờng hợp đúng ; câu lệnh 3 trong trờng hợp đúng ; } else { câu lệnh 1 trong trờng hợp sai ; câu lệnh 2 trong trờng hợp sai ; câu lệnh 3 trong trờng hợp sai ; } (Nếu bạn thành thạo về C thì bạn sẽ chú ý rằng các dấu ngoặc nhọn là cần thiết. Điều này khử bỏ nhu cầu về qui tắc else lòng thòng.) 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. Nhng đúng sai là nh thế nào? Trong Perl, các qui tắc có đôi chút hơi huyền ảo, nhng chúng cho bạn kết quả nh dự kiến. Biểu thức điềukhiển đwojc tính cho một giá trị xâu (nếu nó đã là xâu, thì chẳng có thay đổi gì, nhng 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. Mọi thứ khác đều đợc tự động coi nh là đúng. 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ụ về cách hiểu đúng và sai: 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 (trờng hợp này có huyền ảo, xem mà xem) 0.000 # cũng đúng với cùng lí do và cảnh báo undef # tính thành , 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. Đừng để tôi làm bạn sợ. Sau đây là một thí dụ về câu lệnh if đầy đủ: print Bạn bao nhiêu tuổi rồi? $a = <STDIN>; chop($a); if ($a < 18) { print Này, bạn cha đủ tuổi bầu cử đâu nhé, ấy?\n; } else { print Đủ tuổi rồi! H y bình thản! Vậy đi bầu cử đi!\n;ã $voter++; # đếm số cử tri về sau } Bạn có thể cắt bỏ khối else, chỉ để lại phần then, nh trong: print Bạn bao nhiêu tuổi rồi? $a = <STDIN>; chop($a); if ($a < 18) { print Này, bạn cha đủ tuổi bầu cử đâu nhé, ấy?\n; } Đôi khi, bạn muốn bỏ đi phần then mà chỉ có phần else, vì sẽ tự nhiên hơn để nói * * Bên trong, điều này không hoàn toàn đúng. Nhng nó hành động giống nh đây là điều nó thực hiện. * * Này, rỗng là ngoại trừ cho trờng hợp bệnh hoạn của một kí tự không đấy 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 Bạn bao nhiêu tuổi rồi? $a = <STDIN>; chop($a); unless ($a < 18) { print Đủ tuổi rồi! H y bình thản! Vậy đi bầu cử đi!\n;ã $voter++; } 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 (biểu thức một nào đó) { câu lệnh 1 trong trờng hợp đúng một; câu lệnh 2 trong trờng hợp đúng một; câu lệnh 3 trong trờng hợp đúng một; } elsif (biểu thức hai nào đó ) { câu lệnh 1 trong trờng hợp đúng hai; câu lệnh 2 trong trờng hợp đúng hai; câu lệnh 3 trong trờng hợp đúng hai; } elsif (biểu thức ba nào đó ){ câu lệnh 1 trong trờng hợp đúng ba; câu lệnh 2 trong trờng hợp đúng ba; câu lệnh 3 trong trờng hợp đúng ba; } else { câu lệnh 1 trong trờng hợp sai tất cả ; câu lệnh 2 trong trờng hợp sai tất cả; câu lệnh 3 trong trờng hợp sai tất cả; } Mỗi biểu thức (ở đây, biểu thức một nào đó, biểu thức hai nào đó, và biểu thức ba nào đó) đề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 elsif tuỳ ý. Câu lệnh while/until Không một ngôn ngữ 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 (biểu thức nào đó) { câu lệnh 1; câu lệnh 2; câu lệnh 3; } Để thựuc hiện câu lệnh while này, Perl tính biểu thức điềukhiển (biểu thức nào đó trong thí dụ này). 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 Bạn bao nhiêu tuổi rồi? $a = <STDIN>; chop($a); while ($a > 0) { print Vào lúc này bạn mới $a tuổi.\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 (biểu thức nào đó) { câu lệnh 1; câu lệnh 2; câu lệnh 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 nagy 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 không cho đoạn chơng trình trên 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 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 cha 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. Bạn có thể dùng điều này cho một việc quái quỉ cứ thế chạy hoài cho tới khi hệ thống sập. 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à trong 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 (biểu thức khởi đầu; biểu thức kiểm tra; biểu thức tăng) { câu lệnh 1; câu lệnh 2; câu lệnh 3; } Gỡ ra theo dạng ta đã thấy trớc đây, điều này trở thành biểu thức khởi đầu while (biểu thức kiểm tra) { câu lệnh 1; câu lệnh 2; câu lệnh 3; biểu thức tăng } 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, nhng 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 (chẳng làm gì cả). 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 tính, 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ì điều này vẫn còn bé hơn 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 điều này không còn bé hơn hay bằng 10 nữa, cho nên chu trình đi ra (với $i có giá trị 11). 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 vỏ C: 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 sách nào đó) { câu lệnh 1; câu lệnh 2; câu lệnh 3; } Không giống lớp vỏ C, 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 - một cách khác để nói điều này là ở chỗ 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, cho nên bạn có thể coi nó nh một vùng nháp. (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à mviệ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 bây giờ là (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. Bài tập Xem Phụ lục A về lời giải. 1. Hãy viết một chơng trình hỏi về nhiệt độ bên ngoài, rồi in quá nóng nếu nhiệt độ là trên 72 F, và quá lạnh trong cadc trờng hợp khác. 1. Hãy sửa đổi chơng trình trong bài tập trớc để cho nó in ra quá nóng nếu nhiệt độ là trên 75F, quá lạnh nếu nhiệt độ là dới 68F, và vừa phải nếu nhiệt độ trong khoảng 68 và 75. 1. Hãy viết một chơng trình đọc một danh sách các số (mỗi số một hàng) cho tới khi đọc tới số 999, rồi in ra toàn bộ tất cả các số đã cộng lại với nhau. (Phải chắc đừng có cộng cả 999 vào!) Chẳng hạn, nếu bạn đa vào 1,2,3 và 999 thì chơng trình sẽ đáp ứng với câu trả lời 6 (1+2+3). 1. Hãy viết một chơng trình đọc một danh sách các xâu rồi in chúng ra thành danh sách các xâu theo thứ tự đảo ngợc (không dùng toán tử reverse cho danh sách). (Nhớ rằng toán tử <STDIN> sẽ đọc một danh sách các xâu trên từng dòng tách biệt khi đợc dùng trong hoàn cảnh mảng.) 1. Hãy viết một chơng trình in ra bảng các số và bình phơng của chúng từ không đến 32. Hãy thử đa ra một cách mà bạn không cần phải có tất cả các số từ 0 đến 32 trong danh sách, rồi thử một cách bạn phải có các số đó. (Để trông cho đẹp, printf %5g %8g\n, $a, $b sẽ in ra $a và $b nh một số có năm cột, còn $b nh một số có tám cột. . một kí tự không đấy 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:. 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ều khiển (đợc tính theo tính đúng đắn của nó), và