Đã nhận xét mã ưa thích ngắn so với mã không dễ hiểu dài hơn - được ưa thích hơn?


18

Đôi khi một thuật toán có thể được viết theo hai cách:

  • Cách ngắn gọn, lạ mắt; hoặc là
  • Cách dài hơn, dễ hiểu.

Ví dụ, đây là một cách dễ dàng hơn còn sao chép một chuỗi sourceđể desttrong C:

*dest = *source;
while (*source != '\0') {
    source++;
    dest++;
    *dest = *source;
} (true);

Và đây là một cách ngắn gọn, lạ mắt.

// Copy string source to dest
while (*dest++ = *source++);

Tôi đã luôn luôn nghe và đọc rằng mã ưa thích nên tránh và tôi có xu hướng đồng ý. Nhưng nếu chúng ta đưa ý kiến ​​vào tài khoản thì sao? Giả sử rằng, như trong các ví dụ trên, chúng ta có một mã không bị lỗi, dài hơn và được cho là dễ hiểu hơn, và một mã được nhận xét tốt, ngắn, lạ mắt? Là mã không ưa thích vẫn còn được ưa thích?

EDIT: Nhiều người đã nhận xét về các tên biến, vì vậy tôi đã sửa đổi mã ví dụ để không biến nó thành một yếu tố khi thích hơn các tên khác. Tôi đã cố gắng loại bỏ phép gán kép trong ví dụ đầu tiên, nhưng điều đó chỉ làm cho mã ít đọc hơn.

Có lẽ đây không phải là ví dụ tốt nhất vì nhiều người tìm thấy mã 'ưa thích' dễ đọc và dễ hiểu hơn mã dài hơn. Ý tưởng là có một mã dài hơn, dễ hiểu hơn nhiều so với mã rất ngắn nhưng phức tạp.

EDIT2: Đây là một bài kiểm tra mới tôi nhận được từ SO :

Nhận xét phiên bản ưa thích:

//direct formula for xoring all numbers from 1 to N
int Sum = (N & (N % 2 ? 0 : ~0) | ( ((N & 2)>>1) ^ (N & 1) ) );

Phiên bản dài không bình luận:

int Sum = 0;
for (int i = 1; i < N; ++i)
{
   Sum ^= i; //or Sum = Sum ^ i;
}

2
@gablin: "goodly" là một từ, nhưng nó là một tính từ có nghĩa hơi khác với "tốt" và không được sử dụng rộng rãi nữa (ít nhất là ở Mỹ) Dạng trạng từ "tốt" là "tốt" như trong "mã nhận xét tốt."
John M Gant

@ John: À vâng, tất nhiên rồi. Tôi đã cập nhật câu hỏi tương ứng. Cảm ơn.
gablin

2
Con đường ngắn 'lạ mắt' theo cách nào? Đó là một đoạn văn bản của mã C mà bất kỳ ai tuyên bố biết C đều có thể đọc mà không gặp vấn đề gì. Sự căng thẳng không có nghĩa là 'ưa thích'.
JBRWilkinson

@JBRWilkinson: Như tôi đã nói trong phần sửa đổi của câu hỏi, ví dụ này không phải là một câu hỏi hay vì phiên bản "ưa thích" rõ ràng không quá hấp dẫn. Mục đích là để có một cách ngắn gọn nhưng không dễ hiểu với bình luận và sau đó là một cách dài hơn nhưng dễ đọc hơn nhiều mà không có bình luận.
gablin

@gablin: Chỉnh sửa của bạn thỏa hiệp các câu trả lời hiện tại.
Maniero

Câu trả lời:


28

Tôi thường muốn trích xuất mã ưa thích ra phương thức riêng của mình ..

Thay vì bình luận mã ưa thích, tên phương thức của nó phải là tất cả những gì nó cần để làm cho mọi thứ rõ ràng.

char *copy_string(char *s, const char *t) {    
    while (*s++ = *t++); 
    return s;
}

3
Điểm tuyệt vời. Bao thanh toán ra mã độc lập tốt hơn nhiều so với ý kiến. Trình biên dịch biết làm thế nào để nội tuyến nó nếu cần.
dbkk

À đúng rồi, đó là một cách tuyệt vời để làm cho bình luận trở nên lỗi thời. Nhưng mặc dù vậy, bạn sẽ luôn thích mã ưa thích hơn mã dài hơn?
gablin

Nói chung, có. Tôi cảm thấy rằng các phương thức ngắn, được đặt tên tốt làm cho mã dễ đọc và bảo trì hơn. Sự phức tạp được đưa vào các phương thức riêng của nó làm cho việc tái sử dụng trở nên dễ tiếp cận và rõ ràng hơn. Luôn có ngoại lệ tho, cuối cùng nó luôn phụ thuộc vào mã.
Mongus Pong

Chắc chắn chức năng của bạn nên được đặt tên là 'strcpy'?
JBRWilkinson

Có một lỗi trong đoạn mã đó. Những gì được trả lại? Những gì đã được trả lại? Chỉ cần biến nó thành một phương thức void và được thực hiện với nó;)
Christian Mann

10

Tôi là tất cả cho phiên bản dài hơn. Vấn đề với phiên bản mã ngắn, ngoài việc khó đọc hơn đối với một số lập trình viên, đó là hầu hết thời gian khó phát hiện ra lỗi khi chỉ nhìn vào nó.

Tôi có một ví dụ thực tế cuộc sống. Trong sản phẩm của chúng tôi, chúng tôi đã có đoạn mã sau:

if (++someCounter < MAX_VALUE) {
    // Do something that has to be done only MAX_VALUE times
}

Mã này trông hoàn toàn hợp lý ngay từ cái nhìn đầu tiên, nhưng sau một thời gian dài, bộ đếm này tràn ra và phá vỡ điều kiện (trong kịch bản thực tế, chúng tôi đã nghiền nát hệ thống sản xuất của khách hàng bằng OOM). Mã này hơi "tinh vi" hơn một chút, nhưng rõ ràng là nó thực hiện những gì nó phải làm:

if (someCounter < MAX_VALUE) {
    ++someCounter;
    // Do whatever it is we came here for
}

Và bạn có thể nói rằng nhà phát triển đã viết mã này không đủ tốt (điều đó không đúng, anh ta là một người khá sáng dạ), nhưng tôi nghĩ rằng nếu kỹ thuật mã hóa của bạn yêu cầu bạn phải lập trình siêu kỹ năng siêu phàm GOD trong để có được mã của bạn đúng, bạn đang làm gì đó sai. Mã của bạn phải được chứng minh một cách ngu ngốc nhất có thể vì lợi ích của bạn và cho bất cứ ai phải duy trì mã này sau bạn (người có thể không thông minh như bạn).

Vì vậy - tôi là tất cả vì đơn giản và rõ ràng.

Chỉnh sửa: Ồ, và liên quan đến ý kiến ​​- không thành vấn đề. Nhiều người sẽ không đọc bình luận nào ( http://www.javaspecialists.co.za/archive/Issue039.html ) và những người sẽ không hiểu mã của bạn mà không có nhận xét, sẽ không hiểu rõ về họ để họ hiểu có thể duy trì nó Mục tiêu là giúp mọi người thấy rằng một số mã nhất định là "chính xác", các bình luận không thể giúp với điều đó.


1
"đã nghiền nát hệ thống sản xuất của khách hàng của chúng tôi" - điều đó khá tệ: -S

"Vấn đề với phiên bản mã ngắn, ngoài việc một số lập trình viên khó đọc hơn" - bất kỳ đoạn mã nào cũng có thể khó "đối với một số lập trình viên". Bạn chỉ cần tìm lập trình viên đủ ngu ngốc. Đừng câm mã xuống mẫu số thấp nhất.
quant_dev

@quant_dev: ngược lại, "Gỡ lỗi khó gấp đôi so với viết mã ở vị trí đầu tiên. Do đó, nếu bạn viết mã càng thông minh càng tốt, theo định nghĩa, bạn không đủ thông minh để gỡ lỗi." - Brian W. Kernighan
Michael Borgwardt

@MichaelBorgwardt Tôi sẽ không áp dụng mù quáng của Kernighan cho mọi tình huống có thể. Ví dụ về OP là một hàm được viết "càng thông minh càng tốt" (hoặc gần với nó) và tuy nhiên nó sẽ khá dễ dàng để gỡ lỗi, nếu cần gỡ lỗi. Mặt khác, các onelin xoắn đôi phức tạp có thể rất thông minh, nhưng chắc chắn sẽ khó gỡ lỗi hơn nữa. (Ngoài ra: Kernighan giả định rằng các kỹ năng mã hóa = kỹ năng sửa lỗi. Nó không phải là trường hợp. Tôi đã gỡ lỗi thành công mã mà tôi không thể viết được.)
quant_dev

6

Tôi thường thích phiên bản dài hơn. Hai lý do chính khiến mùa xuân đến với tâm trí:

  • Nhiều người có thể hiểu nó với ít nỗ lực hơn (giả sử nó không phải là một ví dụ "tiêu chuẩn" như bản sao chuỗi này).
  • Có thể đặt các điểm dừng trên các câu lệnh riêng lẻ và thực hiện với trình gỡ lỗi.

Đối với những điều tốt nhất của cả hai thế giới, hãy bọc mã trong một hàm, tên mà bình luận cho bạn:

void copy_string(char *s, char *t)
{
    *s = *t;
    while (*t != '\0') {
        t++;
        s++;
        *s = *t;
    }
}

Lý do duy nhất để không làm điều này là nếu hiệu suất là một vấn đề và hồ sơ thực sự cho thấy chức năng trên là đáng kể.


1
Đó không phải là những gì nội tuyến là cho?
Antsan

Thật vậy, mặc dù nội tuyến đi kèm với một số vấn đề riêng của nó, như phải phơi bày phần thân hàm trong các tệp tiêu đề. Đừng tự động biên dịch nội dung công cụ nội tuyến cho bạn ở đâu hợp lý?
Paul Stephenson

4

Bất cứ điều gì là rõ ràng nhất. Thường thì đó là một đoạn mã nhỏ gọn dễ hiểu mà không cần bình luận ... như ví dụ thứ hai của bạn.

Nói chung các đoạn mã ngắn hơn sẽ dễ hiểu hơn vì người đọc sẽ giữ trong đầu ít hơn một lúc. Rõ ràng, có một giới hạn khi mã bị xáo trộn quá mức và tôi không có nghĩa là không gian trắng tăng cường rõ ràng nên được cắt bớt.

Nhận xét không bao giờ nên nêu bất cứ điều gì rõ ràng từ mã, điều đó chỉ khiến người đọc đọc cùng một điều hai lần. Đối với tôi đoạn trích đầu tiên yêu cầu làm rõ. Tại sao nhà phát triển không sử dụng do...whilevòng lặp để loại bỏ trùng lặp của *s = *t;bài tập? Tôi phải phân tích thêm mã để nhận ra rằng nó đang thực hiện sao chép chuỗi. Một nhận xét sẽ hữu ích hơn về mã dài hơn so với mã ngắn hơn.

Nhận xét về đoạn mã thứ hai gần như là dư thừa vì vòng lặp trong khi thực tế là thành ngữ, nhưng nó nói mã đó làm gì ở cấp độ cao hơn mà chính mã đó làm cho nó trở thành một nhận xét hữu ích.


Nhà phát triển (tôi) đã không sử dụng do ... whileđơn giản vì tôi đã không nghĩ về nó khi tôi viết câu hỏi. Cảm ơn đã chỉ ra rằng. ^^ Đoạn mã thứ hai có thể rõ ràng với bạn, nhưng chắc chắn nó không dành cho tôi.
gablin

4

Vấn đề là không có định nghĩa rõ ràng short fancy codevì nó phụ thuộc rất nhiều vào trình độ của lập trình viên. Trong khi một số người không có vấn đề hiểu một while(*s++ = *t++);biểu thức, những người khác sẽ.

Cá nhân tôi thấy while(*s++ = *t++);hoàn toàn dễ đọc và càng dài càng khó đọc. Những người khác có thể không đồng ý mặc dù.

Bất kể, đó chỉ là vấn đề sử dụng thông thường. Khả năng đọc chắc chắn phải là một ưu tiên nhưng có một điểm mà mã dài hơn sẽ dễ đọc hơn khi nó dài hơn. Độ dài thường làm giảm khả năng đọc từ kinh nghiệm của tôi.


Điều này làm tôi buồn khi một chủ đề của nhiều câu trả lời này là "các nhà phát triển có thể không nhận ra cách viết tiêu chuẩn của strcpy () trong C"
AShelly

Không nên - điều đó không có nghĩa là không còn lập trình viên giỏi nữa, thay vào đó a) chúng ta không dạy C nhiều nữa và b) rằng điểm mạnh của C cũng là điểm yếu của nó - trong khi đó vòng lặp đó là Có thể hiểu được với một người đã thực hành ở C, nó cũng khá phức tạp và trong một thế giới nỗ lực viết mã an toàn khá đáng sợ cho những gì nó ngụ ý về những gì bạn có thể làm trong C
Murph

Vì vậy, chúng tôi thấy mình có một thách thức khác đối với các nhiệm vụ hàng ngày của chúng tôi. Cần phải đoán mức độ hiểu ngôn ngữ chung của các đồng nghiệp trực tiếp của chúng tôi để các đánh giá mã của chúng tôi sẽ vẫn mang tính xây dựng. Không rơi vào các trận chiến trong đó là "chính xác", thể hiện ý định rõ ràng hơn.
ếchstarr78

2

Tôi phải không đồng ý với hầu hết các câu trả lời khác ở đây - tôi nghĩ (ít nhất là trong trường hợp này) mã ngắn hơn là tốt hơn. Trái với yêu cầu của bạn, mã dài hơn không "dễ dàng" hơn, ít nhất là đối với người đọc. Nếu bất cứ điều gì, bạn dường như đã hết ý muốn làm cho nó dài hơn, mặc dù nó làm cho mã khó hiểu hơn và / hoặc được đảm bảo hoạt động chính xác.

Đặc biệt, có sự phân công của byte đầu tiên của chuỗi bên ngoài vòng lặp, tách rời khỏi sự phân công cho các byte khác, phương tiện người ta phải nhiều hơn cẩn thận trong việc đọc để chắc chắn rằng tất cả các byte được sao chép một cách chính xác. Chuỗi hành động cơ bản trong phiên bản ngắn dễ xác minh hơn nhiều. Vấn đề thực sự duy nhất của nó là ở định dạng - khi bạn có một thân vòng lặp trống có chủ ý, tốt nhất là làm rõ điều đó, đại loại như:

while (*dest++ = *source++)
    ;

hoặc thậm chí:

while (*dest++ = *source++)
    ; /* no body */

Một số người thích sử dụng niềng răng thay thế:

while (*dest++ = *source++)
    {}

Bất kể định dạng chính xác mà bạn thích, có đủ lỗi đã xảy ra với những thứ như:

if (x>y);
     x = z;

... rằng điều quan trọng là phải chắc chắn rằng 1) rõ ràng những gì thực sự được kiểm soát bởi bất kỳ kiểm soát dòng chảy nào và 2) rõ ràng là mã được viết để biết những gì được kiểm soát bởi nó, vì vậy ai đó đọc nó không lãng phí thời gian để thử để tìm hiểu xem họ vừa tìm thấy một lỗi chưa.


Có, khi nhìn lại ví dụ cụ thể này, phiên bản 'ưa thích' có thể dễ đọc hơn phiên bản dài hơn. Nếu tôi có thể nghĩ ra một ví dụ tốt hơn tôi sẽ làm, nhưng tôi không thể làm được vào lúc này. Nhưng tôi đã không "tránh đường" để làm cho nó dài hơn - đó là cách tôi sẽ viết một bản sao chuỗi, ít nhất là ban đầu. Nó có thể không phải là cách tiếp cận tốt nhất, nhưng nó không được thực hiện lâu hơn cần thiết.
gablin

2
Keep it simple, stupid!

Hãy chắc chắn rằng bất kỳ lập trình viên nào khác cũng có thể hiểu được mã của bạn làm gì và thậm chí tốt hơn nếu nó trong nháy mắt (đây là nơi các tên và bình luận hay xuất hiện trong chính họ).

Kung-fu twiddling twiddling và lắp ráp nội tuyến là tất cả tuyệt vời trong tên của tối ưu hóa (có lẽ không cần thiết và sớm), nhưng ngay cả khi bạn không thể hiểu được mã bạn đã viết khi bạn gặp một lỗi trong hai tháng sau đó .. Điểm là gì? Bạn sẽ tốn thời gian và công sức.

Tôi thường đề cập đến Zen của Python trong những tình huống này. Đặc biệt:

Explicit is better than implicit.
Simple is better than complex.

1

Đừng viết mã ưa thích trong phần mềm sản xuất. Tôi biết cảm giác thật tốt khi có thể thực sự viết nó, và nó ngắn hơn. Viết mã ưa thích làm tăng đáng kể giá trị "WTF / phút", nói cách khác là giảm chất lượng.

văn bản thay thế


1

Tất nhiên nó hoàn toàn phụ thuộc vào hoàn cảnh. nhưng nói chung, tôi thấy đây là một quy tắc tốt:

Gỡ lỗi khó gấp đôi so với viết mã ở vị trí đầu tiên. Do đó, nếu bạn viết mã càng khéo léo càng tốt, theo định nghĩa, bạn không đủ thông minh để gỡ lỗi.

~ Brian Kernighan


dài hơn nhưng ý nghĩa hơn, thanh lịch hơn, so với từ viết tắt KISS nhỏ gọn hơn ~ hy vọng sự trớ trêu ở đó không bị mất; p
violet313

0

Theo kinh nghiệm của tôi, chiến thắng lớn nhất về mã ưa thích ngắn có xu hướng sử dụng đệ quy thay vì lặp lại. Đó cũng là một trong những điều khó hiểu nhất trong nháy mắt về mã, trừ khi bạn làm việc trong loại ngôn ngữ mà mọi thứ đều được đệ quy. Tôi vẫn sẽ ủng hộ nó vì sự thanh lịch và tốc độ phát triển mà nó mang lại, nhưng tôi cố gắng đảm bảo nó có những nhận xét chi tiết nếu có vẻ như nó sẽ mờ đục đối với những người duy trì trong tương lai hoặc bản thân tương lai của tôi.


0

Tôi bắt đầu không bao giờ tin tưởng bình luận. Tất cả các quá thường xuyên, ý kiến ​​không được cập nhật khi mã được cập nhật và bao la lỗi thời hoặc đại diện quy định từ khách hàng / quản lý mà không còn phù hợp.

Nó đã nhận được đến điểm ở một số trường hợp các ý kiến ​​thậm chí không phù hợp với mã mà họ đang mô tả nữa.

Nếu tôi phải lựa chọn giữa ngắn / fancy và lâu hơn / dễ hiểu hơn thì tôi luôn chọn sau này trừ khi có một thực sự lý do chính đáng.


0

Khả năng đọc và bảo trì là chìa khóa, và ví dụ thứ hai của bạn (tức là cái dài hơn) là nhiều hơn cả hai. Nhưng tại sao lại giới hạn bản thân trong hai lựa chọn? Ngay cả mã dài hơn cũng quá phức tạp (IMHO) để không thông báo thêm. Tôi sẽ đặt nó theo một phương thức riêng với Javadoc thích hợp (hoặc bất cứ thứ gì) và một tên phương thức phù hợp.

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.