Diễn đàn tin học | Tutorial Room Mục lục «« Chương 8 »» Chương 10 Learning Perl - Chương 8: Các cấu trúc điều khiển khác 1. Xác định một hàm tiện ích 2. Gọi một hàm tiện ích 3. Giá trị trả về 4. Đối số của hàm 5. Biến cục bộ trong hàm 6. Bài tập 9.1 Toán tử last Toán tử last tương đương câu lệnh break của C, để thoát sớm khỏi chu trình. Toán tử last ngắt khối chu trình bao quanh ở bên trong nhất, gây ra việc thực hiện tiếp tục với câu lệnh đi ngay sau khối đó. Chẳng hạn: while (something) { something; something; something; if (somecondition) { somethingother; somethingother; lase; #thoat khoi chu trinh while } morething; morething; } #last se nhay den cho nay Nếu somecondition là đúng (true), thì somethingother sẽ được thực hiện, và cuối cùng toán tử last buộc chu trình while phải kết thúc. Toán tử last chỉ tính tới khối chu trình, không tính khối cần để tạo nên kết cấu cú pháp nào đó. Điều này có nghĩa là khối tạo nên câu lệnh if hay else hoặc do không được tính tới - chỉ khối tạo nên for, foreach, while và khối "trần" mới được tính (khối "trần" là khối không thuộc phần khác của một kết cấu lớn hơn, như một chu trình, hay một trình con, hay một câu lệnh if/then/else). Giả sử tôi muốn xem liệu thông báo thư đã được cất giữ trong một tệp có là từ merlyn hay không. Một thông báo như vậy có thể giống như là: From: merlyn@ora.com (Jenny L. Schwartz) To: stevet@ora.com Date: 01-SEP-93 08:16:24 PM PDT - 0700 Subject: A sample mail message Here’s the body of the mail message. And here is some more. Tôi phải duyệt qua thông báo này và tìm dòng bắt đầu với From: và rồi để ý liệu dòng này có chứa tên đăng nhập merlyn hay không. Tôi có thể làm điều đó như thế này: while (<STDIN>) { #doc du lieu if (/^From:/) { #bat dau voi "From:"? Neu co if (/merlyn/) { #no co chua chuoi "merlyn" hay khong? print "Email from Jenny! Its about time!\n"; } last ; #khong can tim "From:" nua nen thoat khoi vong lap } #ket thuc "if /^From:/" if (/^$/) { #neu la dong trong? last ; #thi khong can kiem tra them nua } } #ket thuc while Lưu ý rằng một khi dòng có chứa From: được tìm thấy thì chúng ta đi ra khỏi chu trình chính bởi vì tôi muốn xem chỉ dòng From: đầu tiên. Cũng lưu ý rằng một đầu đề thư kết thúc tại dòng trống đầu tiên, cho nên chúng ta có thể ra khỏi chu trình chính nữa. 9.2 Toán tử next Giống như last, next cũng làm thay đổi luồng thực hiện theo trình tự thông thường. Tuy nhiên, toán tử next làm cho việc thực hiện bỏ qua phần còn lại của khối chu trình được bao bên trong nhất mà không kết thúc khối này (khác với last, last sẽ làm kết thúc khối chu trình). Nó được dùng như thế này: while (mot_cai_gi_do) { phan_thu_nhat; if (dieu_kien) { phan_nao_do; next ; #nhay den cuoi chu trinh while } phan_khac; #next se nhay toi day } Nếu dieu_kien là đúng, thì phan_nao_do sẽ được thực hiện và phan_khac sẽ bị bỏ qua (nếu dieu_kien là sai thì phan_nao_do sẽ không được thực hiện và phan_khac sẽ được thực hiện như bình thường) Một lần nữa, khối của một câu lệnh if không được tính tới như khối chu trình. 9.3 Toán tử redo Cách thứ ba mà bạn có thể nhảy qua trong một khối chu trình là bằng cách dùng toán tử redo. Toán tử này nhảy tới chỗ bắt đầu của khối hiện tại (không tính lại biểu thức điều kiện), kiểu như: while (cai_gi_do) { #redo se nhay toi cho nay phan_1; if (dieu_kien_nao_do) { phan_nao_do; redo ; } phan_khac; } Một lần nữa, khối if không được tính tới. Chỉ tính các khối chu trình. Lưu ý rằng với redo và last dùng trong một khối trần, bạn có thể tạo nên chu trình vô hạn mà đi ra từ giữa, kiểu như: { phan_bat_dau; if (dieu_kien_nao_do) { last ; } phan_sau; redo ; } Điều này sẽ phù hợp cho một chu trình kiểu while mà cần tới việc có một phần nào đó của chu trình này được thực hiện như việc khởi đầu trước phép kiểm thử thứ nhất. (Trong mục "Bộ thay đổi biểu thức" dưới đây, sẽ chỉ ra cho bạn cách viết câu lệnh if với ít kí tự ngắt hơn.) 9.4 Khối có nhãn Điều gì xảy ra nếu bạn muốn ra khỏi một khối có chứa khối bên trong nhất, hay nói theo cách khác, ra khỏi hai khối lồng nhau ngay một lúc? Trong C, bạn phải viện tới toán tử goto để đi ra. Không cần phải làm như vậy trong Perl - bạn có thể dùng last, next và redo tại bất kì khối kết nào bằng việc cho khối một cái tên có nhãn. Nhãn là một kiểu tên khác từ một không gian tên khác mà tuân theo cùng qui tắc như vô hướng, mảng, mảng kết hợp và trình con. Tuy nhiên, như chúng ta thấy, một nhãn không có kí tự ngắt đi đầu đặc biệt (như $ cho vô hướng, @ cho mảng ), cho nên một nhãn có tên print sẽ xung đột với từ dành riêng print và sẽ không được phép. Bởi lí do này, Larry gợi ý bạn hãy chọn các nhãn bao gồm toàn chữ hoa và số, mà anh ấy đảm bảo sẽ không bao giờ bị chọn nhầm thành một từ dành riêng trong tương lai. Bên cạnh đó, tất cả các chữ hoa cho phép dễ nhìn thấy hơn trong một văn bản chương trình mà phần lớn là chữ thường. Một khi bạn đã chọn cẩn thận nhãn, thì nó sẽ đứng ngay trước câu lệnh có chứa khối, theo sau dấu hai chấm, kiểu như thế này: NHAN: while (dieu_kien) { cau_lenh_1; cau_lenh_2; if (dieu_kien_khac) { last NHAN; } } Lưu ý rằng ta đã thêm NHAN, như một tham biến vào câu lệnh last. Tham biến này bảo cho Perl ra khỏi khối có tên NHAN, thay vì ra khỏi khối bên trong nhất. Trong trường hợp này, chúng ta không có cái gì khác ngoài khối bên trong nhất. Nhưng giả sử có các chu trình lồng nhau: OUTER: for ($i = 1; $i <= 10; $i++) { INNER: for ($j = 1; $j >= 10; $j++) { if ($i * $j == 63) { print "$i x $j = 63!\n"; last OUTER; } if ($j >= $i) { next OUTER ; } } } Tập hợp các câu lệnh này thử tất cả các giá trị kế tiếp của hai số nhỏ nhất được nhân với nhau cho tới khi nó tìm ra một cặp có tích là 63 (7 và 9). Lưu ý rằng một khi đã tìm được một cặp thì không cần phải kiểm tra các số khác nữa, cho nên câu lệnh if thứ nhất ra khỏi cả hai chu trình for bằng việc dùng last với nhãn OUTER. Câu lệnh if thứ hai cố gắng đảm bảo rằng số lớn hơn trong hai số bao giờ cũng là số thứ nhất bằng việc bỏ qua việc lặp tiếp của chu trình bên ngoài ngay khi điều kiện này không còn xảy ra nữa. Điều này có nghĩa là các số sẽ được kiểm thử với ($i, $j) sẽ là (1,1), (2,1), (2,2), (3,1), (3,2), (3,3), (4,1) Cho dù khối bên trong nhất được gắn nhãn, thì các toán tử last, next, và redo không có tham biến tuỳ chọn (nhãn) vẫn vận hành tôn trọng khối bên trong nhất. Cũng vậy, bạn không thể dùng nhãn để vào trong một khối - chỉ để ra khối. Các toán tử last, next hay redo phải ở bên trong khối. 9.5 Bộ thay đổi biểu thức Xem như một cách khác để chỉ ra "nếu thế này, thì thế kia," Perl cho phép bạn gắn nhãn cho một bộ sửa đổi if lên một biểu thức vốn là một biểu thức đứng riêng. Kiểu như: bieu_thuc_nao_do if bieu_thuc_dieu_kien; Trong trường hợp này, bieu_thuc_dieu_kien được tính trước để xét giá trị chân lí của nó (bằng việc dùng cùng qui tắc như thường lệ), và nếu đúng, thì biểu_thuc_nao_do sẽ được tính tiếp. Điều này đại thể tương đương với: if (bieu_thuc_dieu_kien) { bieu_thuc_nao_do; } ngoại trừ rằng bạn không cần thêm dấu ngắt phụ, câu lệnh này đọc ngược lại, và biểu thức phải là một biểu thức đơn (không phải là một khối câu lệnh). Tuy nhiên, nhiều lần cách mô tả ngược này lại biến thành cách tự nhiên nhất để phát biểu vấn đề. Chẳng hạn, sau đây là cách bạn có thể ra khỏi chu trình khi một điều kiện nào đó nảy sinh: LINE: while (<STDIN>) { last LINE if /^From: /; } Các dạng song song khác bao gồm những dạng sau: exp2 unless exp1;# giống: unless (exp1) { exp2 } exp2 while exp1; # giống: while (exp1) { exp2 } exp2 until exp1; # giống: until (exp1) { exp2 } Lưu ý rằng tất cả các dạng này đều tính exp1 trước rồi dựa trên đó, tính hay không tính cái gì đó với exp2. Chẳng hạn, sau đây là cách tìm ra luỹ thừa đầu tiên của hai số lớn hơn một số đã cho: chop ($n = <STDIN>); $i = 1; #khoi dau $i *= 2 until $i > $n; #lap lai cho toi khi tim ra no Các dạng này không lồng nhau - bạn không thể nói được exp3 while exp2 if exp1. Là vì dạng exp2 if exp1 không còn là một biểu thức, mà là một câu lệnh hoàn toàn, và bạn không thể gắn thêm một trong các bộ sửa đổi này vào sau câu lệnh. 9.6 &&, || và ?: xem như các cấu trúc điều khiển Những cấu trúc này trông tựa như các kí tự ngắt, hay một phần của biểu thức. Liệu chúng có thể thực sự được coi là các cấu trúc điều khiển không? Thế này, theo cách nghĩ Perl, gần như bất kì cái gì cũng đều có thể cả, cho nên bạn hãy xem điều tôi nói ở đây. cũng đều có thể cả, cho nên bạn hãy xem điều tôi nói ở đây. Thông thường, bạn bắt gặp "nếu cái này, thì cái nọ". Trước đây chúng ta đã thấy hai dạng này: if (cai_nay) { cai_no } # một cách cai_no if cai_nay; # cách khác Đây là cách thứ ba (có thể vẫn còn nữa đấy): cai_nay && cai_no; Tại sao nó lại làm việc? Nó chẳng phải là toán tử logic "và" sao? Uhh, bạn hãy kiểm tra xem cái gì xảy ra khi cai_nay lấy giá trị TRUE hay FALSE: Nếu cai_nay là TRUE, thế thì giá trị của toàn bộ biểu thức vẫn còn chưa được biết tới, vì nó phụ thuộc vào giá trị của cai_kia. Cho nên cai_kia phải được tính. Nếu cai_nay là FALSE thế thì chẳng cần nhìn vào cai_kia nữa, bởi vì lúc này giá trị của toàn bộ biểu thức phải là FALSE rồi. Vì chằng cần gì phải tính cai_kia nên chúng ta có thể bỏ qua. Và trong thực tế, đây là điều mà Perl làm. Perl tính cai_kia chỉ khi cai_nay là đúng, làm cho nó thành tương đương với hai dạng trước. Giống thế, toán tử logic hoặc giống như câu lệnh unless (hay bộ sửa đổi unless). Cho nên bạn có thể thay thế: unless (cai_nay) { cai_kia } bằng cai_nay || cai_kia; Nếu bạn quen thuộc với việc dùng các toán tử này trong các chương trình shell để kiểm soát các chỉ lệnh thực hiện điều kiện, thì bạn sẽ thấy rằng chúng vận hành tương tự trong Perl. Cuối cùng toán tử ba ngôi kiểu C: exp1 ? exp2 : exp3; tính exp2 nếu exp1 đúng, ngược lại tính exp3. Cũng dường như là chúng ta nói: if ( exp1 ) { exp2 } else { exp3 } nhưng một lần nữa không có tất cả các dấu ngắt đó. Chẳng hạn, bạn có thể viết: ($a < 10) ? $b = $a : $a = $b; Ta nên dùng cái nào đây? Tuỳ vào tâm trạng bạn thôi, đôi khi, hay tuỳ theo từng phần biểu thức lớn đến đâu, hay liệu ta cần thêm đóng mở ngoặc nào bởi vì sự xung khắc thứ tự ưu tiên. Bạn hãy nhìn vào chương trình của người khác và xem chúng làm gì. Có lẽ bạn sẽ thấy đôi điều ở đó. Larry gợi ý rằng bạn hãy đặt phần quan trọng nhất của biểu thức lên trước, để cho nó nổi bật ra. 9.7 Bài tập 1. Bạn hãy viết lại bài tập ở chương trước sao cho vòng lặp được thực hiện mãi cho đến khi từ end được nhập vào. 2. Bạn hãy viết lại bài tập ở chương 4: cộng tất cả các số nguyên từ 1 đến 999, sử dụng vòng lặp và thoát ở giữa vòng lặp Mục lục «« Chương 8 »» Chương 10 . Diễn đàn tin học | Tutorial Room Mục lục «« Chương 8 »» Chương 10 Learning Perl - Chương 8: Các cấu trúc điều khiển khác 1. Xác định một hàm tiện ích 2. Gọi một hàm tiện ích 3. Giá trị trả. bạn không thể gắn thêm một trong các bộ sửa đổi này vào sau câu lệnh. 9.6 &&, || và ?: xem như các cấu trúc điều khiển Những cấu trúc này trông tựa như các kí tự ngắt, hay một phần của. coi là các cấu trúc điều khiển không? Thế này, theo cách nghĩ Perl, gần như bất kì cái gì cũng đều có thể cả, cho nên bạn hãy xem điều tôi nói ở đây. cũng đều có thể cả, cho nên bạn hãy xem điều