Comments 47
Особенно про фигурные скобки здорово. Из общего соотношения преимуществ/недостатков складывается впечатление почти абсолютной победы Allman (да и прямо сказано: «в большинстве случаев Allman объективно выигрывает у 1TBS»). В реальности, кажется, он не популярнее соперника (кстати, было бы интересно посмотреть статистику).
Наиболее оптимальным вариантом видится комбинация стилей Allman (как основного) и 1TBS (как вспомогательного, используемого в редких случаях) .
Внутри одного проекта, наверное, было бы странно. Тем не менее, сформулированы какие-то правила оптимального выбора.
Внутри одного проекта, наверное, было бы странно.
А почему «странно»? Могу понять, что в чем-то трудно, но все равно решаемо.
Раздел «Правила форматирования в группах», всего половина страницы.
«У каждого программиста есть свои любимые правила форматирования, но если он работает в группе, то должен руководствоваться групповыми правилами. Группа разработчиков согласует единый стиль форматирования...» Далее рассказ из личного опыта работы в команде.
Ну и в заключение дублируется то, что сказано в начале. «Должен» и «не должен», без обоснований. Классика позиционирует как аксиому.
Кстати, в этой же книге фигурные скобки расставлены в худшем по версии Вашей публикации стиле. Аналогично в «Паттернах» Банды Четырех и новых «Паттернах» (Фримен и др.). Это просто наугад три взятые с полки книги.
Ну, у Мартина я цитирую то, с чем согласен. Проблема в том, что часто те правила, которые приводят «классики», не имеют под собой каких-то ясных объяснений. Я попытался эти правила найти. Цитата, которую вы приводите характерна тем, что в ней автор оперирует такими понятиями как «любимый», то есть он априори говорит лишь о субъективной составляющей, и оставляет в стороне объективные критерии удобочитаемости.
По поводу скобок, если они стоят на своем месте, то есть не вызывают затруднений при чтении и формируют корректную визуальную структуру, то удивления вызывать они не будут. Почему они должны вызывать у вас удивления, если вы готовы к тому, что в ваших правилах есть известные исключения? Расстановка же по жесткому правилу, которое принципиально не способно учесть весь контекст текста, всегда в большей (для 1TBS ) или меньшей (для Allman ) степени будет приводить к неудовлетворительному результату.
Соглашения должны быть, но формироваться они должны на основе понимания того, почему мы объективно должны делать так, а не иначе, а не по праву первого или в результате простого следования тому, что пишут «классики».
Мое оценочное суждение о правильном соблюдении принципа наименьшего удивления и Ваше возражение
Почему они должны вызывать у вас удивления, если вы готовы к тому, что в ваших правилах есть известные исключения?
оба относятся больше к философии. До того момента, когда эксперимент по сравнению качества деятельности программиста при работе с кодами в двух стилях подтвердит одно из двух.
Также эксперимент может показать, что это вообще никакой роли не играло:)
Если удалить философию и рассуждения об экперименте, которое не проводилось, то у нас останутся лишь факты, относящиеся к психо-физическим особенностям зрения. А они ничего не говорят нам о том, что мы должны всегда ставить скобки только одним, заранее выбраным способом. По моему, это единственно правильный способ рассужения. :)
if (условие1) действие 1;
действие 2;
действие 3;
if (условие2) действие 4;
if (длинное-предлинное, уходящее за край экрана условие3)
действие 6;
действие 7;
Действие 6 выполняется всегда или только при условие 3==true? А если при беглом просмотре такого кода?
Прошу прощения, вопрос не понял.
Представьте, что Вы читаете этот код.
Первая строка — условие и действие записаны в одну строку. Так делают. Чаще в javascript, мне кажется, но и в C/С++ и других языках допустимо и встречается. С первым if все ясно: действие 1 выполняется, если (условие 1). Действия 2 и 3 выполняются в любом случае.
Второй if — аналогично, выполнится действие 4, если (условие 2).
И доходим до следующего if. Можно подумать, что действие 6 выполнится в любом случае, поскольку в предыдущих случаях код «ниже if» не относился к if.
То есть, я уже воспринимаю код в режиме, когда одна конструкция if умещается целиком в одной строке. Вот и повод удивиться, когда действие 6 не захочет выполняться (с чего бы это? всего-то условие 3 == false).
Кстати, вполне вероятный пример. Встречал рекомендацию именно короткие стейтменты писать в однострочном варианте. Один в один как в этом примере выходит.
С адаптивным правилом по фигурным скобкам возможны вполне себе похожие на эту ситуации.
Псеводкод я понял. Да, тут можно так подумать. Но это потому, что код некорректно отформатирован (не отражает структуру программы, действие 6 должно иметь отступ). И плохо написан в том смысле, что действие 6 надо по-хорошему заключать в кавычки.
Как это относится к правилам расстановки фигурных скобок, я так и не понял.
Предлагаю взять паузу и обдумать всё хорошенько. Тем более что по результатам нашей беседы у меня появились две небольшие формулировки, которые может стоить вставить в статью. :) Надо обдумать.
И плохо написан в том смысле, что действие 6 надо по-хорошему заключать в кавычки.
В кавычках в коде — текстовый литерал получится вместо стейтмента. Уверен, Вы не это имели ввиду, но не пойму, что именно.
Предлагаю взять паузу и обдумать всё хорошенько.Работа так-то и начинается с перекура)
Тем более что по результатам нашей беседы у меня появились две небольшие формулировки, которые может стоить вставить в статью
Значит, все было не зря)
В данном случае добавление пробелов не только облегчило чтение отдельных строк за счет явного разделения идентификаторов внутри их, но и (вместе с выравниванием) облегчило их сравнение, посредством образования компактных визуальны групп по вертикали. Для того, чтобы понять, что делает этот код, уже не надо читать отдельно каждую строку.
При отделении названия функции пробелом её название начинает восприниматься как переменная в математическм выражении. И скобок закрывающих не хватает.
При отделении названия функции пробелом её название начинает восприниматься как переменная в математическм выражении.
Я бы прокомментировал это так: этап выделения и чтения идентификаторов находится на более низком уровне, по сравнению с этапом определения их роли, то есть разбора синтаксического выражения. Первый этап явно выигрывает от наличия пробелов, и это определяется нашими психо-физическими особенностями восприятия (т.е. не может быть изменено). То, как интерпретируется выражение, это результат научения и выработанной привычки, и, следовательно, может быть изменено. Мы можем привыкнуть легко читать тексты программ, написанные на разных языках программирования, но отсутствие явного разделения между идентификаторами всегда будет требовать больших ментальных усилий.
b = a * a ( a (a, a), a);
b = a * a ( a (a,a), a);
b = a * a( a( a, a ), a );
b = a * a(a(a,a), a);
Не понимаю, что сравнить, но если выбрать из того, что есть, то последний.
Первая строка с пробелами читается так:
а умножаем на а, потом делаем что-то в скобках.
Пробел в тексте привычно воспринимается как пауза, отделение нового смыслового блока. Поэтому отделение названия функции пробелом от скобки дробит восприятие.
Относительно первой строки. Во-первых, лишний пробел после первой скобки. Он отрывает аргумент от имени функции.
b = a * a (a (a, a), a)
В принципе, я могу увидеть здесь и a * a
с хвостом, но с большей легкостью это интерпретируется правильным образом (возможно, потому что я знаю, что это правильно).
Во-вторых, имена короткие. Я бы даже сказал нереально короткие. Размер пробела оказывается большим по сравнению с размерами идентификаторов, и они воспринимаются как далеко отстоящие друг от друга. Да и оператор весит не намного меньше имени. Возможно из-за этого имя легче отрывается от списка аргументов.
Более реальный вариант выглядел бы как-то так:
result = firstOperand * functionName (firstArgument, secondArgument)
Здесь в масштабе имен пробел уже не так весом. При желании, конечно, можно и тут напрячься и проинтерпертировать это как firstOperation * functionName
, но мой мозг сопротивляется ("Там же список аргументов!")
result = max * max (min, max)
Все то же. Из-за масштаба просто пробелы выглядят большими. Но список аргументов все равно попадает в поле зрения и учитывается при интерпретации выражения.
С вариантом без пробела, тоже, казалось бы, не дожно быть каких-то затруднений при чтении, учитывая малые длины:
result = max * max(min, max)
Но тем не менее при первом взгляде на это выражени у меня лично все равно возникает некоторый дискомфорт из-за слипшихся max(min
, которые, если смотреть куда-нибудь в строну result
или первого max
, воспринимаются как что-то единое, с каким-то возмущением посередине.
result = min_current * duty_cycle (vin_min)
Как вариант: префиксы еще добавлять.
Ну, получается, что я вас не понял. И сейчас не совсем понимаю. :)
Потому что, как мне кажется, в контексте того, о чем мы говорим (о расстановке пробелов), конкретные имена не важны. Длина важна.
Вы про имена с подчеркиваниями?
result = firstLongLongArgument * longLongFunctionName (local, local)
Я не вижу здесь какой-то особенной проблемы с интерпретацией этого выражения.
Однако, насколько я вас понимаю, вы пытаетесь сказать мне, что существуют ситуации, когда использование пробелов для разделения идентификаторов может привести привести к такому затруднению.
Если так, то я готов с вами согласиться. Наверно, действительно, существуют такие случаи. Те примеры, которые вы приводили, однако, на мой взгляд, к ним не относятся.
Пробелы облегчают нам чтение идентификаторов, но они же используются для формирования визуальной структуры программы. В случае сложных синтаксических конструкций эти две их функции вполне вероятно могут конфликтовать. В таком случае приходится чем-то жертвовать.
Но это, как мне представляется, достаточно редкие случаи. Пока все что приходит в голову, это шаблоны. Наверно, будет неестественно выглядить, если разделить пробелами имя и описание типа в угловых скобках. Просто потому, что тип воспринимается как продолжение имени.
Не упомянут и почему-то редко используется вариант, когда пробелами выделяется список аргументов:
result = someFunction( firstArgument, secondArgument );
Здесь и скобка остаётся у имени функции, и разделены идентификаторы, и акцентировано завершение списка аргументов.
Или ещё из примера со вложенными скобками, где, кстати, завершающая была потеряна:
width = CFStringGetDoubleValue( (CFStringRef)CFArrayGetValueAtIndex(arrayRef,0) );
Для вложенных вызовов, коротких или единственных аргументов выделение пробелами можно не использовать. А также и для синтаксических конструкций, чтоб вне зависимости от подсветки различать вызов и условие:
whole( yards() );
while (nine())
Я бы его и выбрал. Кстати, раньше так и писал.
Наверно, такой вариант тоже возможен. Его основной недостаток, на мой взгляд, что он не соответсвует привычкам из естественного языка. Кроме того, на мой взгляд, все-таки скобки ассоциируются со списком аргументов. То есть у нас есть имя функции и список аргументов, ограниченный скобками. Пробел в конце выглядит ненужным, его основная функция придать симметрию выражению в скобках.
В общем, неестественно.
Ну и последний аргумент мне тоже кажется надуманным.
Вот ещё момент.
result = someFunction (firstArgument, secondArgument);
Здесь пробел разделяет имя функции и список аргументов, то есть подчеркивает структуру этого выражения, то, что функции передается список аргументов. То есть у нас есть два объекта: функция, представляемая её именем, и жестко (за счет прилегающих скобок) связанный список аргументов.
result = someFunction( firstArgument, secondArgument );
А здесь скобка сильнее связана с именем функции, и получается, что фунция получает на список, а отдельные аргументы, и даже как бы не получает, а содержит их что ли. За счет пробелов, формируется ощущение некоторой свободы у аргументов, кажется, что они разбредутся, как овцы в загоне. Список разваливается на отдельные аргументы.
Возможны разные случаи сочетания имени функции и списка аргументов. В примерах рассматривается C++ — тогда это объявление, определение и вызов. Для объявления и определения отделение имени функции от скобки выглядит, пожалуй, оправданно:
private void someFunction (Type1 firstArgument, Type2 secondArgument);
...
void SomeClass::someFunction (Type1 firstArgument, Type2 secondArgument)
{
А для вызова, когда параметры сами могут содержать скобки, кажется более важным именно выделить каждое передаваемое значение.
width1 = CFStringGet((CFStringRef)first(arrayRef,0), second, third(1));
width2 = CFStringGet ((CFStringRef)first(arrayRef,0), second, third(2));
width3 = CFStringGet( (CFStringRef)first(arrayRef,0), second, third(3) );
Разумеется, отличия в синтаксисе других языков будут влиять на рекомендации, направленные на улучшение зрительного восприятия.
Где-то (например, в Go) не требуются скобки в управляющих конструкциях. В Perl скобки не обязательны даже при вызове функций; это и к вопросу о списках и отдельных аргументах. Вместо круглых скобок может использоваться вертикальная черта (Rust). Во многих языках активно используются угловые скобки, часто вложенные.
И далеко не всегда общепринятый для какого-либо языка стиль написания кода соответствует оптимальному визуальному представлению. OTBS и camelCase побеждают.
Мне кажется, здесь вы так сконцетрировали внимание на идентификаторах, игнорируя исключительную важность скобок, что часть скобок растеряли. Я тоже не сразу заметил ошибку, строка воспринимается как набор идентификаторов, где-то там между ними затерялись скобки, что к чему относится, где имена функций, где аргуметы, непонятно.
Я сейчас используя такой стиль:
width = CFStringGetDoubleValue( (CFStringRef) CFArrayGetValueAtIndex( arrayRef, 0 ) )
Здесь сразу видно, где имя функции, потому что к нему привязана открывающая скобка списка аргументов. И в целом структура вложенности прослеживается чуть лучше, у вас же этот важный момент полностью игнорируется.
По поводу вашего стиля я уже отписывался выше: тут и тут.
В вашем варианте мне нравится то, что есть пробелы. В остальном нет. Почему, вы можете прочитать выше.
Ваши аргументы меня не убеждают, и в целом, на мой взгляд, выражение лучше не выглядит. Кажется каким-то рыхлым. Но спорить не буду. Тот стиль, который я привел, я использую достаточно давно, и он не вызывает у меня отторжения, хотя ничего идеального, конечно, нет. Попробуйте использовать свой стиль хотя бы несколько лет, возможно, вы не будете так категоричны.
Да, я писал про идентификаторы и про важность разделения их пробелами. Мне нужен был пример, но судя по всему, я подобрал его не совсем удачно, поскольку вы уже, как минимум, второй человек который пишет мне про структурированные параметры. Поэтому я его упростил: убрал ненужное приведение типа.
Ошибка со скобкой достаточно показательна. Во-первых, это хороший пример того, что надо использовать правильный инструмент для редактирования, который делает такие ошибки заметными. Чего я не делал по определенным причинам. Но это относится к редактированию, а не к чтению, и в данной статье не рассматривается. Во-вторых, интересно, что мне отсутствие скобки не мешало воспринимать этот фрагмент правильным образом. Но у меня были определенные ожидания. Сильно ли она помешала вам?
P.S. Такую расстановку скобок я (и мои коллеги вслед за мной) использую около десяти лет, на работе. Я всегда в поиске более лучшего стиля, и время от времени что-то меняю, но к этим скобкам вопросов еще не возникало.
По поводу того, какой из вариантов труднее воспринимать, думаю смысла нет обсуждать. поскольку это, как я понимаю, уже достаточно высокоуровневая психическая деятельность, и она связана со сформированными привычками и определенными ментальными моделями (а у нас они по видимому отличаются).
Но вот ваше утверждение:
Но восприятие текста гораздо менее важно, чем отсутствие ошибок. Первая задача стиля, как я это вижу, — способствовать минимизации ошибок, а уже потом можно думать над читабельностью.
— я принять не могу. Поскольку оно противоречит само себе.
Невозможно обнаруживать и исправлять ошибки без понимания программы, а оно образуется только как результат восприятия её текста. Поэтому восприятие первично. Вы же обнаружили отсутствие скобки, только после того, как поняли, что это за выражение, не так ли? Вы можете утверждать, например, что ваш вариант легче для восприятия, и поэтому позволяет легче обнаруживать ошибки. И я (теоретически) могу с вами согласиться в этом. Но утверждать, что он позволяет находить ошибки, независимо от того, что может формировать текст трудный для восприятия, я думаю, вы всё-таки не станете.
Основы оптимального стиля. Когнитивно-ориентированный анализ удобочитаемости текста программы