Các cấu trúc điều khiển khác
Toán tử last
Trong một số bài tập trớc đây bạn có thể đã nghĩ, Nếu tôi có đợc một câu lệnh
break của C ở đây, thì đã xong rồi. Cho dù bạn không nghĩ nh thế, thì hãy cứ để tôi
nói cho bạn về sự tơng đơng của Perl để thoát sớm khỏi chu trình : toán tử last.
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 (cái gì đó) {
cái gì đó ;
cái gì đó ;
cái gì đó ;
if (điều kiện nào đó) {
cái gì đó khác ;
cái gì đó khác ;
last ; # nhẩy ra khỏi chu trình while
}
thêm nữa ;
thêm nữa ;
}
# last nhẩy tới đây
Nếu điều kiện nào đó là đúng, thì cái gì đó khác sẽ đợc thực hiện, và thế rồi 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 nhánh then của câu lệnh if không đợc
tính tới - chỉ khối tạo nên for, foreach, while và các 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ừ tôi
hay không. Một thông báo nh vậy có thể giống nh là:
From: (Randal L. Schwartz)
To:
Date: 01-SEP-93 08:16:24 PM PDT - 0700
Subject: A sample mail message
Heres the body of the mail message. And here is some more.
Tôi phải duyệt qua thông báo này từ 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 của tôi hay không, merlyn.
Tôi có thể làm điều đó nh thế này:
while (<STDIN>) { # đọc dòng vào
if (/^From:/) { # nó có bắt đầu với From: không? Nếu có...
if (/merlyn/) { # nó là từ tôi!
print Email from Randal! Its about time!\n;
}
last ; # không cần tìm From: nữa, cho nên ra
} # kết thúc if from:
if (/^$/) { # dòng trống ?
last ; # nếu đúng thế thì đừng kiểm tra thêm nữa
}
} # kết thúc while
Lu ý 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 lu ý 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.
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
*
. Nó đợc dùng nh thế này:
while (cái gì đó) {
phần thứ nhất ;
phần thứ nhất ;
phần thứ nhất ;
if (điều kiện nào đó) {
phần nào đó ;
phần nào đó ;
next ; # nhẩy ra khỏi chu trình while
}
phần khác ;
phần khác ;
# next tới đây
}
Nếu điều kiện nào đó là đúng, thì phần nào đó đợc thực hiện, và phần khác bị bỏ
qua.
*
*
Nếu có một khối continue cho chu trình này, mà chúng ta thì cha thảo luận tới, thì toán tử next đi tới chỗ bắt đầu của
khối continue thay vì tới cuối khối này. Khá gần.
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.
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 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 (cái gì đó) {
# redo tới đây
cái gì đó ;
cái gì đó ;
cái gì đó ;
if (điều kiện nào đó) {
phần nào đó ;
phần nào đó ;
redo ;
}
phần khác ;
phần khác ;
phần khác ;
}
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 ý rằng với redo và last và 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:
{
phần bắt đầu ;
phần bắt đầu ;
phần bắt đầu ;
if (điều kiện nào đó) {
last ;
}
phần sau ;
phần sau ;
phần 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, tôi sẽ chỉ ra cho bạn cách viết câu lệnh
if với ít kí tự ngắt hơn.)
Khối có nhãn
Điều gì xảy ra nếu bạn muốn nhẩy 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 nhu 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 trình con, vân vân), 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:
SOMELABEL: while (điều kiện) {
câu lệnh ;
câu lệnh ;
câu lệnh ;
if (điều kiện khác) {
last SOMELABEL ;
}
}
L ý rằng tôi đã thêm SOMELABEL, 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 SOMELABEL, 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. Nhng giả sử tôi 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 lần $j là 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). Lu ý 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. 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) là (1,1), (2,1), (2,2),
(3,1), (3,2), (3,3), (4,1) vân vân.
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 để nhẩy vào trong một khối - chỉ để nhẩy ra khối. Các toán
tử last, next hay redo phải ở bên trong khối.
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:
biểu thức nào đó if biểu thức điều khiển ;
Trong trờng hợp này, biểu thức điều khiển đợ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 thức nào đó sẽ đợc
tính tiếp. Điều này đại thể tơng đơng với:
if (biểu thức điều khiển nào đó) {
biểu thức nào đó ;
}
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 lahi biến thành cách tự nhiên nhất để phát biểu vấn đề,
trong khi cũng tiết kiệm đợc vài nhát gõ. 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: / ;
}
Bạn xem dễ viết làm sao. Và bạn thậm chí còn có thể đọc nó theo kiểu tiếng Anh:
dòng cuối nếu nó bắt đầu với 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: util (exp1) { exp2 ; }
L ý 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.