Có lý do kỹ thuật nào để sử dụng> (<) thay vì! = Khi tăng thêm 1 trong vòng lặp 'for' không?


198

Tôi gần như không bao giờ thấy một forvòng lặp như thế này:

for (int i = 0; 5 != i; ++i)
{}

Có một lý do kỹ thuật để sử dụng >hoặc <thay vì !=khi tăng 1 trong một forvòng lặp? Hoặc đây là nhiều hơn một quy ước?


104
Nó làm cho nó dễ đọc hơn, cộng với, nếu bạn thay đổi i++thành i+=2(ví dụ), nó sẽ chạy trong một thời gian rất dài (hoặc có thể là mãi mãi). Bây giờ, vì bạn thường sử dụng <cho những trường hợp bạn tăng số vòng lặp lên hơn 1, nên bạn cũng có thể sử dụng <cho trường hợp bạn tăng nó lên 1 (để thống nhất).
barak manos 16/07/2015

112
5 != ilà xấu xa
Slava

27
Bạn nên luôn luôn ý thức về những gì bạn đang làm, nhưng dù sao bạn cũng sẽ phạm sai lầm. Sử dụng <làm giảm khả năng ai đó đưa lỗi vào mã này.
Aurast 16/07/2015

40
@OleTange Nếu bạn đang lặp lại bằng cách sử dụng các số dấu phẩy động, bạn đã bị lừa ..
CaptainCodeman

29
@Slava ... không ác. Bạn dường như chưa bao giờ được bit trong C bởi một lỗi đánh máy đơn giản tạo ra sự khác biệt giữa if ( i == 5 ) ...if ( i = 5 ). Những điều rất khác nhau. Việc đảo ngược các toán hạng sẽ ngăn ngừa vấn đề: vì nghĩa đen là không lvals, chúng không thể được gán cho và trình biên dịch nhanh chóng đánh máy. @Zingam: lập trình phòng thủ tốt!
Nicholas Carey

Câu trả lời:


303
while (time != 6:30pm) {
    Work();
}

Bây giờ là 6:31 chiều ... Chết tiệt, bây giờ cơ hội tiếp theo của tôi là về nhà vào ngày mai! :)

Điều này cho thấy rằng hạn chế mạnh mẽ hơn giảm thiểu rủi ro và có thể trực quan hơn để hiểu.


25
Đôi khi, cố gắng "giảm thiểu rủi ro" cuối cùng lại che giấu lỗi. Nếu thiết kế của vòng lặp sẽ loại trừ 6:30 chiều do trượt mà không được chú ý, thì việc sử dụng <sẽ che giấu lỗi nhưng việc sử dụng !=sẽ cho thấy rõ ràng có vấn đề.
Adrian McCarthy

27
@AdrianMcCarthy: Không nhất thiết; nó chỉ có thể giới thiệu một lỗi thứ hai, làm cho chẩn đoán khó hơn .
Các cuộc đua nhẹ nhàng trong quỹ đạo

4
@AdrianMcCarthy: Đó là quan điểm của tôi; bạn có thể có một vài ví dụ về nó mà bạn chưa từng thấy;)
Các cuộc đua Lightness trong quỹ đạo

6
Iterators là một ví dụ hoàn hảo về điểm @AdrianMcCarthy tôi nghĩ. So sánh được đề xuất cho họ là! = Thay vì <.
Taekahn

5
Tôi đã thấy chính xác lỗi này (và nó gây ra 3 tháng gỡ lỗi) - nhưng câu trả lời này hoàn toàn sai điểm. Trong ví dụ cho nó là bất khả thi đối với các điều kiện chấm dứt thể bỏ qua. Bạn thậm chí có thể nói rằng có ý thức lựa chọn! = Over <là để khẳng định mạnh mẽ thực tế này. Giảm thiểu rủi ro mà chắc chắn không tồn tại là một mùi mã đối với tôi. (Đã nói rằng, bạn cũng có thể lập luận rằng <là thành ngữ, đó là lý do chính đáng để sử dụng nó như bất kỳ.)
Ian Goldby 20/07/2015

95

Không có lý do kỹ thuật. Nhưng có giảm thiểu rủi ro, khả năng duy trì và hiểu rõ hơn về mã.

<hoặc >là những hạn chế mạnh hơn !=và hoàn thành cùng một mục đích trong hầu hết các trường hợp (tôi thậm chí sẽ nói trong tất cả các trường hợp thực tế).

Có câu hỏi trùng lặp ở đây ; và một câu trả lời thú vị .


6
Có một số loại, chẳng hạn như các trình vòng lặp, không hỗ trợ <hoặc>, nhưng thực hiện hỗ trợ == và! =.
Simon B

1
Đó là một vòng lặp for với int . Không lặp.
Ely

7
"Nhưng có giảm thiểu rủi ro, khả năng duy trì và hiểu rõ hơn về mã." Tất cả đều, đúng, lý do kỹ thuật. :-)
TJ Crowder

@TJCrowder Loại lý do là gì khi bạn chỉ muốn nói về những gì các máy sẽ làm kết quả?
Brilliand

1
@DanSheppard Tôi thường coi việc giảm thiểu rủi ro và khả năng duy trì là lý do kinh doanh và hiểu rõ hơn về mã là một lý do xã hội (mặc dù những lý do này đều được áp dụng cho vấn đề kỹ thuật trong trường hợp này). Các cân nhắc "Giảm thiểu rủi ro" không bị giới hạn trong các vấn đề kỹ thuật và các cân nhắc "hiểu rõ hơn về mã" có thể thay đổi nếu bạn thấy mình làm việc với một nhóm người khác (ngay cả khi các máy liên quan không thay đổi gì cả ).
Brilliand

85

Vâng, có một lý do. Nếu bạn viết một (chỉ mục cũ đơn giản) cho vòng lặp như thế này

for (int i = a; i < b; ++i){}

sau đó, nó hoạt động như mong đợi cho bất kỳ giá trị nào của ab(tức là số lần lặp bằng 0 khi a > bthay vì vô hạn nếu bạn đã sử dụngi == b; ).

Mặt khác, đối với các trình vòng lặp bạn viết

for (auto it = begin; it != end; ++it) 

bởi vì bất kỳ trình vòng lặp nào cũng nên thực hiện một operator!=, nhưng không phải với mọi trình vòng lặp, nó có thể cung cấp mộtoperator< .

Cũng dựa trên phạm vi cho các vòng lặp

for (auto e : v)

không chỉ là đường ưa thích, mà còn làm giảm đáng kể cơ hội viết mã sai.


4
Ví dụ tốt đẹp. Tôi sẽ quan tâm đến một trường hợp !=thay vì <. Cá nhân tôi không bao giờ sử dụng mà tôi nghĩ.
Ely

@Elyasin Tôi không tìm thấy cái nào tốt và tôi không muốn đăng cái xấu: Hãy tưởng tượng bạn có một loại container tròn, trong đó bạn tăng thêm + x modulo kích thước của container N và bạn muốn dừng vòng lặp khi bạn đạt đến một chỉ số nhất định .... đó là một ví dụ thực sự xấu. Có lẽ tôi sẽ tìm thấy một thứ tốt hơn
idclev 463035818 16/07/2015

Vòng lặp tương tự với! = Thay vì <làm rõ lợi thế của <(hoặc>) là gì trong bối cảnh này. for (int i=a; i != b; i++) {}
Paul Smith

10
Sử dụng bất cứ thứ gì ngoài !=iterators đều khá nguy hiểm, bởi vì đôi khi các iterator có thể ít hơn so với (ví dụ đó là một con trỏ) nhưng so sánh là vô nghĩa (đó là một danh sách được liên kết).
Zan Lynx

1
Học cách sử dụng khoảng trắng, nghiêm túc.
Miles Rout

70

Bạn có thể có một cái gì đó như

for(int i = 0; i<5; ++i){
    ...
    if(...) i++;
    ...
}

Nếu biến vòng lặp của bạn được viết bởi mã bên trong, thì i!=5 có thể không phá vỡ vòng lặp đó. Điều này là an toàn hơn để kiểm tra sự bất bình đẳng.

Chỉnh sửa về khả năng đọc. Các hình thức bất bình đẳng là cách thường xuyên hơn được sử dụng. Do đó, điều này rất nhanh để đọc vì không có gì đặc biệt để hiểu (tải não giảm vì nhiệm vụ là phổ biến). Vì vậy, thật tuyệt cho độc giả để sử dụng những thói quen này.


6
Kiểu bên trong "if (condition) then i ++" là điều đầu tiên tôi nghĩ đến.
WernerCD 16/07/2015

3
Đã đến khi hy vọng đây sẽ là câu trả lời được bình chọn cao nhất; Tôi đã ngạc nhiên khi tôi phải di chuyển đến đây để tìm thấy nó. Điều này không ảnh hưởng nhiều đến việc thuyết phục một lập trình viên mới làm quen hơn là "tốt, bạn nên sử dụng điều kiện mạnh nhất". Các câu trả lời khác, nếu chúng luôn đứng đầu, nên kết hợp điều này như một lời giải thích rõ ràng và rõ ràng về những gì có thể sai với mã đề xuất của OP.
msouth

1
@msouth Tôi hoàn toàn đồng ý, nhưng POV của tôi khá chủ quan;)
johan d

3
@msouth Tôi thích nó khi hành vi sản xuất bất ngờ phơi bày lỗi của tôi, phải không? :-)
deworde 20/07/2015

3
@msouth Hãy nhìn xem, tất cả những gì chúng ta phải làm là không bao giờ mắc lỗi nào và mọi thứ sẽ ổn. Đơn giản.
deworde 21/07/2015

48

Và cuối cùng nhưng không kém phần quan trọng, đây được gọi là lập trình phòng thủ , nghĩa là luôn luôn lấy trường hợp mạnh nhất để tránh các lỗi hiện tại và tương lai ảnh hưởng đến chương trình.

Trường hợp duy nhất không cần lập trình phòng thủ là nơi các quốc gia đã được chứng minh bằng các điều kiện trước và sau (nhưng sau đó, chứng minh rằng đây là cách phòng thủ nhất trong tất cả các chương trình).


2
Một ví dụ điển hình hỗ trợ cho điều này: Bộ nhớ dễ bị tổn thương khi lật bit ( SEU ) do bức xạ. Khi sử dụng intđối với một i != 15điều kiện, bộ đếm sẽ được chạy trong một thời gian dài nếu bất cứ điều gì trên các bit thứ tư là lộn, đặc biệt là trên máy nơi sizeof(int)là 64. SEUS là một mối quan tâm rất thực tế ở độ cao hơn hoặc trong không gian (vì bức xạ cao hơn) hoặc trong các siêu máy tính lớn (vì có quá nhiều bộ nhớ).
Josh Sanford

2
Nghĩ rằng chương trình của bạn sẽ chạy ổn khi bức xạ thay đổi trí nhớ của bạn là một cách lạc quan và cách suy nghĩ chống khủng hoảng ... bình luận tốt! :)
Luis Colorado

37

Tôi sẽ tranh luận rằng một biểu thức như

for ( int i = 0 ; i < 100 ; ++i )
{
  ...
}

biểu hiện của ý định nhiều hơn là

for ( int i = 0 ; i != 100 ; ++i )
{
  ...
}

Cái trước rõ ràng gọi rằng điều kiện này là một thử nghiệm cho giới hạn trên độc quyền trên một phạm vi; cái sau là một bài kiểm tra nhị phân của một điều kiện thoát. Và nếu phần thân của vòng lặp là không tầm thường, có thể không rõ ràng rằng chỉ mục chỉ được sửa đổi trong forchính câu lệnh.


13
"Tôi muốn vòng lặp này chạy tối đa 5 lần" so với "Tôi muốn chạy vòng lặp này nhiều lần nếu cần, ngoại trừ chính xác 5 lần, điều mà tôi không muốn."
giám mục

+1 để đề cập đến giới hạn trên trên một phạm vi. Tôi nghĩ rằng điều này là quan trọng đối với phạm vi số. ! = không xác định phạm vi thích hợp trong vòng lặp for như thế này
tử11

22

Lặp lại là một trường hợp quan trọng khi bạn thường xuyên sử dụng !=ký hiệu:

for(auto it = vector.begin(); it != vector.end(); ++it) {
 // do stuff
}

Cấp: trong thực tế tôi sẽ viết tương tự dựa trên một range-for:

for(auto & item : vector) {
 // do stuff
}

nhưng điểm vẫn còn: người ta thường so sánh các trình vòng lặp bằng cách sử dụng ==hoặc !=.


2
Các trình vòng lặp thường chỉ có một khái niệm về đẳng thức / bất bình đẳng và không phải là một trật tự, đó là lý do tại sao bạn sử dụng !=chúng. Ví dụ, trong một std::vectorbạn có thể sử dụng <như iterator được gắn với chỉ mục / con trỏ, nhưng trong std::setđó không có thứ tự nghiêm ngặt như vậy, vì nó đi trên một cây nhị phân.
Martin C.

19

Điều kiện vòng lặp là một bất biến vòng lặp bắt buộc.

Giả sử bạn không nhìn vào phần thân của vòng lặp:

for (int i = 0; i != 5; ++i)
{
  // ?
}

trong trường hợp này, bạn biết khi bắt đầu vòng lặp ikhông bằng nhau 5.

for (int i = 0; i < 5; ++i)
{
  // ?
}

trong trường hợp này, bạn biết khi bắt đầu vòng lặp inhỏ hơn 5.

Thứ hai là nhiều, nhiều thông tin hơn thứ nhất, phải không? Bây giờ, ý định của lập trình viên là (gần như chắc chắn) giống nhau, nhưng nếu bạn đang tìm kiếm lỗi, có sự tự tin từ việc đọc một dòng mã là một điều tốt. Và lần thứ hai thi hành bất biến đó, có nghĩa là một số lỗi sẽ cắn bạn trong trường hợp đầu tiên không thể xảy ra (hoặc không gây ra tham nhũng bộ nhớ, trong trường hợp thứ hai).

Bạn biết nhiều hơn về trạng thái của chương trình, từ việc đọc ít mã hơn, với <hơn!= . Và trên các CPU hiện đại, chúng mất cùng thời gian như không có sự khác biệt.

Nếu bạn ikhông bị thao túng trong thân vòng lặp nó luôn tăng 1 nó bắt đầu ít hơn5 , sẽ không có sự khác biệt. Nhưng để biết liệu nó có bị thao túng hay không, bạn phải xác nhận từng sự thật này.

Một số trong những sự thật này là tương đối dễ dàng, nhưng bạn có thể nhận được sai. Kiểm tra toàn bộ cơ thể của vòng lặp, tuy nhiên, là một nỗi đau.

Trong C ++, bạn có thể viết một indexesloại sao cho:

for( const int i : indexes(0, 5) )
{
  // ?
}

thực hiện tương tự như một trong hai forvòng lặp ở trên , thậm chí xuống trình biên dịch tối ưu hóa nó xuống cùng một mã. Tuy nhiên, ở đây, bạn biết rằng ikhông thể thao tác trong phần thân của vòng lặp, vì nó được khai báo constmà không có mã làm hỏng bộ nhớ.

Càng nhiều thông tin bạn có thể thoát ra khỏi một dòng mã mà không cần phải hiểu ngữ cảnh, thì càng dễ dàng theo dõi những gì đang xảy ra. <trong trường hợp các vòng lặp số nguyên cung cấp cho bạn nhiều thông tin về trạng thái của mã tại dòng đó hơn !=.



13

Có thể xảy ra là biến iđược đặt thành một giá trị lớn và nếu bạn chỉ sử dụng !=toán tử, bạn sẽ kết thúc trong một vòng lặp vô tận.


1
Không thực sự vô tận - trên hầu hết các triển khai của C, isẽ âm thầm bao quanh khi ở mức tối đa. Nhưng vâng, rất có thể lâu hơn bạn chuẩn bị chờ đợi.
usr2564301

12

Như bạn có thể thấy từ rất nhiều câu trả lời khác, có những lý do để sử dụng <thay vì! = Sẽ giúp ích trong các trường hợp cạnh, điều kiện ban đầu, sửa đổi bộ đếm vòng lặp ngoài ý muốn, v.v ...

Thành thật mà nói, tôi không nghĩ bạn có thể nhấn mạnh tầm quan trọng của quy ước. Trong ví dụ này, nó sẽ đủ dễ dàng để các lập trình viên khác thấy những gì bạn đang cố gắng làm, nhưng nó sẽ gây ra một cú đúp. Một trong những công việc trong khi lập trình là làm cho nó dễ đọc và quen thuộc với mọi người nhất có thể, do đó, chắc chắn khi ai đó phải cập nhật / thay đổi mã của bạn, bạn sẽ không mất nhiều công sức để tìm ra những gì bạn đang làm trong các khối mã khác nhau . Nếu tôi thấy ai đó sử dụng !=, tôi cho rằng có một lý do họ đã sử dụng nó thay vì <và nếu đó là một vòng lặp lớn, tôi sẽ xem xét toàn bộ sự cố gắng để tìm ra những gì bạn đã làm điều đó cần thiết ... và đó là lãng phí thời gian


12

Như Ian Newson đã nói, bạn không thể lặp lại một cách đáng tin cậy một biến nổi và thoát với !=. Ví dụ,

for (double x=0; x!=1; x+=0.1) {}

sẽ thực sự lặp lại mãi mãi, bởi vì 0,1 chính xác không thể được biểu diễn trong dấu phẩy động, do đó bộ đếm sẽ bỏ lỡ 1. Với <nó chấm dứt.

(Tuy nhiên, lưu ý rằng về cơ bản đó là hành vi không xác định cho dù bạn có nhận được 0.9999 ... là số được chấp nhận cuối cùng - loại vi phạm giả định ít hơn - hoặc đã thoát tại 1.0000000000000001.)


10

Tôi lấy tính từ "kỹ thuật" để chỉ hành vi / quirks ngôn ngữ và tác dụng phụ của trình biên dịch, chẳng hạn như hiệu suất của mã được tạo.

Cuối cùng, câu trả lời là: không (*). (*) Là "vui lòng tham khảo hướng dẫn sử dụng bộ xử lý của bạn". Nếu bạn đang làm việc với một số hệ thống RISC hoặc FPGA trường hợp cạnh, bạn có thể cần kiểm tra xem hướng dẫn nào được tạo và giá của chúng. Nhưng nếu bạn đang sử dụng khá nhiều bất kỳ kiến trúc hiện đại thông thường, thì không có sự khác biệt mức xử lý đáng kể về chi phí giữa lt, eq, negt.

Nếu bạn đang sử dụng một trường hợp cạnh bạn có thể thấy rằng !=đòi hỏi ba hoạt động ( cmp, not, beq) vs hai ( cmp, blt xtr myo). Một lần nữa, RTM trong trường hợp đó.

Đối với hầu hết các phần, lý do là phòng thủ / cứng, đặc biệt là khi làm việc với con trỏ hoặc vòng lặp phức tạp. Xem xét

// highly contrived example
size_t count_chars(char c, const char* str, size_t len) {
    size_t count = 0;
    bool quoted = false;
    const char* p = str;
    while (p != str + len) {
        if (*p == '"') {
            quote = !quote;
            ++p;
        }
        if (*(p++) == c && !quoted)
            ++count;
    }
    return count;
}

Một ví dụ ít gây tranh cãi sẽ là nơi bạn đang sử dụng các giá trị trả về để thực hiện gia số, chấp nhận dữ liệu từ người dùng:

#include <iostream>
int main() {
    size_t len = 5, step;
    for (size_t i = 0; i != len; ) {
        std::cout << "i = " << i << ", step? " << std::flush;

        std::cin >> step;
        i += step; // here for emphasis, it could go in the for(;;)
    }
}

Hãy thử điều này và nhập các giá trị 1, 2, 10, 999.

Bạn có thể ngăn chặn điều này:

#include <iostream>
int main() {
    size_t len = 5, step;
    for (size_t i = 0; i != len; ) {
        std::cout << "i = " << i << ", step? " << std::flush;
        std::cin >> step;
        if (step + i > len)
            std::cout << "too much.\n";
        else
            i += step;
    }
}

Nhưng những gì bạn có thể muốn là

#include <iostream>
int main() {
    size_t len = 5, step;
    for (size_t i = 0; i < len; ) {
        std::cout << "i = " << i << ", step? " << std::flush;
        std::cin >> step;
        i += step;
    }
}

Ngoài ra còn có một điều gì đó thiên về quy ước <, bởi vì việc đặt hàng trong các thùng chứa tiêu chuẩn thường dựa vào operator<, ví dụ băm trong một số container STL xác định sự bằng nhau bằng cách nói

if (lhs < rhs) // T.operator <
    lessthan
else if (rhs < lhs) // T.operator < again
    greaterthan
else
    equal

Nếu lhsrhslà một lớp do người dùng định nghĩa viết mã này là

if (lhs < rhs) // requires T.operator<
    lessthan
else if (lhs > rhs) // requires T.operator>
    greaterthan
else
    equal

Người thực hiện phải cung cấp hai chức năng so sánh. Vì vậy, <đã trở thành nhà điều hành ưa thích.


Để dễ đọc, i + = bước nên nằm trong phần điều khiển. Nếu bạn sẽ bước = 0
Mikkel Christiansen

1
Trong khi điều đó sẽ giúp dễ đọc mã tốt, tôi muốn nhấn mạnh lỗ hổng trong phiên bản lỗi
kfsone

9

Có một số cách để viết bất kỳ loại mã nào (thông thường), có hai cách trong trường hợp này (ba nếu bạn đếm <= và> =).

Trong trường hợp này, mọi người thích> và <để đảm bảo rằng ngay cả khi có điều gì đó bất ngờ xảy ra trong vòng lặp (như lỗi), nó sẽ không lặp lại vô hạn (BAD). Hãy xem xét các mã sau đây, ví dụ.

for (int i = 1; i != 3; i++) {
    //More Code
    i = 5; //OOPS! MISTAKE!
    //More Code
}

Nếu chúng ta sử dụng (i <3), chúng ta sẽ an toàn khỏi một vòng lặp vô hạn vì nó đặt một hạn chế lớn hơn.

Đó thực sự là lựa chọn của bạn cho dù bạn muốn một lỗi trong chương trình của mình để tắt toàn bộ sự việc hoặc tiếp tục hoạt động với lỗi ở đó.

Hy vọng điều này sẽ giúp!


người ta có thể lập luận rằng vòng lặp vô hạn sẽ là một chỉ báo tốt về sai lầm, nhưng sau đó một người khác sẽ lập luận rằng i = 5 không nhất thiết là một sai lầm
njzk2 20/07/2015

7

Lý do phổ biến nhất để sử dụng <là quy ước. Nhiều lập trình viên nghĩ về các vòng lặp như thế này là "trong khi chỉ số nằm trong phạm vi" thay vì "cho đến khi chỉ số đạt đến cuối". Có giá trị gắn bó với quy ước khi bạn có thể.

Mặt khác, nhiều câu trả lời ở đây đang tuyên bố rằng sử dụng <biểu mẫu giúp tránh lỗi. Tôi tranh luận rằng trong nhiều trường hợp, điều này chỉ giúp che giấu lỗi. Nếu chỉ số vòng lặp được cho là đạt đến giá trị cuối, và thay vào đó, nó thực sự vượt xa nó, thì có điều gì đó xảy ra mà bạn không mong đợi có thể gây ra sự cố (hoặc là tác dụng phụ của một lỗi khác). Các <khả năng sẽ trì hoãn phát hiện ra lỗi. Điều !=này có nhiều khả năng dẫn đến một gian hàng, treo, hoặc thậm chí là một sự cố, sẽ giúp bạn phát hiện ra lỗi sớm hơn. Càng phát hiện sớm lỗi, giá càng rẻ để sửa.

Lưu ý rằng quy ước này là đặc thù cho việc lập chỉ mục mảng và vector. Khi duyệt qua gần như bất kỳ loại cấu trúc dữ liệu nào khác, bạn sẽ sử dụng một trình vòng lặp (hoặc con trỏ) và kiểm tra trực tiếp giá trị cuối. Trong những trường hợp đó, bạn phải chắc chắn rằng trình lặp sẽ đạt và không vượt quá giá trị cuối thực tế.

Ví dụ: nếu bạn đang bước qua một chuỗi C đơn giản, thông thường sẽ phổ biến hơn để viết:

for (char *p = foo; *p != '\0'; ++p) {
  // do something with *p
}

hơn

int length = strlen(foo);
for (int i = 0; i < length; ++i) {
  // do something with foo[i]
}

Đối với một điều, nếu chuỗi rất dài, hình thức thứ hai sẽ chậm hơn bởi vì strlenmột chuỗi khác đi qua chuỗi.

Với chuỗi C ++ std ::, bạn sẽ sử dụng vòng lặp dựa trên phạm vi, thuật toán tiêu chuẩn hoặc trình lặp, ngay cả khi độ dài có sẵn. Nếu bạn đang sử dụng các trình vòng lặp, quy ước là sử dụng !=chứ không phải là< , như trong:

for (auto it = foo.begin(); it != foo.end(); ++it) { ... }

Tương tự, việc lặp lại một cây hoặc một danh sách hoặc một deque thường liên quan đến việc xem một con trỏ null hoặc các câu lệnh khác thay vì kiểm tra xem một chỉ mục có nằm trong phạm vi không.


3
Bạn hoàn toàn đúng khi nhấn mạnh tầm quan trọng của thành ngữ trong lập trình. Nếu tôi nhìn vào một số mã và nó có một mẫu quen thuộc, tôi không cần lãng phí thời gian để suy ra mẫu đó, nhưng có thể đi thẳng vào cốt lõi của nó. Tôi đoán đó là sự kìm kẹp chính của tôi với các so sánh Yoda - chúng không có vẻ gì đặc biệt, vì vậy tôi cuối cùng phải đọc chúng hai lần để chắc chắn rằng chúng có nghĩa là những gì tôi nghĩ.
Toby Speight

7

Một lý do không sử dụng cấu trúc này là số dấu phẩy động. !=là một so sánh rất nguy hiểm khi sử dụng với phao vì nó hiếm khi được đánh giá là đúng ngay cả khi các con số trông giống nhau. <hoặc >loại bỏ rủi ro này.


Nếu vòng lặp vòng lặp của bạn là một số dấu phẩy động, thì !=so với <là vấn đề nhỏ nhất của bạn.
Lundin

7

Có hai lý do liên quan để tuân theo thực tiễn này mà cả hai đều phải làm với thực tế là ngôn ngữ lập trình suy cho cùng là ngôn ngữ sẽ được con người đọc (trong số những ngôn ngữ khác).

(1) Một chút dư thừa. Trong ngôn ngữ tự nhiên, chúng tôi thường cung cấp nhiều thông tin hơn mức cần thiết, giống như mã sửa lỗi. Ở đây thông tin bổ sung là biến vòng lặp i(xem cách tôi sử dụng dự phòng ở đây? Nếu bạn không biết 'biến vòng lặp' nghĩa là gì, hoặc nếu bạn quên tên của biến, sau khi đọc "biến vòng lặp"i ", bạn có đầy đủ thông tin) nhỏ hơn 5 trong vòng lặp, không chỉ khác với 5. Dự phòng tăng cường khả năng đọc.

(2) Công ước. Ngôn ngữ có những cách tiêu chuẩn cụ thể để diễn đạt những tình huống nhất định. Nếu bạn không tuân theo cách thiết lập để nói điều gì đó, bạn vẫn sẽ được hiểu, nhưng nỗ lực cho người nhận tin nhắn của bạn sẽ lớn hơn vì những tối ưu hóa nhất định sẽ không hiệu quả. Thí dụ:

Đừng nói chuyện xung quanh mash nóng. Chỉ cần chiếu sáng những khó khăn!

Câu đầu tiên là một bản dịch nghĩa đen của một thành ngữ tiếng Đức. Thứ hai là một thành ngữ tiếng Anh thông dụng với các từ chính được thay thế bằng các từ đồng nghĩa. Kết quả là dễ hiểu nhưng mất nhiều thời gian để hiểu hơn thế này:

Đừng đánh quanh bụi rậm. Chỉ cần giải thích vấn đề!

Điều này đúng ngay cả trong trường hợp các từ đồng nghĩa được sử dụng trong phiên bản đầu tiên xảy ra để phù hợp với tình huống tốt hơn các từ thông thường trong thành ngữ tiếng Anh. Các lực tương tự có hiệu lực khi lập trình viên đọc mã. Đây cũng là lý do tại sao 5 != i5 > ilà những cách kỳ lạ để đặt nó trừ khi bạn đang làm việc trong một môi trường mà nó là tiêu chuẩn để trao đổi bình thường hơn i != 5i < 5theo cách này. Các cộng đồng phương ngữ như vậy tồn tại, có lẽ bởi vì tính nhất quán làm cho việc ghi nhớ dễ dàng hơn 5 == ithay vì tự nhiên nhưng dễ bị lỗi i == 5.


1
Ausgezeichnet . Tương tự, người ta sẽ không viết bài kiểm tra như i < 17+(36/-3)(ngay cả khi đây là các hằng số được sử dụng ở nơi khác; sau đó bạn phải viết tên của chúng cho rõ ràng!).
usr2564301

6

Sử dụng so sánh quan hệ trong những trường hợp như vậy là một thói quen phổ biến hơn bất cứ điều gì khác. Nó đã trở nên phổ biến trở lại trong thời đại khi những cân nhắc về khái niệm như các thể loại lặp và khả năng so sánh của chúng không được coi là ưu tiên cao.

Tôi muốn nói rằng người ta nên sử dụng so sánh bình đẳng thay vì so sánh quan hệ bất cứ khi nào có thể, vì so sánh bình đẳng áp đặt ít yêu cầu hơn đối với các giá trị được so sánh. Trở thành EqualityComparable là một yêu cầu ít hơn so với LessThanComparable .

Một ví dụ khác cho thấy khả năng ứng dụng rộng rãi của so sánh bình đẳng trong các bối cảnh như vậy là câu hỏi hóc búa phổ biến với việc thực hiện unsignedlặp đi lặp lại 0. Nó có thể được thực hiện như là

for (unsigned i = 42; i != -1; --i)
  ...

Lưu ý rằng ở trên có thể áp dụng như nhau cho cả phép lặp có chữ ký và không dấu, trong khi phiên bản quan hệ bị phá vỡ với các loại không dấu.


2

Bên cạnh các ví dụ, trong đó biến vòng lặp sẽ (vô tình) thay đổi bên trong cơ thể, có những cách khác để sử dụng các toán tử nhỏ hơn hoặc lớn hơn:

  • Các tiêu cực làm cho mã khó hiểu hơn
  • <hoặc >chỉ có một char, nhưng !=hai

4
Viên đạn thứ hai tốt nhất là một lý do phù phiếm. Mã phải chính xác và rõ ràng. Nhỏ gọn là ít quan trọng.
Jonathan Leffler

@JonathanLeffler Chà, trong TDD REPL, ký tự phụ đó là thêm một nano giây để phân tích cú pháp, và nhân lên hàng tỷ lần lặp lại để tìm ra lỗi do viết 5 != $i, chỉ mất một giây trong cuộc đời tôi. Uh ... cười thầm
giám mục

1

Ngoài những người khác nhau đã đề cập rằng nó giảm thiểu rủi ro, nó cũng làm giảm số lượng quá tải chức năng cần thiết để tương tác với các thành phần thư viện tiêu chuẩn khác nhau. Ví dụ, nếu bạn muốn loại của mình được lưu trữ trong một std::sethoặc được sử dụng làm khóa cho std::maphoặc sử dụng với một số thuật toán tìm kiếm và sắp xếp, thư viện chuẩn thường sử dụng std::lessđể so sánh các đối tượng vì hầu hết các thuật toán chỉ cần một thứ tự yếu nghiêm ngặt . Do đó, nó trở thành một thói quen tốt để sử dụng các <so sánh thay vì !=so sánh (tất nhiên là có ý nghĩa).


1
bạn có thể đúng về số lượng quá tải, nhưng khi xem xét các yêu cầu trên các đối tượng, thì đối với hầu hết mọi đối tượng, người ta có thể định nghĩa một !=toán tử, nhưng không phải lúc nào cũng có thể xác định một thứ tự yếu nghiêm ngặt. Theo nghĩa này !=sẽ tốt hơn, bởi vì nó đặt ra ít yêu cầu hơn về loại. Tuy nhiên, tôi vẫn ở lại với <.
idclev 463035818 16/07/2015

0

Không có vấn đề từ góc độ cú pháp, nhưng logic đằng sau biểu thức đó 5!=ikhông phải là âm thanh.

Theo tôi, việc sử dụng !=để đặt giới hạn của vòng lặp for không phải là âm thanh hợp lý bởi vì vòng lặp for tăng hoặc giảm chỉ số lặp, do đó, đặt vòng lặp thành lặp cho đến khi chỉ số lặp đi ra khỏi giới hạn ( !=đối với một cái gì đó) không phải là một thực hiện đúng.

Nó sẽ làm việc, nhưng nó là dễ bị vi sai trái từ việc xử lý dữ liệu ranh giới bị mất khi sử dụng !=cho một vấn đề gia tăng (có nghĩa là bạn biết ngay từ đầu nếu gia số nó hay decrements), đó là lý do tại sao thay vì !=những <>>==>được sử dụng.


2
Logic là hoàn toàn tốt. Khi i5thoát vòng lặp. Bạn có ý định nói gì khác không?
Dan Getz

1
Nếu dữ liệu không được chuyển chính xác do nhiệt, điện từ sẽ ảnh hưởng đến mã của bạn chạy mãi mãi. Nếu bạn thực hiện việc này trên máy trạm hoặc máy chủ thông thường có RAM ECC thì không có vấn đề gì (cả logic, kỹ thuật cũng như vật lý)
Markus
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.