Pull to refresh

Comments 47

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

А питон вам вообще такое не простит.
Да, неприятие с первого взгляда часто наблюдаю, это ведь непривычно. Но не побоялся написать, потому что уверен. Проверено на кроликах, проверено в практике. Мест для такого форматирования немного, но там где это к месту, это очень полезно.
Появление таких вот мест зачастую сигнализирует о том, что что-то в этом коде не так. В некоторых компаниях так вообще нельзя запушить код, который не проходит проверку на читаемость.

Возможно ваша идея и хороша, но примеры, на мой взгляд, вышли не очень.
Тем не менее, встречается не редко и совсем не у новичков. Допускаю, что обычно это люди, которые пишут большую часть или весь код сами. То есть индивидуалисты. Так это совсем не плохо же…
Да, примерно такой стиль кажется для восприятия более естественным чем как-бы-классический (где закрывающие скобки под операторами).
И есть стойкое подозрение — что это и не кажется. Он явно меньше нагружает механизмы распознавания, тогда как классика нуждается в настойчивой привычке поначалу. Да и потом уже, при наличии привычки — все равно, не приобретает визуальной стройности.
Иногда избежать лесенки можно с помощью обращения условия:

def foobar():
    if something:
        do_1()
        do_2()
        for i in xrange(10):
            do_4()

def foobar():
    if not something:
        return
    do_1()
    do_2()
    for i in xrange(10):
        do_4()

Ключевое слово — иногда.
UFO just landed and posted this here
Так ведь всегда найдутся те, кто скажет, что больше одного RETURN из функции — это плохой стиль, затрудняющий понимание кода.
return — это только частный вариант. Есть еще и continue в цикле:

for i in xrange(10):
    if something:
        do_1()
        do_2()

for i in xrange(10):
    if not something:
        continue
    do_1()
    do_2()

А дальше смотрите сами, что больше затрудняет восприятие кода — лишний return из функции, которая состоит из 5-10 строк и ничего не возвращает, или лишний отступ в теле этой же функции.
Да, я и не спорю. Вариантов много. Навскидку еще два:
1. GOTO
2. «Возбуждение» искусственного исключения с перехватом в функции и с пустой обработкой.
Эти варианты более громоздкие — goto требует «еще одной строки» для метки (и, собственно, придумывания уникального названия этой метки), да и остался он не во всех языках, а механизм исключений, ИМХО, суть есть еще больше скобочко-инструкций, которые лишний раз выносят мозг программисту. Зато их можно впихнуть туда, куда не впихнешь return/continue (с другой стороны, есть конструкция repeat ... until true и break внутри нее для замены отсутствующего goto, но это уже начало какой-то хрен-пойми-что-магии).

Я это все к чему — если цепляется глаз, то стоит написать несколько вариантов одного и того же кода и посмотреть, в котором из них баланс читабельности (как «первичной» — простого чтения кода, который из-за отступов чуть ли не уходит за границу экрана, так и «вторичной» — понимания, WTF есть этот код и что он конкретно делает) выдержан идеальней. А еще лучше дать почитать эти куски кода другим программистам — свежий незамыленный взгляд, общие тенденции к субъективному восприятию (то же фрейдовское «это не опечатка, это для красного словца») и все такое.
1)
if(cond1)
if(cond2)
{
}
некоторыми автокоде-форматтерами неадекватно воспринимается и могут переформатировать произвольным образом. С одной стороны это конечно их проблема, с другой стороны теряется универсальность.

2)
Даже если его использовать «безскобочность», то
if(cond1) if(cond2) {
}
предпочтем перед
if(cond1)
if(cond2)
{
}
Вообще не понимаем, если честно, этого вертикального вытягивания. Без него в один экран влезает как правило в 2-3 раза больше кода, а все-таки удобнее когда код перед глазами, и не надо туда сюда елозить по экранам.
if(cond1) if(cond2) {

прекрасно записывается как

if(cond1 && cond2) {

форматирование же double-if следует применять там где это уместно, скорее в тех случаях, когда условия из за их сложности или длины удобно разнести на две строки. Все для читаемости в первую очередь, что будет более читаемо, то и применять.
if(cond1) if(cond2) {
прекрасно записывается как
if(cond1 && cond2) {
Так мы с этим согласны и ни в коем случае не оспариваем.
Речь у нас шла о том, что когда нужно 2 ифа подряд (по тем или иным причинам), то предпочтем записать их в строчку.
В основном такая необходимость появляется при похожести условий.
Например, если по коду идет периодически if(COND1 && COND2) и нужно вставить условие if(COND1 && COND2 && COND3), то это условие явный претендент на 2 ифа.
Тогда это не мой случай. Ничего не могу сказать про ваш вариант, т.к. тестировал только примение double-if на отдельных строках и могу отвечать только за применимость этого варианта.
В js есть одно место где использование подобного оправдано:
for( a in obj ) if( obj.hasOwnProperty( a ) ){
  do something
}
И то и другое — вредные советы.
Зачем нужна эта экономия в один отступ? Монитор 15"? Или 80-ти символов не хватает?

В первом случае возникает стойкое ощущение, что просто-напросто потеряна then-часть условия.

Во втором вы сами написали в комментарии к коду:
Это не опечатка, два if один за другим и без отступа!

Т.е. даже вы понимаете, что большая часть программистов воспримет это именно как опечатку.
И как уже выше указал x0rHamster:
Иногда избежать лесенки можно с помощью обращения условия

ИМХО это единственный допустимый вариант сокращения.
Попробую пояснить. Речь идет не об экономии в один отступ. Речь идет о том, что такая конструкция в итоге читается как один смысловой блок. Это как читать не по буквам, а целыми словами или иероглифами. То есть переход на следующий уровень восприятия. А про опечатку, это для красного словца.
Ну про экономию отступов я понял из статьи:
Ведь уменьшение на несколько отступов в глубокой лесенке очень помогает.


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

У меня глаза начинают плакать кровью при виде этого :)

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

Это как читать не по буквам, а целыми словами или иероглифами. То есть переход на следующий уровень восприятия.

Не вижу связи с целостностью восприятия.

У каждого языка есть style guides. И большинство программистов изначально ориентированно на них.

Ничего личного, просто подобные велосипеды заставляют «спотыкаться» при чтении кода, вынуждая понять «следующий уровень восприятия» :)
if (a | b | c)
if (d | e)
for(цикл)
if (условие)
{
}

Да, я тоже так писал одно время на С++. Долгое время — лет так несколько. Потом начал получать логические ошибки — ставил else, думая, что оно относится к одному if, а оно, на самом деле, «прилеплялось» к другому.
Причина почему сначала «всё было хорошо» — низкая скорость разработки и мало чужого кода. Когда скорость разработки повышается, мозг начинает ориентироваться на визуальные якори, а не разбирать семантику, ибо ему так легче. Результат — ошибки.

Вобчем, я сейчас пишу только со скобками, даже если 100% уверен, что больше одного оператора в этих скобках не будет.
Да, об этом я написал, нельзя если есть else. else означает, что блок уже слишком сложный для цельного восприятия. Четкое правило предохраняет от ошибок.
Проблема в том, что else появляется позже. При отладке. И добавляет багов ;)
Вы не всегда знаете, что может понадобится через месяц
Я сейчас не спеша ковыряю C на Xcode, и там следующая конструкция форматируется, как я её написал, хотя всю жизнь книги меня приучили к отступам:
if (board_program == NULL)
return 1;
Я согласен, что возможно лучше написать в одну строчку, но тогда ставить точки останова на return становится сложнее.
В общем, о чём я? :) Я, как и вы, пишу теперь со скобками.
Отвратительно. При первой же попытке что-то вставить, добавить else, что-то поменять — получите лишнюю проблему, о которой надо думать. А если кто-то другой полезет в код — и подавно.

Да, и по поводу «выглядит более логично» — это ваше субъективное мнение. Я в обоих случаях вижу несоответствие форматирования и мой перфекционизм бунтует.
получите лишнюю проблему, о которой надо думать
Нет, таких проблем не наблюдается. Когда вы пишете такую конструкцию, else нет, вы не пишете скобочки. Когда появляется else пишете скобочки. Проблем нет, никаких, абсолютно.
это ваше субъективное мнение
«Субъективное», это ваше «отвратительно». А если объективно, то две-три однотипных строки визуально одна над другой читаются легче чем те же строки с отступами. Мне кажется «перфекционистский бунт» мешает вам мыслить объективно.
«Субъективное», это ваше «отвратительно»

А если объективно, то <...> читаются легче <...>

NO U.
Э-э-э… объективные возражения у кого-нибудь будут? Поверьте, каждое ваше высказывание я тщательно взвешиваю и если вы сможете доказать мне неправоту, тут же сам себя и опровергну. Но вот NO не дает почвы для выводов, т.к. укладывается в схему как бездумное и непроверяемое возражение.
Утверждение «X читается легче и красивее, чем Y» само по себе именно что субъективно и непроверяемо.

Объективно — ну, например, замерить время, потраченное на ревью одного и того же кода, записанного в разном стиле. Тогда уже будет из чего делать какие-то выводы.
Это только если не иметь представление о самом понятии удобочитаемости. Несмотря на то, что удобочитаемость вещь сама по себе субъективна, понятие все-таки объективно. Варианты «в строку», «в столбик», «с отступами и разбивкой» разобраны до косточек и объективно вариант «в столбик» более удобочитаем. Много по этому поводу написано в книгах по полиграфии и дизайну.
Пожалуйста почините этому человеку TAB. Кстати по поводу подобных конструкций:
if(someExpression)
someAction();

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

В JavaScript if без скобок использую ТОЛЬКО в одном случае — досрочный возврат из функции:

if (condition) return;

И больше никак.
Ну вот смотрите, вы кричите «ТОЛЬКО», но ведь и вы применяете редкие исключения из правил тогда, когда это возможно. И вот ваш пример. Поэтому не стоит отвечать так категорично. Вот другой пример: comment_7189622, сам так не делал, но понимаю его.
Сам принцип чтения и восприятия кода человеком приводит к таким элементам, конструкцию которая может восприниматься как единое целое можно записать компактно. Я всего-лишь озвучил еще два момента, когда это возможно. Сделал максимально культурно, предварительно тщательно проверил, оттестировал, предупредил от ошибок. Думаю если бы тот же Кармак на Хабре писал инкогнито, его бы тоже… гхм… да что уж тут, забанили бы вы его. Когда мы внедряли соглашения о форматировании кода, еще когда об этом никто не задумывался и крупные фирмы не использовали, тоже было крикунов просто завались, люди даже увольнялись. А сейчас вот вы кричите в обратную сторону. Может пора бы уже и прекратить и попытаться подумать вместо криков?
Я вас никоим образом не хочу оскорбить. Я выразил здесь лишь свое мнение. Кто такой Кармак не знаю. Я никого не банил и не собираюсь. Я ни о чем не кричу ни в какую сторону. Всегда стараюсь думать.

Объясню свой подход к разработке: основной ориентир качества — читаемость кода. Отсюда я, ДЛЯ СЕБЯ, выработал несколько принципов и решений относительно кодирования на JavaScript.

Вложенность — это плохо. Все, что больше 2-х уровней вложенности требует рефакторинга.

Как я избавляюсь от вложенности:

1. Досрочный возврат if (condition) return; в начале функции (именно поэтому — он является единственным исключением. так-как его основное назначение — убирать вложенность)
2. Вынос вложенного функционала в отдельные функции.

Схематичный пример:

function f1(){
    if (условие){
        for(цикл)
        {
            //....
        }
    }
    //....
}

Превращается в:

function f1(){
    if (условие){
        f2();
    }

    //....

    function f2(){
        //....
    }
}


А с учетом первого и в:

function f1(){
    if (!условие) return;
    
    f2();
    
    //....

    function f2(){
        //....
    }
}


Это мое мнение, такое же ИМХО как и у вас в статье. Вы показали свой подход, я свой. Я не говорю, что я прав, а вы не правы. Как говориться на вкус и цвет. Ваш вариант (лично мне) трудно читать, у себя в коде я не допускаю такой вложенности: if if for if.
И какой примерно у вас получается процент служебного кода, то есть тех конструкций, которые направлены на соблюдение этого правила? И включая пустые строки между ними, то есть общий процент пространства отданного под повышение удобочитаемости для вас? Если строго соблюдать такое правило, получим до 40% пространства занято под служебный код. Что не может радовать.
В моем примере нет потери пространства (в коде «до переделки» не проставлены 2 пустые строки для читаемости), но вообще я допускаю, что они будут. Это будет платой за читаемость. Я не гонюсь за минимализацией кода, мой приоритет — читаемость.
Я вот об этом служебном блоке:
    f2();
    
    //....

    function f2(){
        //....
    }

то есть если не создавать функцию f2(), от этого кода останется только комментарий, это как раз пример служебного кода. Тут нет реального кода, поэтому трудно судить о проценте служебного относительно реального.
Просто, чтобы поддержать беседу — вот скопипастил реальный пример функции:
код
function handleLine(obj) {
    //console.log(obj);

    switch (obj.type) {
        case 'typedef':
            return handleTypeDef(obj);
        case 'typeof':
            return handleTypeOf(obj);
        default:
            throw new Error('Строка начинается на что-то отличное от: typedef/typeof!');
    }

    function handleTypeDef(obj){
        varHash[obj.rightVar] = evaluateShifts(obj);
    }

    function handleTypeOf(obj){
        var expVal = evaluateShifts(obj);

        console.log(getStringOfValue(expVal));
    }

    function evaluateShifts(obj) {
        switch (obj.leftVar) {
            case 'void':
                return addShifts(obj.shift, 0);
            case 'errtype':
                return addShifts('errtype', 0);
            default:
                return addShifts(obj.shift, varHash[obj.leftVar]);
        }

        function addShifts(expressionShift, leftVariableShift){
        
            if (leftVariableShift === 'errtype') {
                return 'errtype';
            }
                    
            if (expressionShift + leftVariableShift < 0) {
                return 'errtype';
            } else {
                return expressionShift + leftVariableShift;
            }
            
        }
    }

    function getStringOfValue(val){

        if (val === 'errtype') return 'errtype';

        return 'void' + Array(val+1).join('*');
    }

}



На вскидку не знаю какой была бы здесь разница, если все обратно засунуть…
Ну как бы получается ваш код с ошибками. И ошибки как раз из за применения вашего правила. К примеру, return handleTypeDef(obj); вернет undefine, т.к. в теле функции забыт return. При таком обилии служебного кода не мудрено и запутаться.
Это не ошибка, handleLine вызывается как процедура. return для досрочного возврата. Покажите что-нибудь свое сходного размера.
Честно даю первый же попавшийся
switch и несколько уровней вложенности
switch(opcode) {
    case 1:
        (node.childNodes.length === 0) ? node.appendChild(fragment) : node.insertBefore(node.firstChild, fragment);
        break;
    case 2:
        var nodeparent = node.parentNode;
        if (nodeparent)
            nodeparent.insertBefore(fragment, node);
        break;
    case 3:
        var nodeparent = node.parentNode;
        if (nodeparent) {
            var next_node = node.nextSibling;
            if (next_node)
                nodeparent.insertBefore(fragment, next_node);
            else
                nodeparent.appendChild(fragment);
        }
        break;
    default:
        node.appendChild(fragment);
}
Когда по первому примеру при добавлении кода поймаете баг — перестанете «оптимизировать» так форматирование.
Вы можете привести хотя бы один пример? Не пример откровенного и явно видимого ляпа, а пример когда конкретно вот эти конструкции приводят к ошибке, неправильному пониманию? Не какие-то как вам показалось похожие, а именно такие? Пока такого примера не было за долгую практику применения.
Не уверен, что найдется именно такой пример, но почитайте посты PVS-Studio о ревизиях исходного кода с помощью их статического анализатора — помнится, там встречались ошибки «выглядит похоже / что-то не заметили, вот и перепутали».
Я читал эти отчеты. И мое мнение таково, что из за круглых скобочек внутри сложных условий большинство ошибок такого рода. Сложно отследить в уме парность скобочек и опечятки остаются незамеченными. double-if снижает вероятность появления именно таких ошибок, разделяя и упрощая формулу, там где это возможно.
(Кстати, лично у меня ошибки такого рода примерно одна на 150-200к исходников, мне кажется это довольно хороший показатель, такую частоту ошибок такого рода готов принять.)
используйте flat только там, где это не повредит читаемости кода

Мне кажется, это везде повредит
Sign up to leave a comment.

Articles

Change theme settings