Chưa từng thấy trước C ++ cho vòng lặp


164

Tôi đã chuyển đổi thuật toán C ++ sang C #. Tôi đã xem qua vòng lặp này:

for (u = b.size(), v = b.back(); u--; v = p[v]) 
b[u] = v;

Nó không có lỗi trong C ++, nhưng nó không có trong C # (không thể chuyển đổi int thành bool). Tôi thực sự không thể tìm ra điều này cho vòng lặp, điều kiện ở đâu?

Ai đó có thể vui lòng giải thích?

Tái bút Chỉ cần kiểm tra, để điều chỉnh một YAMOR thành LIST, b.back () có tương ứng với b [b.Count-1] không?


35
Điều kiện ở đâu? Đó sẽ là u--. Các dấu chấm phẩy được sử dụng để phân định các phần khác nhau của fortuyên bố.
David Heffernan

77
Đây là một vòng lặp khá bình thường. C # không chuyển đổi số thành bools một cách ngầm định nên bạn cần biến điều kiện thành; u-- != 0;
R. Martinho Fernandes

28
@Jessie Tốt - Mã tốt không liên quan gì đến những gì được phép trong ngôn ngữ, nó phải làm trong bao lâu để một đồng nghiệp không được yêu cầu đọc mã. Nếu nó gây ra bất kỳ loại nhầm lẫn nào, thì đó không phải là giải pháp tốt nhất có thể, ngay cả khi đó là hợp pháp. Thường thì một giải pháp dài dòng tốt hơn nhiều so với một giải pháp ngắn gọn và hầu hết các trình biên dịch sẽ biên dịch theo cùng một cách.
Bill K

18
Tôi hy vọng rằng, sau khi chuyển đổi mã, bạn cung cấp cho các biến tên tốt hơn b, u, v, vv Lý do duy nhất mà họ được đặt tên theo cách này là vì ai đó muốn tìm kiếm thông minh bằng cách làm cho mã của họ không thể đọc được.
Dan

33
@houbysoft: đây là một vấn đề stackoverflow chung. Nếu bạn hỏi một câu hỏi rất chi tiết, được nghiên cứu kỹ lưỡng và thú vị trong một lĩnh vực nghiên cứu cụ thể, dẫn đến một giải pháp cho một vấn đề khó khăn và thú vị, và bạn trả lời một câu hỏi như vậy sau một ngày nghiên cứu khó khăn, bạn sẽ chỉ nhận được một vài hàng chục khách truy cập và một hoặc hai upvote từ một vài chuyên gia trong lĩnh vực này. Nếu bạn muốn đạt được nhiều rep nhanh chóng, bạn phải hỏi và trả lời những câu hỏi như thế này. "Làm cách nào để thêm hai số trong php", " donghĩa là gì trong C ++" - sẽ nhận được hàng ngàn lượt truy cập từ những người mới bắt đầu tìm kiếm một hướng dẫn.
vsz

Câu trả lời:


320

Điều kiện của forvòng lặp nằm ở giữa - giữa hai dấu chấm phẩy ;.

Trong C ++, bạn có thể đặt gần như bất kỳ biểu thức nào dưới dạng điều kiện: mọi thứ đánh giá là 0 false; phương tiện khác không true.

Trong trường hợp của bạn, điều kiện là u--: khi bạn chuyển đổi sang C #, chỉ cần thêm != 0:

for (u = b.size(), v = b.back(); u-- != 0; v = p[v]) 
    b[u] = v; //                     ^^^^ HERE

55
Nhân tiện, Thomas có thể đã bị nhầm lẫn bởi việc sử dụng dấu phẩy, nó rất khác so với dấu chấm phẩy, nó cho phép bạn thực hiện nhiều việc trong một phần của vòng lặp for (trong trường hợp này, nó đã khởi tạo hai biến). Lần trước tôi đã kiểm tra các cấu trúc bất thường này không được coi là giải pháp dễ đọc nhất có thể và do đó có thể bị một số người cau mày.
Bill K

2
Nếu tôi nhớ chính xác và biểu thức có dấu phẩy có giá trị của biểu thức con cuối cùng bên phải. Điều này xuất phát từ C và có thể được sử dụng trong bất kỳ biểu thức nào, không chỉ trong một vòng lặp for.
Giorgio

7
@Roger Đây sẽ không phải là cùng một vòng lặp vì bạn đang giảm u ở cuối vòng lặp, thay vì bắt đầu (tức là sau b [u] = v thay vì trước đó). Trong thực tế, bạn sẽ cần phải khởi tạo nó với u = b.size() - 1thay thế.
Didier L

Fyi: Tôi vừa đẩy bạn vượt quá giới hạn 500K. Có phải giống như là khách hàng thứ một triệu ở đâu đó và chiến thắng đôi khi? Trong mọi trường hợp: nhiều lời chúc mừng; luôn luôn tìm kiếm câu trả lời chính xác, sắc nét của bạn! Tiếp tục đi; gặp bạn làm 1 triệu thứ ... vào cuối thế kỷ này.
GhostCat

165

Rất nhiều câu trả lời chính xác, nhưng tôi nghĩ rằng nó đáng để viết ra vòng lặp while tương đương.

for (u = b.size(), v = b.back(); u--; v = p[v]) 
   b[u] = v;

Tương đương với:

u = b.size();
v = b.back();
while(u--) {
   b[u] = v;
   v = p[v];
}

Bạn có thể xem xét tái cấu trúc sang định dạng while () khi bạn dịch sang C #. Theo tôi thì rõ ràng hơn, ít bẫy hơn cho các lập trình viên mới và hiệu quả không kém.

Như những người khác đã chỉ ra - nhưng để hoàn thành câu trả lời của tôi - để làm cho nó hoạt động trong C #, bạn sẽ cần phải thay đổi while(u--)thành while(u-- != 0).

... Hoặc while(u-- >0)chỉ trong trường hợp bạn bắt đầu tiêu cực. (OK, b.size()sẽ không bao giờ âm tính - nhưng hãy xem xét một trường hợp chung trong đó có lẽ một cái gì đó khác được khởi tạo u).

Hoặc, để làm cho nó rõ ràng hơn:

u = b.size();
v = b.back();
while(u>0) {
   u--;
   b[u] = v;
   v = p[v];
}

Tốt hơn là rõ ràng hơn là ngắn gọn.


29
Câu trả lời này không chỉ làm rõ mã xấu mà còn đưa ra một lựa chọn tốt. 1 lên !!
polvoazul

2
Như một bên, tôi sẽ cẩn thận với các while (u-- >0)hình thức. Nếu khoảng cách bị rối tung, bạn có thể kết thúc bằng một vòng lặp "xuống không" : while (u --> 0), điều này có xu hướng gây nhầm lẫn cho mọi người ngay từ cái nhìn đầu tiên. ( Tôi không chắc chắn nếu nó có giá trị C #, nhưng nó là trong C, và tôi nghĩ rằng nó có thể là cũng trong C ++? )
Izkata

9
Tôi không nghĩ rằng mã của bạn nhất thiết phải rõ ràng hơn. Điểm forthay vì whilechính xác là bạn đặt phần khởi tạo và phần tăng / giảm trong một câu lệnh và điều đó không nhất thiết làm cho mã khó hiểu hơn. Nếu không, chúng ta không nên sử dụng fortất cả.
musiphil

1
Nó cũng quan trọng để viết lại càng ít mã càng tốt. (Rốt cuộc, vòng lặp for có thể thay đổi.) Bạn đã đặt "u--" ở hai vị trí riêng biệt và vòng lặp không thực sự rõ ràng hơn (Tôi có thể thấy vòng lặp for làm gì trong nháy mắt; Tôi phải quét với nhiều dòng). Là terse cũng có lợi ích. Đừng xem thường họ. Tuy nhiên, một ví dụ tốt về cách nó có thể được viết. Nó làm cho nó dễ hiểu hơn đối với bất kỳ ai chưa quen với các câu lệnh trong C ++ (hoặc thậm chí có thể cả).
NotKyon

2
@Izkata: Nó không phải là toán tử, nó được phân tích thành --mã thông báo theo sau là >mã thông báo. Hai toán tử riêng biệt. Vòng lặp "xuống không" chỉ là sự kết hợp đơn giản giữa giảm sau và lớn hơn. Quá tải toán tử C ++ không tạo ra các toán tử mới, nó chỉ tái sử dụng các toán tử hiện có.
Ben Voigt

66

Điều kiện là u--;, bởi vì nó là ở vị trí thứ hai của cho giảng dạy.

Nếu giá trị của u--;khác 0, nó sẽ được hiểu là true(nghĩa là, được đặt ngầm định thành giá trị boolean true). Nếu, thay vào đó, giá trị của nó là 0, nó sẽ được chuyển thành false.

Đây là mã rất xấu .

Cập nhật: Tôi đã thảo luận về cách viết các vòng lặp "cho" trong bài đăng trên blog này . Khuyến nghị của nó có thể được tóm tắt trong các đoạn sau:

Một vòng lặp for là một thực tế, có thể đọc được (một khi bạn đã quen với nó) và cấu trúc ngắn gọn, nhưng bạn cần sử dụng nó tốt. Do cú pháp không phổ biến của nó, sử dụng nó theo cách quá giàu trí tưởng tượng không phải là một ý tưởng tốt.

Tất cả các phần của vòng lặp for phải ngắn và dễ đọc. Tên biến nên được chọn để dễ hiểu.

Ví dụ này rõ ràng vi phạm những đề xuất này.


3
Hoặc nó sẽ dừng lại ở u == 0 có lẽ ...?
Thomas

2
Không; trong C ++ có một chuyển đổi ngầm định từ int sang bool, với 0 chuyển thành false. Vòng lặp sẽ chấm dứt khi u-- == 0. Trong C #, không có chuyển đổi ngầm định như vậy nên bạn phải nói rõ ràng u-- == 0. EDIT: Đây là phản hồi cho nhận xét đầu tiên của bạn.
Chris

48
Đây là mã khủng khiếp , vì một lý do rất đơn giản; bạn không thể dễ dàng hiểu nó khi bạn đọc nó. Đó là "thông minh", một "hack"; nó sử dụng kết hợp các cấu trúc mã hóa và kiến ​​thức về cách chúng hoạt động đằng sau hậu trường, để tạo ra một cấu trúc thực hiện công việc nhưng bất chấp sự hiểu biết, bởi vì nó không ở dạng mà các tác giả ngôn ngữ đã hình dung và được truyền đạt tới hầu hết người sử dụng ngôn ngữ.
KeithS

4
Câu trả lời tuyệt vời. Tôi sẽ +1 nó ... nếu không phải là "Đây là mã rất tệ." tuyên bố;)
Sandman4

14
Tôi không hiểu tại sao nhiều người dường như nghĩ rằng bạn-- thực sự là mã xấu chỉ vì thiếu (ẩn trong C ++) ! = 0 . Chắc chắn bất cứ ai làm việc với mã sẽ hoàn toàn nhận thức được rằng 0 = false, mọi giá trị khác = true . Có nhiều phạm vi hơn cho sự nhầm lẫn liên quan đến trước / tăng sau của u hoặc mọi người có thể cho rằng u = b.size () sẽ luôn luôn thực thi trước v = b.back () (sự hiểu biết của tôi là chuỗi thực thi không được xác định, nhưng tôi đứng để được sửa chữa).
FumbleFingers

23

Đây sẽ là hình thức C # của vòng lặp của bạn.

// back fetches the last element of vector in c++.
for (u = b.size(), v = b.back(); (u--) != 0; v = p[v]) 
{      
  b[u] = v;      
}

Chỉ cần thay thế tương đương cho kích thước () và trở lại ().

Những gì nó làm là đảo ngược danh sách và lưu trữ trong một mảng. Nhưng trong C #, chúng tôi trực tiếp có chức năng xác định hệ thống cho việc này. Vì vậy, bạn không cần phải viết vòng lặp này.

b = b.Reverse().ToArray();

1
bằng cách lấy v = b.back();ra từ intializer, bạn không thay đổi cách thức hoạt động, vì v = p[v]nó bị ghi đè bởi
João Portela

v = p [v] sẽ không có bất kỳ ảnh hưởng nào đến kết quả cuối cùng vì dòng này sẽ thực thi cuối cùng. Và sau đó v không được gọi trong vòng lặp. Dòng này ở đó chỉ để hiển thị cách vòng lặp được chuyển đổi từ c ++ sang C #.
Narendra

1
trong mã c ++ v = b.back();đã được thực thi một lần trước khi các lần lặp bắt đầu và v = p[v]được thực thi ở đầu mỗi lần lặp. Trong đó phiên bản C # v = p[v]vẫn được thực thi ở đầu mỗi lần lặp nhưng v = b.back();được thực hiện ngay sau nó, thay đổi giá trị của vlệnh tiếp theo b[u] = v;. (có thể câu hỏi đã được chỉnh sửa sau khi bạn đọc nó)
João Portela

5
@Rain Vấn đề là v = b.back(). Bạn có nó thực thi ở mỗi lần lặp thay vì chỉ lần đầu tiên - chúng ta không biết điều gì back()xảy ra (có bất kỳ tác dụng phụ nào không? Nó có thay đổi biểu diễn bên trong bkhông?), Vì vậy vòng lặp này không tương đương với vòng lặp trong câu hỏi.
Izkata

1
@Rain Chính xác, hãy xem xét kỹ hơn về nó. Bước khởi tạo chỉ xảy ra một lần trước khi vòng lặp bắt đầu, không phải ở đầu mỗi lần lặp . Mã của bạn sẽ chính xác nếu v = b.back()được di chuyển ra ngoài vòng lặp, phía trên nó. (Ngoài ra, nếu bạn đang cố gắng phản hồi với ai đó, hãy sử dụng @trước tên của họ để chúng tôi nhận được thông báo)
Izkata


14

Điều kiện là kết quả của u--, đó là giá trị của utrước khi nó bị giảm.

Trong C và C ++, an int có thể chuyển đổi thành bool bằng cách ngầm != 0so sánh (0 là false, mọi thứ khác true).

b.back()là phần tử cuối cùng trong một thùng chứa, đó là b[b.size() - 1], khi nào size() != 0.


11

Trong C, mọi thứ khác không nằm truetrong ngữ cảnh "boolean", chẳng hạn như điều kiện kết thúc vòng lặp hoặc câu lệnh điều kiện. Trong C #, bạn phải làm cho kiểm tra rõ ràng : u-- != 0.


Đây không phải là câu hỏi của OP. Anh ta đang hỏi về việc đánh giá tình trạng thiết bị đầu cuối (tức là 'u--' trong trường hợp này).
ApplePie

6

Như đã nói bởi những người khác, thực tế là C ++ đã ẩn truyền sang boolean có nghĩa là điều kiện là u--, điều này sẽ đúng nếu giá trị khác không.

Thật đáng để thêm vào, rằng bạn đã có một giả định sai lầm khi hỏi "đâu là điều kiện". Trong cả C ++ và C # (và các ngôn ngữ được cú pháp tương tự khác), bạn có thể có một điều kiện trống. Trong trường hợp này nó luôn luôn trả về true, vì vậy vòng lặp tiếp tục mãi mãi, hoặc cho đến khi một số lối ra tình trạng khác nó (thông qua return, breakhoặc throw).

for(int i = 0; ; ++i)
  doThisForever(i);

Thật vậy, bất kỳ phần nào của câu lệnh for có thể bị bỏ qua, trong trường hợp đó, nó chỉ không được thực hiện.

Nói chung, for(A; B; C){D}hoặc for(A; B; C)D;trở thành:

{A}
loopBack:
if(!(B))
  goto escapeLoop;
{D}
{C}
goto loopBack;
escapeLoop:

Bất kỳ một hoặc nhiều A, B, C hoặc D có thể bị bỏ lại.

Kết quả của điều này, một số ủng hộ for(;;) cho các vòng lặp vô hạn. Tôi làm bởi vì trong khi while(true)phổ biến hơn, tôi đọc rằng "cho đến khi sự thật kết thúc là sự thật", nghe có vẻ hơi tận thế so với việc tôi đọc for(;;) là "mãi mãi".

Đó là vấn đề của hương vị, nhưng vì tôi không phải là người duy nhất trên thế giới thích for(;;)nó nên biết ý nghĩa của nó.


4

tất cả các câu trả lời đều đúng: -

cho vòng lặp có thể được sử dụng theo nhiều cách như sau:

Single Statement inside For Loop
Multiple Statements inside For Loop
No Statement inside For Loop
Semicolon at the end of For Loop
Multiple Initialization Statement inside For
Missing Initialization in For Loop
Missing Increment/Decrement Statement
Infinite For Loop
Condition with no Conditional Operator.

4
for (u = b.size(), v = b.back(); u--; v = p[v]) 
   b[u] = v;

Trong mã trên, uvđược khởi tạo với b.size()b.back().

Mỗi khi điều kiện được kiểm tra, nó cũng thực thi câu lệnh giảm dần u--.

Các forvòng lặp sẽ thoát khi usẽ trở thành 0.


3

Lỗi gặp phải trong chính C # sẽ xóa tan nghi ngờ. Các tìm kiếm vòng lặp cho một

SAI

điều kiện để chấm dứt. Và như chúng ta đã biết,

(BOOL) SAI = (int) 0

nhưng C # không thể tự xử lý việc này không giống như C ++. Vì vậy, điều kiện bạn đang tìm kiếm là

bạn--

nhưng bạn phải đưa ra điều kiện trong C # một cách rõ ràng

u--! = 0

hoặc là

u--> 0

Nhưng vẫn cố gắng tránh loại thực hành mã hóa này. Các

trong khi lặp lại

nêu ở trên trong câu trả lời là một trong những phiên bản đơn giản nhất của bạn

cho vòng lặp.


@Downvoter: Downvot là ổn khi bạn không hài lòng với giải pháp nhưng đồng thời, xin vui lòng dành thời gian để nêu lý do, để câu trả lời có thể được cải thiện.
Abhineet

3

Nếu bạn đã quen với C / C ++ thì mã này không quá khó đọc, mặc dù nó khá ngắn gọn và không phải là mã tuyệt vời. Vì vậy, hãy để tôi giải thích những phần có nhiều Cism hơn bất cứ điều gì khác. Trước hết, cú pháp chung của vòng lặp C for trông như thế này:

for (<initialization> ; <condition>; <increment>)
{
    <code...>
}

Mã khởi tạo được chạy một lần. Sau đó, điều kiện được kiểm tra trước mỗi vòng lặp và cuối cùng là gia số được gọi sau mỗi vòng lặp. Vì vậy, trong ví dụ của bạn, bạn sẽ tìm thấy điều kiện làu--

Tại sao u--hoạt động như một điều kiện trong C mà không phải C #? Bởi vì C ngầm chuyển đổi rất nhiều thứ quá đỗi và nó có thể gây rắc rối. Đối với một số bất cứ điều gì khác không là đúng và không là sai. Vì vậy, nó sẽ đếm ngược từ b.size () - 1 đến 0. Có tác dụng phụ trong điều kiện là một chút khó chịu và tốt hơn là đặt nó trong phần tăng của vòng lặp for, mặc dù rất nhiều C mã làm điều này. Nếu tôi viết nó tôi sẽ làm nó giống như thế này:

for (u = b.size() - 1, v = b.back(); u>=0; --u) 
{
    b[u] = v;
    v = p[v]
}

Lý do cho điều này là, với tôi ít nhất, nó rõ ràng hơn. Mỗi phần của vòng lặp for thực hiện công việc của nó và không có gì khác. Trong mã ban đầu, điều kiện là sửa đổi biến. Phần tăng đã làm một cái gì đó nên có trong khối mã, v.v.

Toán tử dấu phẩy cũng có thể ném cho bạn một vòng lặp. Trong C, một cái gì đó x=1,y=2trông giống như một câu lệnh liên quan đến trình biên dịch và phù hợp với mã khởi tạo. Nó chỉ đánh giá từng bộ phận và trả về giá trị của phần cuối cùng. Ví dụ:

std::cout << "(1,2)=" << (1,2) << std::endl;

sẽ in ra 2.


Vấn đề với việc viết lại của bạn là nếu b.size () không được ký, nó sẽ lặp lại trong một thời gian rất dài. (Ngoài ra, bạn đang thiếu một dấu chấm phẩy.) Nhưng ít nhất bạn đã không sử dụng cách tiếp cận "'Dick and Jane' là tiếng Anh tốt" mà rất nhiều câu trả lời và nhận xét khác đã làm, việc viết lại những lời viết dài dòng vô lý chỉ dễ dàng hơn để đọc bởi neophyte và các lập trình viên không có kỹ năng khác.
Jim Balter
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.