Комментированный короткий причудливый код по сравнению с некомментированным длинным простым для понимания кодом - что является предпочтительным?


18

Иногда алгоритм может быть написан двумя способами:

  • Короткий, причудливый путь; или
  • Более длинный и понятный способ.

Например, здесь больше, проще способ копирования строки sourceв destв C:

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

И вот короткий, причудливый путь.

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

Я всегда слышал и читал, что нужно избегать причудливого кода, и я склонен согласиться. Но что, если мы примем во внимание комментарии? Предположим, что, как и в приведенных выше примерах, у нас есть некомментированный, более длинный и, предположительно, более легкий для понимания код и хорошо прокомментированный, короткий, причудливый код? Не причудливый код все еще предпочтителен?

РЕДАКТИРОВАТЬ: Многие прокомментировали имена переменных, поэтому я изменил пример кода, чтобы не делать этого фактора при предпочтении над другими. Я попытался удалить двойное назначение в первом примере, но это только сделало код менее читабельным.

Возможно, это был не лучший пример, потому что многие находят «причудливый» код более читабельным и понятным, чем более длинный код. Идея заключалась в том, чтобы иметь один более длинный код, который был бы намного легче понять, чем очень короткий, но сложный код.

EDIT2: вот новый пример, который я получил от SO :

Комментированная модная версия:

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

Длинная версия без комментариев:

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

2
@gablin: «хорошо» - это слово, но это прилагательное, имеющее несколько иное значение, чем «хорошо», и больше не используется (по крайней мере, в США). Наречие формы «хорошо» - «хорошо» как в «хорошо прокомментированном коде».
Джон М Гант

@Джон: Ах да, конечно. Я обновил вопрос соответственно. Благодарю.
Габлин

2
Как короткий путь «причудливый»? Это учебник кода C, который любой, кто утверждает, что знает C, может читать без проблем. Краткость не означает «фантазия».
Дж.Б. Уилкинсон

@JBRWilkinson: Как я уже говорил в модифицированной части вопроса, этот пример не очень хороший, поскольку «причудливая» версия явно не такая уж причудливая. Цель состояла в том, чтобы иметь один короткий, но непонятный способ с комментариями, а затем более длинный, но гораздо более читаемый способ без комментариев.
Габлин

@gablin: Ваше редактирование ставит под угрозу текущие ответы.
Маньеро

Ответы:


28

Как правило, я бы предпочел извлечь необычный код из его собственного метода.

Вместо того, чтобы комментировать причудливый код, его имя метода должно быть всем, что нужно, чтобы прояснить ситуацию.

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

3
Отличный момент. Выделение автономного кода намного лучше, чем комментарии. Компилятор знает, как встроить его в случае необходимости.
2010 года

Ах да, это отличный способ сделать комментарий устаревшим. Но несмотря на это, вы бы всегда предпочли причудливый код длинному коду?
Габлин

В общем да. Я чувствую, что короткие, хорошо названные методы облегчают чтение и поддержку кода. Сложность, учитываемая в его собственных методах, делает повторное использование более доступным и очевидным. Всегда есть исключения, но в конечном итоге это всегда зависит от кода.
Mongus Pong

Конечно, ваша функция должна называться 'strcpy'?
JBRWilkinson

В этом куске ошибки есть ошибка. Что возвращается? Что должно было быть возвращено? Просто сделайте это пустым методом и покончите с этим;)
Кристиан Манн

10

Я все за более длинную версию. Проблема с короткой версией кода, кроме того, что некоторым программистам труднее ее читать, состоит в том, что большую часть времени труднее обнаружить ошибки, просто взглянув на нее.

У меня есть пример из реальной жизни. В нашем продукте был следующий фрагмент кода:

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

На первый взгляд этот код выглядит вполне разумным, но после длительного периода времени этот счетчик переполняется и нарушает условие (в реальном сценарии мы разрушили производственную систему нашего клиента с помощью OOM). Этот код немного менее "сложен", но ясно, что он делает то, что должен:

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

И вы можете сказать, что разработчик, который написал этот код, просто не был достаточно хорош (что не соответствует действительности, он был довольно умным парнем), но я думаю, что если ваша техника кодирования требует, чтобы вы были опытным программистом GOD в Чтобы ваш код был правильным, вы делаете что-то не так. Ваш код должен быть как можно более надежным для вас и для тех, кто должен поддерживать этот код после вас (кто может быть не таким умным, как вы).

Так что - я за простоту и понятность.

Редактировать: Да, а что касается комментариев - это не имеет значения. Многие люди не будут читать комментарии в любом случае ( http://www.javaspecialists.co.za/archive/Issue039.html ), а те, кто не поймет ваш код без комментариев, не поймут его достаточно хорошо с ними, чтобы они может поддерживать это. Цель состоит в том, чтобы помочь людям увидеть, что определенный код является «правильным», комментарии не могут помочь с этим.


1
«раздавил производственную систему нашего клиента» - это довольно плохо: -S

«Проблема с короткой версией кода, кроме того, что некоторым программистам труднее ее читать» - любой фрагмент кода может быть трудным «для некоторых программистов». Вам просто нужно найти достаточно глупых программистов. Не вводите код до наименьшего знаменателя.
quant_dev

@quant_dev: наоборот: «Отладка в два раза сложнее, чем писать код в первую очередь. Поэтому, если вы пишете код настолько умно, насколько это возможно, вы, по определению, недостаточно умны, чтобы отлаживать его». - Брайан В. Керниган
Майкл Боргвардт

@MichaelBorgwardt Я бы не применял изречение Кернигана вслепую к любой возможной ситуации. Пример OPs - это функция, написанная «настолько умно, насколько это возможно» (или близко к ней), и, тем не менее, отладка должна быть довольно простой, если требуется какая-либо отладка. С другой стороны, сложные однонаправленные биты могут быть очень умными, но, конечно, их будет сложнее отлаживать. (Также: Керниган предполагает, что навыки кодирования = навыки отладки. Это не обязательно должно быть так. Я успешно отладил код, который не смог бы написать.)
quant_dev

6

Я бы вообще предпочел более длинную версию. На ум приходят две основные причины:

  • Больше людей, вероятно, поймут это с меньшими усилиями (при условии, что это не такой «стандартный» пример, как эта строковая копия).
  • Можно ставить точки останова на отдельные операторы и переходить к отладчику.

Для лучшего из обоих миров, оберните код в функцию, чье имя делает комментарии для вас:

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

Единственная причина этого не делать, если производительность была проблемой, а профилирование действительно показало значительную нагрузку на функцию.


1
Разве это не то, для чего inline?
Antsan

Действительно, хотя встраивание имеет свою долю проблем, таких как необходимость выставлять тело функции в заголовочных файлах. Разве современные компиляторы не делают для вас что-нибудь автоматически, если это разумно?
Пол Стивенсон

4

Что бы ни было яснее. Часто это компактный, легкий для понимания фрагмент кода, который не требует комментариев ... как ваш второй пример.

Как правило, более короткие фрагменты кода легче понять, так как читателю все время остается в голове. Очевидно, есть предел, когда код становится чрезмерно запутанным, и я не имею в виду, что пробел, улучшающий ясность, должен быть обрезан.

Комментарии никогда не должны содержать ничего очевидного из кода, который просто заставляет читателя читать одно и то же дважды. Для меня первый фрагмент требует уточнения. Почему разработчик не использовал do...whileцикл для удаления дублирующегося *s = *t;назначения? Мне нужно разобрать больше кода, чтобы понять, что он реализует копию строки. Комментарий был бы более полезен для этого более длинного кода, чем для более короткого кода.

Комментарий ко второму фрагменту почти избыточен, поскольку цикл while практически идиоматичен, но он говорит о том, что код делает на более высоком уровне, чем сам код, что делает его полезным комментарием.


Разработчик (я) не использовал do ... whileпросто потому, что я не думал об этом, когда писал вопрос. Спасибо что подметил это. ^^ Второй фрагмент может быть очевиден для вас, но это, конечно, не для меня.
Габлин

4

Проблема в том, что нет четкого определения, short fancy codeпоскольку оно очень сильно зависит от уровня программиста. В то время как у некоторых людей нет проблем с пониманием while(*s++ = *t++);выражения, другие будут.

Лично я нахожу while(*s++ = *t++);отлично читаемым и длиннее, труднее для чтения. Другие могут не согласиться, хотя.

Независимо от этого, это просто вопрос использования здравого смысла. Читаемость должна определенно быть приоритетом, но есть момент, когда длинный код становится менее читаемым, когда становится длиннее. Многословие часто снижает читабельность из моего опыта.


Мне грустно, что тема многих из этих ответов такова: «разработчики могут не распознавать стандартный способ написания strcpy () на C»
AShelly

Это не должно - это не значит, что больше нет хороших программистов, скорее, что а) мы больше не учим С и б) что сила С также является его слабостью - в то время как этот цикл Понятно для того, кто практикуется в C, это также довольно загадочно и в мире, пытающемся написать безопасный код, довольно страшный из-за того, что он подразумевает в том, что вы можете делать в C
Murph

Таким образом, мы сталкиваемся с еще одним вызовом для наших повседневных задач. Нужно угадать общий уровень понимания языка нашими прямыми коллегами, чтобы наши обзоры кода оставались конструктивными. Не попасть в битвы, из которых "правильное", более чистое выражение намерений.
frogstarr78

2

Я должен не согласиться с большинством других ответов здесь - я думаю (по крайней мере, в этом случае), чем короче код, тем лучше. Вопреки вашему требованию, более длинный код не «проще», по крайней мере, для читателя. Во всяком случае, вы, кажется, перестали стремиться сделать его длиннее, хотя это затрудняет понимание кода и / или гарантирует правильную работу.

В частности, наличие назначения первого байта строки вне цикла, отдельного от назначения для других байтов, означает, что нужно быть намного более осторожным при чтении, чтобы быть уверенным, что все байты скопированы правильно. Основная последовательность действий в более короткой версии намного легче проверить. Его единственная реальная проблема заключается в форматировании - когда у вас намеренно пустое тело цикла, лучше прояснить это, что-то вроде:

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

или даже:

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

Некоторые люди предпочитают использовать вместо этого фигурные скобки:

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

Независимо от того, какое форматирование вы предпочитаете, достаточно ошибок было сделано с такими вещами, как:

if (x>y);
     x = z;

... важно быть уверенным, что 1) очевидно, что на самом деле контролируется каким-либо контролем потока, и 2) очевидно, что код был написан, зная, что им контролируется, поэтому кто-то, читая его, не тратит время на попытки чтобы выяснить, нашли ли они только что ошибку.


Да, в ретроспективе этого конкретного примера «причудливая» версия, вероятно, более удобочитаема, чем более длинная версия. Если бы я мог придумать лучший пример, я бы это сделал, но сейчас я не могу. Но я не «старался изо всех сил» сделать его длиннее - так я бы написал строковую копию, по крайней мере, на начальном этапе. Возможно, это не лучший подход, но он не был сделан намеренно дольше, чем это необходимо.
Габлин

2
Keep it simple, stupid!

Убедитесь, что любой другой программист может понять, что делает ваш код - и даже лучше, если он выглядит с первого взгляда (здесь хорошие имена и комментарии вступают в свои права).

Фантастическое кунг-фу и встроенная сборка - все это прекрасно во имя (возможно, ненужного и преждевременного) оптимизации, но даже когда вы не можете понять код, который вы написали, когда вы столкнулись с ошибкой в ​​нем два месяца спустя ... . Какой был смысл? Вы будете стоить себе времени и усилий.

Я часто обращаюсь к дзен Python в таких ситуациях. Особенно:

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

1

Не пишите причудливый код в производственном программном обеспечении. Я знаю, что это здорово, когда я могу написать это, и это короче. Написание причудливого кода значительно увеличивает значение «WTF / минута», другими словами, снижает качество.

альтернативный текст


1

Это, конечно, полностью зависит от обстоятельств. но в целом я считаю, что это хорошее правило:

Отладка в два раза сложнее, чем писать код в первую очередь. Поэтому, если вы пишете код настолько умно, насколько это возможно, вы, по определению, недостаточно умны для его отладки.

~ Брайан Керниган


длиннее, но значительнее, даже элегантнее , чем более компактная аббревиатура KISS ~ надеюсь, что ирония там не потеряна; p
violet313

0

По моему опыту, самая большая победа с точки зрения короткого причудливого кода - это использование рекурсии, а не итерации. Это также одна из самых сложных вещей, которую нужно понять с первого взгляда на код, если только вы не работаете на языке, где все равно рекурсивно. Я бы все же одобрил его за элегантность и скорость разработки, которую он предлагает, но я стараюсь, чтобы в нем содержались подробные комментарии, если он будет выглядеть непрозрачным для будущих сопровождающих или моей будущей личности.


0

Я начинаю никогда не доверять комментариям. Слишком часто комментарии не обновляются, когда код обновляется, и сильно устарели или представляют правила от клиентов / руководства, которые больше не актуальны.

Дошло до того, что в некоторых случаях комментарии даже не соответствуют коду, который они описывают.

Если мне придется выбирать между короткой / фантазией и больше / легче понять то я всегда выбираю последний , если не является действительно хорошей причиной.


0

Читаемость и ремонтопригодность являются ключевыми, и ваш второй пример (то есть более длинный) гораздо больше обоих. Но зачем ограничивать себя двумя вариантами? Даже более длинный код слишком сложен (ИМХО), чтобы не записывать дальше. Я бы поместил его в собственный метод с соответствующим Javadoc (или любым другим) и подходящим именем метода.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.