Hãy xem xét các mã sau đây:
public void doSomething(int input)
{
while(true)
{
TransformInSomeWay(input);
if(ProcessingComplete(input))
break;
DoSomethingElseTo(input);
}
}
Giả sử rằng quá trình này bao gồm số bước hữu hạn nhưng phụ thuộc đầu vào; vòng lặp được thiết kế để tự chấm dứt do thuật toán và không được thiết kế để chạy vô thời hạn (cho đến khi bị hủy bởi một sự kiện bên ngoài). Bởi vì thử nghiệm để xem vòng lặp có nên kết thúc ở giữa một tập hợp các bước hợp lý hay không, bản thân vòng lặp while hiện không kiểm tra bất cứ điều gì có ý nghĩa; thay vào đó, kiểm tra được thực hiện tại vị trí "thích hợp" trong thuật toán khái niệm.
Tôi đã nói rằng đây là mã xấu, bởi vì nó dễ bị lỗi hơn do điều kiện kết thúc không được kiểm tra bởi cấu trúc vòng lặp. Khó khăn hơn để tìm ra cách bạn thoát khỏi vòng lặp và có thể mời các lỗi vì điều kiện phá vỡ có thể được bỏ qua hoặc bỏ qua vô tình đưa ra các thay đổi trong tương lai.
Bây giờ, mã có thể được cấu trúc như sau:
public void doSomething(int input)
{
TransformInSomeWay(input);
while(!ProcessingComplete(input))
{
DoSomethingElseTo(input);
TransformInSomeWay(input);
}
}
Tuy nhiên, điều này sao chép một cuộc gọi đến một phương thức trong mã, vi phạm DRY; nếu TransformInSomeWay
sau đó được thay thế bằng một số phương thức khác, cả hai cuộc gọi sẽ phải được tìm thấy và thay đổi (và thực tế là có hai cuộc gọi có thể ít rõ ràng hơn trong một đoạn mã phức tạp hơn).
Bạn cũng có thể viết nó như sau:
public void doSomething(int input)
{
var complete = false;
while(!complete)
{
TransformInSomeWay(input);
complete = ProcessingComplete(input);
if(!complete)
{
DoSomethingElseTo(input);
}
}
}
... nhưng bây giờ bạn có một biến có mục đích duy nhất là chuyển dịch kiểm tra điều kiện sang cấu trúc vòng lặp và cũng phải được kiểm tra nhiều lần để cung cấp hành vi giống như logic ban đầu.
Về phần tôi, tôi nói rằng với thuật toán mà mã này thực hiện trong thế giới thực, mã gốc là thứ dễ đọc nhất. Nếu bạn đã tự mình trải qua nó, đây là cách bạn nghĩ về nó, và vì vậy nó sẽ trực quan với những người quen thuộc với thuật toán.
Vậy, cái nào "tốt hơn"? tốt hơn là giao trách nhiệm kiểm tra điều kiện cho vòng lặp while bằng cách cấu trúc logic xung quanh vòng lặp? Hoặc tốt hơn là cấu trúc logic theo cách "tự nhiên" như được chỉ ra bởi các yêu cầu hoặc mô tả khái niệm về thuật toán, mặc dù điều đó có thể có nghĩa là bỏ qua các khả năng tích hợp của vòng lặp?
do ... until
trúc này cũng hữu ích cho các loại vòng lặp này vì chúng không phải được "mồi".