Có nên tránh <= và> = khi sử dụng số nguyên, chẳng hạn như trong vòng lặp For không? [đóng cửa]


15

Tôi đã giải thích cho các sinh viên của mình rằng kiểm tra bằng không đáng tin cậy cho các biến float, nhưng tốt cho các số nguyên. Sách giáo khoa tôi đang sử dụng nói rằng nó dễ đọc hơn> và <than> = và <=. Tôi đồng ý ở một mức độ nào đó, nhưng trong một vòng lặp For? Không rõ ràng hơn khi vòng lặp chỉ định giá trị bắt đầu và kết thúc?

Tôi có thiếu một cái gì đó mà tác giả sách giáo khoa là chính xác?

Một ví dụ khác là trong các bài kiểm tra Phạm vi như:

nếu điểm> 89 lớp = 'A'
khác nếu điểm> 79 lớp = 'B' ...

Tại sao không chỉ nói: nếu điểm> = 90?

loops 

11
Thật không may, vì không có sự khác biệt khách quan trong hành vi giữa các tùy chọn này, điều này dẫn đến một cuộc thăm dò ý kiến ​​về những gì mọi người cho là trực quan hơn và các cuộc thăm dò không phù hợp với các trang web StackExchange như thế này.
Ixrec 18/03/2016

1
Nó không thành vấn đề. Có thật không.
Auberon

4
Trên thực tế, đây là câu trả lời khách quan. Chờ ...
Robert Harvey

9
@Ixrec Tôi luôn thấy thú vị rằng "thực tiễn tốt nhất" không được coi là một chủ đề phù hợp. Ai không muốn cải thiện hoặc làm cho mã của họ dễ đọc hơn? Nếu mọi người không đồng ý, tất cả chúng ta đều tìm hiểu thêm các khía cạnh của vấn đề, và có thể ... thậm chí ... thay đổi suy nghĩ của chúng ta! Ugh, điều đó thật khó nói. Robert Harvey nói rằng ông có thể trả lời nó một cách khách quan. Điều này sẽ rất thú vị.

1
@nocomprende Chủ yếu là vì "thực hành tốt nhất" là một thuật ngữ rất mơ hồ và bị lạm dụng mà có thể tham khảo những lời khuyên hữu ích dựa trên sự thật khách quan về ngôn ngữ, nhưng cũng giống như thường đề cập đến "hầu hết quan điểm phổ biến" (hoặc ý kiến của bất cứ ai đang sử dụng thuật ngữ) trong thực tế, tất cả các tùy chọn đều có giá trị như nhau và không hợp lệ. Trong trường hợp này, bạn chỉ có thể đưa ra một lập luận khách quan bằng cách hạn chế câu trả lời cho một số loại vòng lặp như Robert đã làm, và như bạn đã chỉ ra mình trong một bình luận không hoàn toàn trả lời câu hỏi.
Ixrec 18/03/2016

Câu trả lời:


36

Trong các ngôn ngữ lập trình được uốn cong với các mảng dựa trên zero , thông thường sẽ viết forcác vòng lặp như thế này:

for (int i = 0; i < array.Length, i++) { }

Điều này đi qua tất cả các phần tử trong mảng và là trường hợp phổ biến nhất. Nó tránh việc sử dụng <=hoặc >=.

Lần duy nhất điều này sẽ phải thay đổi là khi bạn cần bỏ qua yếu tố đầu tiên hoặc cuối cùng, hoặc di chuyển nó theo hướng ngược lại, hoặc đi qua nó từ một điểm bắt đầu khác hoặc đến một điểm kết thúc khác.

Đối với các bộ sưu tập, trong các ngôn ngữ hỗ trợ các trình vòng lặp, thông thường sẽ thấy điều này:

foreach (var item in list) { }

Mà tránh sự so sánh hoàn toàn.

Nếu bạn đang tìm kiếm một quy tắc cứng và nhanh khi nào nên sử dụng <=so với <, thì không có quy tắc nào cả ; sử dụng những gì thể hiện tốt nhất ý định của bạn. Nếu mã của bạn cần phải thể hiện khái niệm "Nhỏ hơn hoặc bằng 55 dặm một giờ", sau đó nó cần phải nói <=, không <.

Để trả lời câu hỏi của bạn về các phạm vi lớp, >= 90có ý nghĩa hơn, bởi vì 90 là giá trị biên thực tế, không phải 89.


9
Nó không tránh được sự so sánh hoàn toàn, nó chỉ quét chúng dưới tấm thảm bằng cách chuyển chúng vào thực hiện điều tra viên. : P
Mason Wheeler

2
Tất nhiên, nhưng điều đó không còn đi qua một mảng, phải không?
Robert Harvey

4
Bởi vì mảng là trường hợp sử dụng phổ biến nhất cho forcác vòng lặp như thế này. Hình thức forvòng lặp tôi cung cấp ở đây sẽ được nhận ra ngay lập tức đối với bất kỳ nhà phát triển nào có một chút kinh nghiệm. Nếu bạn muốn có một câu trả lời cụ thể hơn dựa trên một kịch bản cụ thể hơn, bạn cần đưa câu hỏi đó vào câu hỏi của mình.
Robert Harvey

3
"Sử dụng những gì thể hiện tốt nhất ý định của bạn" <- Điều này!
Jasper N. Brouwer

3
@ JasperN.Brouwer Nó giống như một luật lập trình zeroth. Tất cả các quy tắc phong cách và quy ước mã hóa sụp đổ trong sự xấu hổ sâu sắc nhất khi các nhiệm vụ của chúng mâu thuẫn với sự rõ ràng của mã.
Idillotexist Idonotexist 19/03/2016

16

Nó không thành vấn đề.

Nhưng vì lợi ích của các đối số, chúng ta hãy phân tích hai lựa chọn: a > bvs a >= b.

Treo lên! Những cái đó không tương đương!
OK, sau đó a >= b -1vs a > bhoặc a > bvs a >= b +1.

Hừm, a >ba >= bcả hai đều trông đẹp hơn a >= b - 1a >= b +1. Tất cả những cái này 1là gì? Vì vậy, tôi cho rằng bất kỳ lợi ích nào từ việc >thay thế >=hoặc ngược lại đều bị loại bỏ bằng cách thêm hoặc bớt các 1s ngẫu nhiên .

Nhưng nếu đó là một con số thì sao? Là tốt hơn để nói a > 7hay a >= 6? Chờ giây lát. Có phải chúng ta đang tranh luận một cách nghiêm túc rằng liệu có tốt hơn khi sử dụng >so với >=và bỏ qua các biến được mã hóa cứng không? Vì vậy, nó thực sự trở thành một câu hỏi liệu a > DAYS_OF_WEEKtốt hơn a >= DAYS_OF_WEEK_MINUS_ONE... hay là a > NUMBER_OF_LEGS_IN_INSECT_PLUS_ONEso với a >= NUMBER_OF_LEGS_IN_INSECT? Và chúng tôi quay lại việc thêm / bớt 1s, chỉ lần này là tên biến. Hoặc có thể tranh luận nếu tốt nhất là sử dụng ngưỡng, giới hạn, tối đa.

Và có vẻ như không có quy tắc chung: nó phụ thuộc vào những gì được so sánh

Nhưng thực sự, có nhiều điều quan trọng hơn để cải thiện mã của một người và các hướng dẫn hợp lý và khách quan hơn nhiều (ví dụ: giới hạn ký tự X trên mỗi dòng) vẫn có ngoại lệ.


1
OK, không có quy tắc chung. (Tự hỏi tại sao sách giáo khoa dường như nêu ra một quy tắc chung, nhưng tôi có thể để nó đi.) Không có sự đồng thuận mới nổi nào rằng đó là một bản ghi nhớ mà tôi đã bỏ lỡ.

2
@nocomprende vì các tiêu chuẩn mã hóa và định dạng đều hoàn toàn chủ quan và gây tranh cãi cao. Hầu hết thời gian không ai trong số họ quan trọng, nhưng các lập trình viên vẫn tiến hành các cuộc chiến thần thánh đối với họ. (chúng chỉ quan trọng khi mã cẩu thả có khả năng dẫn đến lỗi)

1
Tôi đã thấy rất nhiều cuộc thảo luận điên rồ về các tiêu chuẩn mã hóa nhưng điều này có thể chỉ là lấy bánh. Thực sự ngớ ngẩn.
JimmyJames 18/03/2016

@JimmyJames đây là về cuộc thảo luận về >vs >=hoặc cuộc thảo luận về việc cuộc thảo luận về >vs >=có ý nghĩa không? mặc dù có lẽ tốt nhất để tránh thảo luận về điều này: p
Thanos Tintinidis 18/03/2016

2
Các chương trình có nghĩa là được đọc bởi con người và chỉ tình cờ cho các máy tính thực thi chức năng - Donald Knuth. Sử dụng văn phong giúp dễ đọc, hiểu và duy trì nhất.
đơn giản

7

Tính toán không có sự khác biệt về chi phí khi sử dụng <hoặc >so với <=hoặc >=. Nó được tính toán nhanh như nhau.

Tuy nhiên, hầu hết các vòng lặp sẽ được tính từ 0 (vì nhiều ngôn ngữ sử dụng lập chỉ mục 0 cho mảng của chúng). Vì vậy, vòng lặp chính trong các ngôn ngữ đó là

for(int i = 0; i < length; i++){
   array[i] = //...
   //...
}

làm điều này với một <=yêu cầu bạn phải thêm -1 ở đâu đó để tránh lỗi do một lỗi

for(int i = 1; i <= length; i++){
   array[i-1] = //...
   //...
}

hoặc là

for(int i = 0; i <= length-1; i++){
   array[i] = //...
   //...
}

Tất nhiên, nếu ngôn ngữ sử dụng lập chỉ mục dựa trên 1 thì bạn sẽ sử dụng <= làm điều kiện giới hạn.

Điều quan trọng là các giá trị được thể hiện trong điều kiện là những giá trị từ mô tả vấn đề. Nó sạch hơn để đọc

if(x >= 10 && x < 20){

} else if(x >= 20 && x < 30){

}

trong một khoảng thời gian nửa mở

if(x >= 10 && x <= 19){

} else if(x >= 20 && x <= 29){

}

và phải làm toán để biết rằng không có giá trị khả dĩ nào giữa 19 và 20


Tôi thấy ý tưởng "độ dài", nhưng nếu bạn chỉ sử dụng các giá trị đến từ đâu đó và có nghĩa là lặp từ giá trị bắt đầu đến giá trị kết thúc. Một ví dụ về lớp là tỷ lệ phần trăm đánh dấu từ 5 đến 10. Không rõ ràng hơn khi nói: for (markup = 5; markup <= 10; markup ++) ... ? Tại sao tôi thay đổi để kiểm tra: đánh dấu <11 ?

6
@nocomprende bạn sẽ không, nếu các giá trị biên từ mô tả vấn đề của bạn là 5 và 10 thì tốt hơn là để chúng rõ ràng trong mã thay vì giá trị thay đổi một chút.
ratchet freak

@nocomprende Định dạng đúng sẽ rõ ràng hơn khi giới hạn trên là một tham số : for(markup = 5; markup <= MAX_MARKUP; ++markup). Bất cứ điều gì khác sẽ được quá mức nó.
Navin

1

Tôi muốn nói rằng vấn đề không phải là bạn nên sử dụng> hay> =. Vấn đề là sử dụng bất cứ điều gì cho phép bạn viết mã biểu cảm.

Nếu bạn thấy rằng bạn cần thêm / bớt một, hãy xem xét sử dụng toán tử khác. Tôi thấy rằng những điều tốt đẹp xảy ra khi bạn bắt đầu với một mô hình tốt về tên miền của bạn. Sau đó logic tự viết.

bool IsSpeeding(int kilometersPerHour)
{
    const int speedLimit = 90;
    return kilometersPerHour > speedLimit;
}

Đây là cách biểu cảm hơn

bool IsSpeeding(int kilometersPerHour)
{
    const int speedLimit = 90;
    return kilometersPerHour >= (speedLimit + 1);
}

Trong các trường hợp khác, cách khác là thích hợp hơn:

bool CanAfford(decimal price, decimal balance)
{
    return balance >= price;
}

Tốt hơn nhiều so với

bool CanAfford(decimal price, decimal balance)
{
    const decimal epsilon = 0e-10m;
    return balance > (price - epsilon);
}

Xin thứ lỗi cho "nỗi ám ảnh nguyên thủy". Rõ ràng là bạn muốn sử dụng loại Vận tốc và Tiền tương ứng ở đây, nhưng tôi đã bỏ qua chúng để cho ngắn gọn. Vấn đề là: Sử dụng phiên bản ngắn gọn hơn và cho phép bạn tập trung vào vấn đề kinh doanh mà bạn muốn giải quyết.


2
Ví dụ giới hạn tốc độ là một ví dụ kém. Đi 90kph trong vùng 90kph không được coi là tăng tốc. Một giới hạn tốc độ là bao gồm.
eidsonator 18/03/2016

bạn hoàn toàn chính xác, não rắm về phía tôi. vui lòng chỉnh sửa nó để sử dụng một ví dụ tốt hơn, hy vọng rằng điểm không bị mất hoàn toàn.
sara

0

Như bạn đã chỉ ra trong câu hỏi của mình, việc kiểm tra sự bằng nhau trên các biến float là không đáng tin cậy.

Điều tương tự cũng đúng cho <=>=.

Tuy nhiên, không có vấn đề độ tin cậy như vậy đối với các loại số nguyên. Theo ý kiến ​​của tôi, tác giả đã bày tỏ ý kiến của mình để dễ đọc hơn.

Cho dù bạn có đồng ý với anh ấy hay không thì tất nhiên là ý kiến của bạn .


Đây có phải là một quan điểm thường được tổ chức, bởi các tác giả khác và những người tóc bạc trong lĩnh vực này (thực sự tôi có mái tóc màu xám)? Nếu đó chỉ là "Ý kiến ​​của một phóng viên", tôi có thể nói với các sinh viên đó. Tôi có, thực sự. Chưa bao giờ nghe ý tưởng này trước đây.

Giới tính của tác giả là không liên quan, nhưng dù sao tôi cũng cập nhật câu trả lời của mình. Tôi thích chọn <hoặc <=dựa trên những gì tự nhiên nhất cho vấn đề cụ thể mà tôi đang giải quyết. Như những người khác đã chỉ ra, trong một vòng lặp FOR <có ý nghĩa hơn trong các ngôn ngữ loại C. Có trường hợp sử dụng khác mà ủng hộ <=. Sử dụng tất cả các công cụ theo ý của bạn, khi nào và khi thích hợp.
Dan Pichelman 18/03/2016

Không đồng ý. Có thể có các vấn đề về độ tin cậy với các loại số nguyên: trong C, hãy xem xét: for (unsigned int i = n; i >= 0; i--)hoặc for (unsigned int i = x; i <= y; i++)nếu yxảy ra UINT_MAX. Rất tiếc, những vòng lặp mãi mãi.
jamesdlin

-1

Mỗi phòng trong số các mối quan hệ <, <=, >=, >cũng ==!=có trường hợp sử dụng của họ để so sánh hai giá trị dấu chấm động. Mỗi cái có một ý nghĩa cụ thể và cái thích hợp nên được chọn.

Tôi sẽ đưa ra ví dụ cho các trường hợp bạn muốn chính xác toán tử này cho từng người trong số họ. (Mặc dù vậy, hãy chú ý đến NaNs.)

  • Bạn có một hàm thuần túy đắt tiền flấy giá trị dấu phẩy động làm đầu vào. Để tăng tốc độ tính toán của bạn, bạn quyết định thêm một bộ nhớ cache của các giá trị thời gian gần đây hầu hết các tính toán, có nghĩa là, một ánh xạ bảng tra cứu xđể f(x). Bạn sẽ thực sự muốn sử dụng ==để so sánh các đối số.
  • Bạn muốn biết liệu bạn có thể chia một cách có ý nghĩa cho một số x? Bạn có thể muốn sử dụng x != 0.0.
  • Bạn muốn biết liệu một giá trị xnằm trong khoảng đơn vị? (x >= 0.0) && (x < 1.0)là điều kiện chính xác.
  • Bạn đã tính toán xác định dcủa một ma trận và muốn cho biết liệu nó có xác định dương không? Không có lý do để sử dụng bất cứ điều gì khác nhưng d > 0.0.
  • Bạn muốn biết liệu Σ n = 1, ..., ∞ n phân kì? Tôi muốn kiểm tra alpha <= 1.0.

Toán học dấu phẩy động (nói chung) là không chính xác. Nhưng điều đó không có nghĩa là bạn nên sợ nó, coi nó như ma thuật và chắc chắn không phải lúc nào cũng coi hai đại lượng dấu phẩy bằng nhau nếu chúng ở trong 1.0E-10. Làm như vậy sẽ thực sự phá vỡ toán học của bạn và gây ra tất cả những điều kỳ lạ xảy ra.

  • Ví dụ: nếu bạn sử dụng so sánh mờ với bộ đệm được đề cập ở trên, bạn sẽ đưa ra các lỗi vui nhộn. Nhưng thậm chí tệ hơn, hàm sẽ không còn thuần túy và lỗi phụ thuộc vào kết quả tính toán trước đó!
  • Có, ngay cả khi x != 0.0ylà một giá trị dấu phẩy động hữu hạn, y / xkhông cần phải là hữu hạn. Nhưng nó có thể có liên quan để biết liệu y / xkhông hữu hạn vì tràn hay do hoạt động không được xác định rõ về mặt toán học để bắt đầu.
  • Nếu bạn có một hàm có điều kiện tiên quyết là tham số đầu vào xphải nằm trong khoảng đơn vị [0, 1), tôi sẽ thực sự khó chịu nếu nó bắn lỗi xác nhận khi được gọi bằng x == 0.0hoặc x == 1.0 - 1.0E-14.
  • Yếu tố quyết định bạn đã tính toán có thể không chính xác. Nhưng nếu bạn sẽ giả vờ rằng ma trận không xác định dương khi xác định được tính toán 1.0E-30thì không có gì đạt được. Tất cả các bạn đã làm là tăng khả năng đưa ra các câu trả lời sai.
  • Có, đối số của bạn alphacó thể bị ảnh hưởng bởi các lỗi làm tròn và do đó alpha <= 1.0có thể đúng mặc dù giá trị toán học thực sự cho biểu thức alphađược tính từ có thể thực sự lớn hơn 1. Nhưng vào thời điểm này, bạn không thể làm gì được.

Như mọi khi trong công nghệ phần mềm, xử lý lỗi ở mức thích hợp và chỉ xử lý chúng một lần. Nếu bạn thêm các lỗi làm tròn theo thứ tự 1.0E-10(đây dường như là giá trị kỳ diệu mà hầu hết mọi người sử dụng, tôi không biết tại sao) mỗi khi bạn so sánh số lượng dấu phẩy động, bạn sẽ sớm gặp lỗi theo thứ tự 1.0E+10Giáo dục


1
Một câu trả lời xuất sắc nhất, mặc dù phải thừa nhận, chỉ dành cho một câu hỏi khác với câu hỏi.
Dewi Morgan

-2

Loại điều kiện được sử dụng trong một vòng lặp có thể giới hạn các loại tối ưu hóa mà trình biên dịch có thể thực hiện, tốt hơn hoặc xấu hơn. Ví dụ: đã cho:

uint16_t n = ...;
for (uint16_t i=1; i<=n; i++)
  ...  [loop doesn't modify i]

một trình biên dịch có thể giả định rằng điều kiện trên sẽ khiến vòng lặp thoát ra sau vòng lặp thứ n trừ khi n có thể 65535 và vòng lặp có thể thoát theo một số cách khác ngoài i vượt quá n. Nếu những điều kiện đó được áp dụng, trình biên dịch phải tạo mã sẽ khiến vòng lặp chạy cho đến khi một cái gì đó không phải là điều kiện trên khiến nó thoát ra.

Nếu vòng lặp thay vào đó được viết là:

uint16_t n = ...;
for (uint16_t ctr=0; ctr<n; ctr++)
{
  uint16_t i = ctr+1;
  ... [loop doesn't modify ctr]
}

sau đó một trình biên dịch có thể giả định một cách an toàn rằng vòng lặp sẽ không bao giờ cần phải thực thi nhiều hơn n lần và do đó có thể tạo mã hiệu quả hơn.

Lưu ý rằng bất kỳ tràn với các loại đã ký có thể có hậu quả khó chịu. Được:

int total=0;
int start,lim,mult; // Initialize values somehow...
for (int i=start; i<=lim; i++)
  total+=i*mult;

Một trình biên dịch có thể viết lại như sau:

int total=0;
int start,lim,mult; // Initialize values somehow...
int loop_top = lim*mult;
for (int i=start; i<=loop_top; i+=mult)
  total+=i;

Một vòng lặp như vậy sẽ hoạt động giống hệt với bản gốc nếu không xảy ra tràn trong tính toán, nhưng có thể chạy mãi mãi ngay cả trên các nền tảng phần cứng nơi tràn số nguyên thường có ngữ nghĩa gói nhất quán.


Vâng, tôi nghĩ rằng đó là một chút xa hơn trong sách giáo khoa. Chưa xem xét. Tối ưu hóa trình biên dịch rất hữu ích để hiểu và phải tránh tràn, nhưng nó không thực sự là động lực cho câu hỏi của tôi, mà nhiều hơn là về cách mọi người hiểu mã. Đọc x> 5 hay x> = 6 có dễ hơn không? Chà, nó phụ thuộc ...

Trừ khi bạn viết cho Arduino, giá trị tối đa cho int sẽ cao hơn một chút so với 65535.
Corey

1
@Corey: Giá trị tối đa cho uint16_t sẽ là 65535 trên bất kỳ nền tảng nào có loại tồn tại; Tôi đã sử dụng uint16_t trong ví dụ đầu tiên vì tôi mong muốn nhiều người biết giá trị tối đa chính xác của một uint16_t hơn so với uint32_t. Điểm giống nhau sẽ được áp dụng trong cả hai trường hợp. Ví dụ thứ hai là thuyết bất khả tri liên quan đến loại "int" và đại diện cho một điểm hành vi quan trọng mà nhiều người không nhận ra về trình biên dịch hiện đại.
supercat
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.