Có phải các vòng lặp trong khi (đúng) rất tệ? [đóng cửa]


218

Tôi đã lập trình Java được vài năm rồi, nhưng tôi mới trở lại trường để lấy bằng chính thức. Tôi khá ngạc nhiên khi biết rằng, trong bài tập cuối cùng của mình, tôi đã mất điểm vì sử dụng một vòng lặp như vòng dưới đây.

do{
     //get some input.
     //if the input meets my conditions, break;
     //Otherwise ask again.
} while(true)

Bây giờ đối với thử nghiệm của tôi, tôi chỉ quét một số đầu vào giao diện điều khiển, nhưng tôi được cho biết rằng loại vòng lặp này không được khuyến khích vì sử dụng break gần giống với goto, chúng tôi không làm điều đó.

Tôi hiểu đầy đủ những cạm bẫy của goto và anh em họ Java của nó break:labelvà tôi có ý thức tốt khi không sử dụng chúng. Tôi cũng nhận ra rằng một chương trình hoàn chỉnh hơn sẽ cung cấp một số phương tiện trốn thoát khác, ví dụ như chỉ kết thúc chương trình, nhưng đó không phải là lý do giáo sư của tôi trích dẫn, vì vậy ...

Có chuyện gì với bạn do-while(true)vậy?


23
Hãy hỏi giáo viên của bạn, loại công cụ đó là khá chủ quan.
Chris Eberle

18
Tôi đã tìm thấy bài viết này hữu ích để hiểu những gì có hại về goto . Việc so sánh với breakcó lẽ cũng có ý nghĩa, nhưng thực sự bị hiểu lầm. Có lẽ bạn có thể giáo dục giáo sư của bạn về điều này;) Theo kinh nghiệm của tôi, các giáo sư không biết nhiều về nghề lập trình.
Magnus Hoff

100
Điều tồi tệ thực sự duy nhất, không thể chối cãi về điều này là trong tâm trí của tôi, thực tế do {} while (true)là tương đương while(true) {}và sau này là hình thức thông thường hơn và rõ ràng hơn nhiều.
Voo

11
Nếu bất cứ ai không đánh giá cao sức mạnh biểu cảm đơn giản của breakhọ, họ nên thử lập trình bằng ngôn ngữ mà không có nó. Nó không mất quá nhiều vòng lặp trước khi bạn muốn nó!
Steven

16
Tôi không đồng ý với thẻ bài tập về nhà.
aaaa bbbb

Câu trả lời:


220

Tôi sẽ không nói nó tệ - nhưng bình thường tôi sẽ ít nhất tìm kiếm một sự thay thế.

Trong những tình huống mà đó là điều đầu tiên tôi viết, tôi hầu như luôn cố gắng tái cấu trúc nó thành một cái gì đó rõ ràng hơn. Đôi khi không thể giúp được (hoặc thay thế là có một boolbiến không có ý nghĩa gì ngoại trừ chỉ ra kết thúc của vòng lặp, ít rõ ràng hơn một breaktuyên bố) nhưng ít nhất nó đáng để thử.

Ví dụ về nơi sử dụng rõ ràng breakhơn cờ, hãy xem xét:

while (true)
{
    doStuffNeededAtStartOfLoop();
    int input = getSomeInput();
    if (testCondition(input))
    {
        break;
    }
    actOnInput(input);
}

Bây giờ hãy buộc nó sử dụng cờ:

boolean running = true;
while (running)
{
    doStuffNeededAtStartOfLoop();
    int input = getSomeInput();
    if (testCondition(input))
    {
        running = false;
    }
    else
    {
        actOnInput(input);
    }
}

Tôi xem phần sau phức tạp hơn để đọc: nó có thêm một elsekhối, phần actOnInputlõm vào nhiều hơn và nếu bạn đang cố gắng tìm ra điều gì xảy ra khi testConditiontrả về true, bạn cần xem xét kỹ phần còn lại của khối để kiểm tra xem có ở đó không không phải là một cái gì đó sau khi các elsekhối đó sẽ xảy ra dù runningđã được thiết lập đểfalse hay không.

Các break tuyên bố truyền đạt ý định rõ ràng hơn, và cho phép phần còn lại của khối tiếp tục với những gì nó cần phải làm mà không lo lắng về điều kiện trước đó.

Lưu ý rằng đây chính xác là cùng một loại đối số mà mọi người có về nhiều câu lệnh return trong một phương thức. Ví dụ: nếu tôi có thể tìm ra kết quả của một phương thức trong một vài dòng đầu tiên (ví dụ: vì một số đầu vào là null hoặc trống hoặc bằng 0) tôi thấy rõ ràng hơn là trả về câu trả lời đó trực tiếp hơn là có một biến để lưu kết quả , sau đó là cả một khối mã khác, và cuối cùng là một returncâu lệnh.


3
Tôi đồng ý rằng đây không phải là công cụ đầu tiên tôi tiếp cận, nhưng nó dường như xử lý vấn đề rất rõ ràng và tôi thích mã sạch.
JHarnach

3
@ X-Zero: Có, đôi khi. Nếu bạn có thể biến "phá vỡ" thành "trở lại" thường là sự tốt lành ... mặc dù bạn vẫn có thể kết thúc với mộtwhile (true)
Jon Skeet

21
@trutheality: Mặc dù vậy, tôi thích phần lớn mã của mình là không bị ràng buộc nhất có thể. Và một điều kiện với một bài tập ghép cảm thấy phức tạp hơn đối với tôi.
Jon Skeet

3
@Vince: Vâng, và thật tuyệt nếu bạn có thể diễn đạt điều kiện dễ dàng khi bắt đầu vòng lặp . Nhưng đôi khi bạn không thể - và đó là tình huống chúng ta đang nói đến. Xin lưu ý vài câu đầu tiên trong câu trả lời của tôi.
Jon Skeet

6
@Ismail: Tôi hy vọng là không. Bài viết nên được đánh giá chỉ dựa trên nội dung của họ, không phải trên tác giả. Heck, tôi thậm chí còn đánh giá thấp một bài đăng của Eric Lippert nếu tôi cảm thấy nó không chính xác / không có ích. Điều đó chưa xảy ra mặc dù :)
Jon Skeet

100

AFAIK không có gì, thực sự. Giáo viên chỉ dị ứng với goto, vì họ nghe thấy ở đâu đó nó thực sự tồi tệ. Nếu không, bạn sẽ chỉ viết:

bool guard = true;
do
{
   getInput();
   if (something)
     guard = false;
} while (guard)

Mà gần như là điều tương tự.

Có thể đây là trình dọn dẹp (vì tất cả thông tin lặp được chứa ở đầu khối):

for (bool endLoop = false; !endLoop;)
{

}

4
Tôi thích đề xuất của bạn vì điều kiện chấm dứt rõ ràng hơn nhiều. Điều đó có nghĩa là khi đọc mã bạn sẽ sớm hiểu được những gì nó đang cố gắng làm. Tôi sẽ không bao giờ sử dụng một vòng lặp vô hạn mà thường xuyên sử dụng hai phiên bản của bạn.
Sled

4
Sự đồng thuận là một lá cờ tốt hơn phá vỡ phần lớn vì nó thể hiện ý định? Tôi có thể thấy rằng có lợi. Tất cả các câu trả lời chắc chắn, nhưng tôi sẽ đánh dấu điều này là được chấp nhận.
JHarnach

20
Cả hai điều này vẫn phải chịu đựng phần còn lại của vòng lặp bị ô nhiễm (ít nhất là trong một số trường hợp) do phải tiếp tục đi đến cuối vòng lặp ngay cả khi bạn biết bạn đang phá vỡ . Tôi sẽ đưa ra một ví dụ trong câu trả lời của tôi ...
Jon Skeet

8
Tôi thích câu trả lời của Jon Skeet hơn. Ngoài ra, while (true) {} tốt hơn so với {} while (true)
aaaa bbbb

1
Tôi ghét rằng Dijkstra đã từng viết bài báo GoTo đó. Mặc dù GoTo chắc chắn có thể thường bị lạm dụng trong quá khứ không có nghĩa là bạn không nên sử dụng nó. Ngoài ra, Thoát cho, Phá, Thoát Trong khi, Thử / Bắt đều là các dạng Goto chuyên dụng. Gotos thường có thể làm cho mã dễ đọc hơn. Và vâng tôi biết rằng mọi thứ đều có thể được thực hiện mà không có Goto. Điều đó không có nghĩa là nó nên.
Kibbee

39

Douglas Crockford đã có một nhận xét về cách ông muốn JavaScript chứa một loopcấu trúc:

loop
{
  ...code...
}

Và tôi cũng không nghĩ Java sẽ tệ hơn khi có loopcấu trúc.

Không có gì vốn sai với là while(true)các vòng, nhưng có một xu hướng cho giáo viên để ngăn cản họ. Từ góc độ giảng dạy, rất dễ để học sinh tạo ra những vòng lặp vô tận và không hiểu tại sao vòng lặp không bao giờ thoát ra được.

Nhưng điều họ hiếm khi đề cập đến là tất cả các cơ chế lặp có thể được nhân rộng bằng while(true)các vòng lặp.

while( a() )
{
  fn();
}

giống như

loop
{
  if ( !a() ) break;
  fn();
}

do
{
  fn();
} while( a() );

giống như:

loop
{
  fn();
  if ( !a() ) break;
}

for ( a(); b(); c() )
{
  fn();
}

giống như:

a();
loop
{
  if ( !b() ) break;
  fn();
  c();
}

Miễn là bạn có thể thiết lập các vòng lặp của mình theo cách làm việc với cấu trúc mà bạn chọn sử dụng là không quan trọng. Nếu nó xảy ra để phù hợp với một forvòng lặp, sử dụng mộtfor vòng lặp.

Một phần cuối cùng: giữ cho các vòng lặp của bạn đơn giản. Nếu có rất nhiều chức năng cần phải xảy ra trên mỗi lần lặp, hãy đặt nó vào một chức năng. Bạn luôn có thể tối ưu hóa nó sau khi bạn đã làm việc.


2
+1: Có các phức tạp bổ sung với forcác vòng lặp khi các continuecâu lệnh được tham gia, nhưng chúng không phải là một phần mở rộng chính.
Donal Fellows

Tôi thường sử dụng một vòng lặp for khi có một chuỗi tôi muốn chạy ngang qua và một vòng lặp while khi có một điều kiện mà tôi muốn đáp ứng. Điều này giữ cho mã imo dễ đọc hơn.
dascandy

4
Có ai viết for (;;) {nữa không? (Phát âm là "mãi mãi"). Điều này đã từng rất phổ biến.
Dawood ibn Kareem

16

Trở lại năm 1967, Edgar Dijkstra đã viết một bài báo trên một tạp chí thương mại về lý do tại sao nên loại bỏ goto khỏi các ngôn ngữ cấp cao để cải thiện chất lượng mã. Toàn bộ mô hình lập trình được gọi là "lập trình có cấu trúc" ra đời từ điều này, mặc dù chắc chắn không phải ai cũng đồng ý rằng goto tự động có nghĩa là mã xấu.

Mấu chốt của lập trình có cấu trúc về cơ bản là cấu trúc của mã nên xác định luồng của nó thay vì có gotos hoặc ngắt hoặc tiếp tục xác định luồng, bất cứ khi nào có thể. Tương tự, có nhiều điểm vào và thoát vào một vòng lặp hoặc hàm cũng không được khuyến khích trong mô hình đó.

Rõ ràng đây không phải là mô hình lập trình duy nhất, nhưng thường nó có thể dễ dàng áp dụng cho các mô hình khác như lập trình hướng đối tượng (ala Java).

Giáo viên của bạn có thể đã được dạy và đang cố gắng dạy cho lớp của bạn rằng chúng tôi sẽ tránh "mã spaghetti" tốt nhất bằng cách đảm bảo mã của chúng tôi được cấu trúc và tuân theo các quy tắc ngụ ý của lập trình có cấu trúc.

Mặc dù không có gì "sai" với việc triển khai sử dụng break, một số người cho rằng việc đọc mã dễ dàng hơn đáng kể trong đó điều kiện cho vòng lặp được chỉ định rõ ràng trong điều kiện while () và loại bỏ một số khả năng là quá khó. Chắc chắn có những cạm bẫy khi sử dụng một điều kiện (đúng) dường như thường xuyên xuất hiện trong mã bởi các lập trình viên mới làm quen, chẳng hạn như nguy cơ vô tình tạo ra một vòng lặp vô hạn hoặc tạo mã khó đọc hoặc khó hiểu một cách không cần thiết.

Trớ trêu thay, xử lý ngoại lệ là một lĩnh vực mà sự sai lệch so với lập trình có cấu trúc chắc chắn sẽ xuất hiện và được mong đợi khi bạn tiếp tục lập trình trong Java.

Cũng có thể người hướng dẫn của bạn có thể mong đợi bạn thể hiện khả năng sử dụng cấu trúc vòng lặp hoặc cú pháp cụ thể được dạy trong chương hoặc bài học của văn bản đó và trong khi mã bạn viết tương đương về mặt chức năng, bạn có thể không thể hiện kỹ năng cụ thể mà bạn được cho là đang học trong bài học đó.


2
Đây là bài viết của Edsger Dijkstra . Đó là một đọc tốt.
Roy Prins

14

Quy ước Java hữu dụng để đọc đầu vào là:

import java.io.*;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String strLine;

while ((strLine = br.readLine()) != null) {
  // do something with the line
}

Và quy ước C ++ thông thường để đọc đầu vào là:

#include <iostream>
#include <string>
std::string data;
while(std::readline(std::cin, data)) {
  // do something with the line
}

Và trong C, nó

#include <stdio.h>
char* buffer = NULL;
size_t buffer_size;
size_t size_read;
while( (size_read = getline(&buffer, &buffer_size, stdin)) != -1 ){
  // do something with the line
}
free(buffer);

hoặc nếu bạn bị thuyết phục bạn biết dòng văn bản dài nhất trong tệp của bạn dài bao nhiêu, bạn có thể làm

#include <stdio.h>
char buffer[BUF_SIZE];
while (fgets(buffer, BUF_SIZE, stdin)) {
  //do something with the line
}

Nếu bạn đang kiểm tra xem liệu người dùng của bạn đã nhập quitlệnh hay chưa, thật dễ dàng để mở rộng bất kỳ cấu trúc vòng lặp nào trong số 3 cấu trúc này. Tôi sẽ làm điều đó trong Java cho bạn:

import java.io.*;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line;

while ((line = br.readLine()) != null  && !line.equals("quit") ) {
  // do something with the line
}

Vì vậy, trong khi chắc chắn có trường hợp breakhoặc gotohợp lý, nếu tất cả những gì bạn đang làm là đọc từ tệp hoặc bảng điều khiển theo từng dòng, thì bạn không cần một while (true)vòng lặp để thực hiện nó - ngôn ngữ lập trình của bạn đã cung cấp cho bạn với một thành ngữ thích hợp để sử dụng lệnh đầu vào làm điều kiện vòng lặp.


Trong thực tế, nếu bạn đang sử dụng một while (true)vòng lặp thay vì một trong các vòng lặp đầu vào thông thường này, bạn có thể quên kiểm tra phần cuối của tệp.
Ken Bloom

3
Bạn đang thực hiện điều này một cách hiệu quả bằng cách nhồi phần đầu tiên của vòng lặp vào whileđiều kiện. Trong điều kiện Java cuối cùng, nó đã trở nên khá nặng nề và nếu bạn phải thực hiện thao tác mở rộng để quyết định có tiếp tục hay không, điều đó có thể sẽ khá lâu. Bạn có thể chia nó thành một chức năng riêng biệt và đó có thể là điều tốt nhất. Nhưng nếu bạn thực sự muốn nó trong một chức năng, và có công việc không cần thiết phải làm trước khi quyết định có tiếp tục hay không, while(true)có thể là tốt nhất.
poolie

@poolie: Sau đó, có lẽ tốt nhất là sử dụng lệnh read làm điều kiện vòng lặp (kiểm tra EOF) và kiểm tra các điều kiện khác của bạn bên trong vòng lặp dưới dạng các câu lệnh break.
Ken Bloom

1
Đối với những gì nó có giá trị, gets()không phải là quy ước chung. Nó rất thuận lợi để tràn bộ đệm. fgets(buffer, BUFFER_SIZE, file)giống như thực hành tiêu chuẩn hơn nhiều.
Dave

@Dave: Bây giờ tôi đã chỉnh sửa câu trả lời để sử dụng fgets.
Ken Bloom

12

Đó không phải là một điều khủng khiếp, nhưng bạn cần phải xem xét các nhà phát triển khác khi viết mã. Ngay cả trong trường học.

Các nhà phát triển đồng nghiệp của bạn sẽ có thể thấy mệnh đề thoát cho vòng lặp của bạn, tại khai báo vòng lặp. Bạn đã không làm điều đó. Bạn ẩn mệnh đề thoát ở giữa vòng lặp, tạo thêm công việc cho người khác đi cùng và cố gắng hiểu mã của bạn. Đây là cùng một lý do mà những thứ như "phá vỡ" được tránh.

Điều đó đang được nói, bạn vẫn sẽ thấy những thứ như thế này trong RẤT NHIỀU mã ngoài kia trong thế giới thực.


4
Nếu vòng lặp bắt đầu với một while (true)điều khá rõ ràng là sẽ có một breakhoặc returnbên trong nó, hoặc nó sẽ chạy mãi mãi. Nói thẳng là rất quan trọng, nhưng while(true)bản thân nó không đặc biệt xấu. Các biến có bất biến phức tạp trên các vòng lặp lặp sẽ là một ví dụ về một thứ gây ra nhiều sự giận dữ hơn.
poolie

4
Hãy thử đi sâu ba cấp độ trong một tìm kiếm và sau đó bằng cách nào đó thoát ra. Mã sẽ trở nên tồi tệ hơn nhiều nếu bạn không trở về từ thời điểm đó.
dascandy

11

Đó là súng của bạn, đạn của bạn và chân của bạn ...

Thật tệ vì bạn đang yêu cầu rắc rối. Sẽ không phải là bạn hay bất kỳ người đăng nào khác trên trang này có ví dụ về các vòng lặp ngắn / đơn giản.

Rắc rối sẽ bắt đầu vào một thời điểm rất ngẫu nhiên trong tương lai. Nó có thể được gây ra bởi một lập trình viên khác. Nó có thể là người cài đặt phần mềm. Nó có thể là người dùng cuối.

Tại sao? Tôi đã phải tìm hiểu tại sao một ứng dụng LỘC 700K sẽ dần dần bắt đầu đốt cháy 100% thời gian CPU cho đến khi mọi CPU bị bão hòa. Đó là một vòng lặp while (thật) tuyệt vời. Nó to và khó chịu nhưng nó bị luộc xuống:

x = read_value_from_database()
while (true) 
 if (x == 1)
  ...
  break;
 else if (x ==2)
  ...
  break;
and lots more else if conditions
}

Không có chi nhánh cuối cùng. Nếu giá trị không khớp với điều kiện if thì vòng lặp tiếp tục chạy cho đến hết thời gian.

Tất nhiên, lập trình viên đổ lỗi cho người dùng cuối không chọn được giá trị mà lập trình viên mong đợi. (Sau đó tôi đã loại bỏ tất cả các trường hợp while (true) trong mã.)

IMHO không phải là lập trình phòng thủ tốt để sử dụng các cấu trúc như while (true). Nó sẽ quay trở lại ám ảnh bạn.

(Nhưng tôi nhớ lại các giáo sư chấm điểm nếu chúng tôi không bình luận mọi dòng, ngay cả đối với i ++;)


3
+1 cho nhận xét về lập trình phòng thủ.
JHarnach

3
Trong ví dụ của bạn, mã là ngu ngốc không phải vì sự lựa chọn của thời gian (đúng) mà vì ý tưởng đặt một vòng lặp xung quanh mã.
Florian F

Thật vậy, điểm của vòng lặp đó là gì? :)
juzzlin

6

Thật tệ theo nghĩa là các cấu trúc lập trình có cấu trúc được ưa thích để phá vỡ và tiếp tục các câu lệnh. Họ, bằng cách so sánh, ưa thích "goto" theo nguyên tắc này.

Tôi luôn khuyên bạn nên làm cho mã của mình có cấu trúc nhất có thể ... mặc dù, như Jon Skeet chỉ ra, đừng làm cho nó có cấu trúc nhiều hơn thế!


5

Theo kinh nghiệm của tôi trong hầu hết các trường hợp, các vòng lặp có điều kiện "chính" để tiếp tục. Đây là điều kiện nên được ghi vào chính toán tử while (). Tất cả các điều kiện khác có thể phá vỡ vòng lặp là thứ yếu, không quá quan trọng, v.v. Chúng có thể được viết dưới dạng các if() {break}câu lệnh bổ sung .

while(true) thường khó hiểu và ít đọc hơn.

Tôi nghĩ rằng các quy tắc này không bao gồm 100% các trường hợp nhưng có lẽ chỉ 98% trong số đó.


Nói hay lắm. Nó giống như sử dụng một vòng lặp for với: while (true) {i ++; ...}. Bạn đang chôn vùi điều kiện vòng lặp bên trong vòng lặp chứ không phải trong 'chữ ký'.
Truyền thuyết Bước sóng

3

Mặc dù không nhất thiết phải là một câu trả lời về lý do tại sao không sử dụng while (true), tôi đã luôn tìm thấy truyện tranh này và lời tuyên bố của tác giả đi kèm là một lời giải thích ngắn gọn về lý do tại sao phải làm trong khi thay vì làm trong khi.

Liên quan đến câu hỏi của bạn: Không có vấn đề cố hữu với

while(true) {
   do_stuff();
   if(exit_time) {
      break;
   }
}

... nếu bạn biết những gì bạn đang làm và chắc chắn rằng exit_timemột lúc nào đó sẽ đánh giátrue .

Giáo viên không khuyến khích bạn sử dụng while(true)vì cho đến khi và trừ khi bạn ở điểm mà bạn biết chính xác những gì bạn đang làm, đó là một cách dễ dàng để phạm sai lầm nghiêm trọng.


Tôi nghĩ rằng thiết lập exit_time = false; while(!exit_time) { execute_stuff(); }do { execute_stuff(); } while(! exit_time );cả hai đều rõ ràng hơn nhiều so với việc có một if( condition ) { break; }kết thúc vòng lặp với a while(true). Các ngắt là ngắn mạch đến các vòng lặp - hoàn toàn tốt khi được sử dụng như một mạch ngắn ở giữa một vòng lặp, nhưng bạn chỉ nên đánh giá một điều kiện trong câu lệnh while và ngắt ở cuối vòng lặp.
dr jimbob

Liên quan đến truyện tranh đó: trong khi làm trong khi không thể làm tất cả những gì có thể làm, thì đôi khi làm tốt hơn những gì làm - trong khi có thể làm ngay cả khi đó cũng có thể làm.
Theraot

3

Bạn có thể chỉ cần sử dụng cờ Boolean để cho biết khi nào kết thúc vòng lặp while. Breakgo tolà lý do khiến phần mềm khó bảo trì - khủng hoảng phần mềm (tm) - và nên tránh, và cũng có thể dễ dàng xảy ra.

Đó là một câu hỏi nếu bạn thực dụng hay không. Các lập trình viên thực dụng có thể chỉ sử dụng break trong tình huống đơn giản đó.

Nhưng thật tốt khi bạn không sử dụng chúng, nếu không bạn có thể sử dụng chúng trong các tình huống không phù hợp, như trong các vòng lặp phức tạp, nơi khả năng đọc và duy trì mã của bạn trở nên khó khăn hơn bằng cách sử dụng break.


8
Bởi vì việc giữ một lá cờ boolean có lẽ không rõ ràng hơn, và có thể ít rõ ràng hơn , theo kinh nghiệm của tôi?
Jon Skeet

3
@Jon Skeet Vâng, đó là một câu hỏi về việc đi với một người tốt hoặc tự đào tạo mình một người xấu bằng cách sử dụng nghỉ ngơi. Một bool gọi là "chạy" không rõ ràng với bạn? Rõ ràng, dễ gỡ lỗi của nó, và như tôi đã đề cập trước một hợi tốt là một điều tốt để giữ. Vấn đề ở đâu?
Daniel Leschkowski

4
Một bool được gọi là chạy mà sau đó yêu cầu tôi phải có một if (running)vòng lặp, thụt vào tất cả các phần còn lại của mã khi tất cả những gì tôi muốn là thoát khỏi vòng lặp chắc chắn là ít rõ ràng hơn so với một câu lệnh ngắt đơn giản nêu chính xác những gì tôi muốn làm. Bạn dường như được xem xét sử dụng phá vỡ như một thói quen xấu axiomatically - Tôi không nghĩ về nó như vậy.
Jon Skeet

2
Tại sao bạn có một if (đang chạy) trong vòng lặp? Như với break ở cuối vòng lặp, bạn kiểm tra xem bạn có muốn thoát ra và lật cờ thay vì sử dụng break và sử dụng cờ trong while (đang chạy) thay vì while (true). Tôi không nhận được quan điểm của bạn, nghiêm túc. Tôi đồng ý rằng một lập trình viên thực dụng có thể sử dụng break trong một số tình huống nhất định, nhưng tôi thực sự không nhận được những bình luận mà bạn đưa ra theo đề nghị của tôi
Daniel Leschkowski

3
Bạn cho rằng bạn không cần phải làm bất cứ điều gì trong vòng lặp sau khi bạn đã xác định có nên bỏ hay không. Ví dụ, trong tình huống của OP, anh ta cần yêu cầu thêm đầu vào nếu anh ta không bỏ việc.
Jon Skeet

3

Có lẽ tôi không may mắn. Hoặc có thể tôi chỉ thiếu một kinh nghiệm. Nhưng mỗi khi tôi nhớ lại việc xử lý while(true)breakbên trong, có thể cải thiện mã áp dụng Phương thức trích xuất thành khối chặn , điều này giữ while(true)nhưng (do trùng hợp?) Đã biến tất cả các breaks thành returns.

Theo kinh nghiệm của tôi while(true)mà không nghỉ giải lao (tức là trả lại hoặc ném) khá thoải mái và dễ hiểu.


  void handleInput() {
      while (true) {
          final Input input = getSomeInput();
          if (input == null) {
              throw new BadInputException("can't handle null input");
          }
          if (input.isPoisonPill()) {
              return;
          }
          doSomething(input);
      }
  }

3

Tôi nghĩ rằng có, nó khá tệ ... hoặc ít nhất, đối với nhiều nhà phát triển. Đó là triệu chứng của các nhà phát triển không nghĩ về điều kiện vòng lặp của họ. Hậu quả là dễ bị lỗi.


2

Không có vấn đề lớn với while(true)các breakcâu lệnh, tuy nhiên một số người có thể nghĩ rằng nó hơi làm giảm khả năng đọc mã. Cố gắng đưa ra các biến có ý nghĩa tên, đánh giá biểu thức ở vị trí thích hợp.

Ví dụ của bạn, có vẻ rõ ràng hơn nhiều để làm một cái gì đó như:

do {
   input = get_input();
   valid = check_input_validity(input);    
} while(! valid)

Điều này đặc biệt đúng nếu vòng lặp do while kéo dài - bạn biết chính xác nơi kiểm tra xem có lặp lại thêm hay không. Tất cả các biến / hàm có tên thích hợp ở mức độ trừu tượng. Cácwhile(true) tuyên bố, không có gì cho bạn biết chế biến mà không phải là ở nơi bạn nghĩ.

Có thể bạn muốn đầu ra khác nhau vào lần thứ hai thông qua vòng lặp. Cái gì đó như

input = get_input();
while(input_is_not_valid(input)) {
    disp_msg_invalid_input();
    input = get_input();
}

có vẻ dễ đọc hơn với tôi

do {
    input = get_input();
    if (input_is_valid(input)) {
        break;
    }
    disp_msg_invalid_input();
} while(true);

Một lần nữa, với một ví dụ tầm thường cả hai đều khá dễ đọc; nhưng nếu vòng lặp trở nên rất lớn hoặc được lồng sâu (có nghĩa là bạn có thể đã được tái cấu trúc), kiểu đầu tiên có thể rõ ràng hơn một chút.


1

Tôi sử dụng một cái gì đó tương tự, nhưng với logic ngược lại, trong rất nhiều chức năng của tôi.

DWORD dwError = ERROR_SUCCESS;

do
{
    if ( (dwError = SomeFunction()) != ERROR_SUCCESS )
    {
         /* handle error */
         continue;
    }

    if ( (dwError = SomeOtherFunction()) != ERROR_SUCCESS )
    {
         /* handle error */
         continue;
    }
}
while ( 0 );

if ( dwError != ERROR_SUCCESS )
{
    /* resource cleanup */
}

1

Đó là một điều thẩm mỹ hơn, dễ đọc mã hơn khi bạn biết rõ ràng lý do tại sao vòng lặp sẽ dừng ngay trong khai báo của vòng lặp.


1

Tôi sẽ nói rằng nói chung lý do nó không được coi là một ý tưởng tốt là bạn không sử dụng cấu trúc để phát huy hết tiềm năng. Ngoài ra, tôi có xu hướng nghĩ rằng rất nhiều giảng viên lập trình không thích điều đó khi sinh viên của họ đến với "hành lý". Điều đó có nghĩa là tôi nghĩ rằng họ thích là người có ảnh hưởng chính đến phong cách lập trình của sinh viên. Vì vậy, có lẽ đó chỉ là một tiểu thú cưng của người hướng dẫn.


1

Đối với tôi, vấn đề là khả năng đọc.

Một câu lệnh while với một điều kiện đúng cho bạn biết không có gì về vòng lặp. Nó làm cho công việc hiểu nó khó khăn hơn nhiều.

Điều gì sẽ dễ hiểu hơn trong hai đoạn trích này?

do {
  // Imagine a nice chunk of code here
} while(true);

do {
  // Imagine a nice chunk of code here
} while(price < priceAllowedForDiscount);

0

Tôi đoán sử dụng break cho giáo viên của bạn giống như bẻ cành cây để lấy quả, sử dụng một số thủ thuật khác (cúi cành) để bạn lấy quả và cành vẫn còn sống. :)


0

1) Không có gì sai với do -while(true)

2) Giáo viên của bạn sai.

NSFS !!:

3) Hầu hết giáo viên là giáo viên và không phải lập trình viên.


@Connell đợi cho đến khi giáo viên của bạn nghe thấy điều đó!
Pacerier

1
Tôi tự học trên mặt trận lập trình. Tất cả những gì tôi phải đánh giá là giáo viên CNTT của tôi ở trường cấp hai, người đã dạy chúng tôi tạo trang web bằng Dreamweaver theo cách mà MỌI THỨ là một vị trí tuyệt đối ...
Connell

@ Kết nối bài viết của tôi để thể hiện sự đồng ý của bạn
Pacerier

2
"Những người không thể làm, dạy." - Đó là một khái quát khá lớn, bạn không nghĩ sao? Tất cả các bác sĩ, kỹ sư, phi công, vv đều có thể tự học vì người hướng dẫn của họ không đủ năng lực theo bạn?
filip-fku

@ filip-fku wow wow ~ mát nó mát đấy!
Pacerier

0

Nó có thể là xấu nếu vòng lặp của bạn chạy trên một luồng nền, vì vậy khi bạn đóng ứng dụng của mình bằng cách chấm dứt một luồng UI, đoạn mã đó sẽ tiếp tục thực thi. Như những người khác đã nói, bạn nên luôn luôn sử dụng một số loại kiểm tra để cung cấp cách hủy bỏ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.