Pull to refresh

Comments 175

Хм, могу ошибаться но дядюшка Боб как раз из за проблем 1 и 3 рекомендует делать только один возврат в конце.
Найдете ссылку на рекомендацию?
Постараюсь вечером найти, если не забуду.
Timmmm Хм, не нашел, хотя казалось бы точно читал. При этом у Макконела, книга которого мне как то больше понравилась, есть раздел «множественные возвраты и функции» в котором ранний выход при проверке условий как раз поощряется.
Спасибо, что не забыл.
«поощряется»? На мой взгляд это единственно верное выполнение функции. Все перечисленные проблемы, это же смех, притянуты за уши. Хочется добраться до реальных проблем, если таковые есть.
Там всё хитро. Надеюсь, изложу близко к тому, что написано у Макконнелла, но, возможно, на моё изложение наложились и собственные деформации и опыт.

Тезис: функция должна соблюдать инварианты.
Сначала надо выполнить проверки входных данных и не приступать к работе, если они не соблюдены.
Дальше выполняется код, который должен иметь одну точку выхода.
Да, я примерно так же этот раздел понял. Но все таки странно что у меня в памяти это в другом виде отложилось… Ведь и сам в основном писал сначала проверки с возвратом, далее основной код, и в конце еще один возврат.
А что делать, если проверка входных данных — дорогостоящая операция? Обычно такое бывает когда проверка входных данных делается где-то в вызывающем коде один раз, а не каждый раз при вызове «вложенной функции».
Пример:
double div(a,b)
{
  if(b==0.0) {
      if(a==0.0) return NaN;
    return sign(a)*Inf;
  }
  return a/b;
}
int main()
{
  const double denom=3.14;
  for (double num: {3.14, 2.71, 0.0}) {
    cout<<div(num,denom)<<"\n";
  }
}

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

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

Это уже есть, называется assert, проблемы не решает.
Потому, что всё равно бывает необходимо отлавливать ошибки в рантайме (в production) и отлавливать их на нижнем уровне вложенности (assert) — медленно, а на верхнем — ненадёжно (легко ошибиться, пропустив недопустимое значение параметра дальше по стеку вызова).
Assert — это только для дебага, когда скорость не важна.
Тут возникает идея Design by contract.
UFO just landed and posted this here
UFO just landed and posted this here
Это уже design by contract. Не придумал, как такое сделать на C++.
UFO just landed and posted this here
Как-то так наверное
#include <iostream>


enum class Infinity
{
    Positive,
    Negative
};

std::ostream& operator << (std::ostream& stream, Infinity value)
{
    switch (value)
    {
    case Infinity::Positive:
        stream << "+Inf";
        break;
    case Infinity::Negative:
        stream << "-Inf";
    }
    return stream;
}

struct NaN {};

std::ostream& operator<< (std::ostream& stream, const NaN&)
{
    return stream << "NaN";
}

template<size_t A, size_t B>
struct Div
{
    static constexpr size_t Value = A / B;
};

template<size_t X>
struct Div<X, 0>
{
    static constexpr Infinity Value = (X > 0) ? Infinity::Positive : Infinity::Negative;
};

template<>
struct Div<0, 0>
{
    static constexpr NaN Value = NaN{};
};

#define CHECK(A, B) << "Div<" << #A << "," << #B << "> : " << Div<A,B>::Value << std::endl

int main()
{

    std::cout
        CHECK(5, 2)
        CHECK(5, -2)
        CHECK(-5, 2)
        CHECK(-5, -2)
        CHECK(5, 0)
        CHECK(-5, 0)
        CHECK(0, 1)
        CHECK(0, -1)
        CHECK(0, 0)
        ;

    return 0;
}


Правда компилятор позволяет в шаблонах использовать только исчислимые типы.
не сработает с рантайм переменными.
int main()
{ 
  auto denom=rand()+3; // всегда >0
  for (int i=0; i<10; i++) {
    cout<<Div<rand(),denom><<" ";
  }
  
}

Хотелось бы что-то такое, что позволяет с одной стороны указать диапазон допустимых значений входящих параметров, а с другой — выкидывать эти проверки когда они и так выполнены уровнем выше по стеку вызова.
Значит я не понял идеи тремя комментариями выше.
Если хочется рантайм с исключениями, можно использовать boost::variant / std::variant.
Что бы выкинуть исключение, нужно проверить условие, нет никакого выигрыша. Фактически я говорю о необходимости design by contract вместо обработки ошибок через ветвления или assert. Assert работает только в дебаге, а ветвления — всегда, даже когда не нужно. Design by contact как бы совмещает лучшее из обоих. Даже ещё лучше чем assert, который становится static_assert где можно и runtime ветвлением где нельзя.
Давайте сначала договоримся о терминах: какую задачу мы хотим решить и какие ограничения соблюсти?

(несмотря на то, что это обсуждение уже далеко за пределами тематики исходной статьи)
С моей точки зрения общая задача стоит так: обеспечить надёжную и предсказуему работу программ. Для этого полезно автоматизировать контроль за соблюдением диапазонов входящих значений параметров функций. На сегодняшний день такой контроль реализуется чаще всего двумя способами:
if (isInvalid(param)) return Failed;
и
assert(isValid(param));
Первый способ связан с потерей производительности, которую вполне можно было бы избежать. Во втором случае проверки просто нет в Release, а если есть, то неудобно контролировать поток выполнения — это не return. Хотелось бы что-то вроде варианта с `if`, но так, что бы компилятор выкидывал проверку, когда это возможно. Также хотелось бы, что бы компилятор автоматически переносил эту проверку максимально высоко по стеку вызова. Лучшим решение этой проблемы, из тех что я знаю, является Design by Contract, которого в мейнстримных языках вроде нет, даже в Rust, если я правильно понимаю.

В Eiffel, единственном языке, который, как я знаю, был специально спроектирован под Design by Contract, проверки в runtime рекомендуется исключать в продакшен окружении. По сути, те же assert, только сбоку.

UFO just landed and posted this here
Сценарий 1. Данные известны в момент компиляции. Передаём в некоторое выражение, получаем результат. Валидация может быть проведена во время компиляции, во время выполнения проверка не требуется и ничего не сто'ит.
Сценарий 2. Данные получены из внешнего источника во время выполнения, их надо передать дальше. Проверить можно только во время выполнения. Выполнять следует на том слое, который уверенно может обработать данные на своём уровне.

Теперь контрольные вопросы:
0. Когда известны входные данные?
1. Применим ли наиболее быстрый вариант в вашем конкретном случае?
Если да, то выбор принят.
2. Если ответ на вопрос отрицательный, то можем ли мы локализовать место, где входные данные могут быть провалидированы?
Если да, то выбор принят. На остальных уровнях делаем код прозрачным к для исключений и сохраняющим инварианты.
3. Если ответ отрицательный, то можем ли мы спроектировать систему так, чтобы ответ на вопрос 2 стал утвердительным?
Если да, то переходим к пункту 2 и повторяем.
Если нет, то у вас проблемы с пониманием задачи.
UFO just landed and posted this here
То есть всего лишь делаем обёртку над типом только для того, чтобы гарантировать, что в эту обёртку программист должен влезть?
UFO just landed and posted this here
Если некоторая функция предназначена для математических расчётов (работа в некоторой предметной области), то возвращаемый тип хотелось бы иметь из той же предметной области, а не сколь угодно выразительные типы, с которыми надо много воевать, чтобы наконец выковырять то, что нужно.
UFO just landed and posted this here
Может тогда стоит написать, что там с этим делать, например, если надо посчитать a/b + c/d, к примеру?
UFO just landed and posted this here
Хорошо. Проверять где надо? В этой функции или где-то выше по стеку вызовов?
Если откуда-то сверху этот аргумент приходит в виде, исключающем необходимость проверки, например, когда аргумент — промежуточный результат, полученный как 1+x^2?
А может быть есть другие проверки, которые гарантированно не дадут ноль в знаменателе, например, модуль аргумента строго больше единицы? Поймёт ли ваш чекер, что его проверка лишняя?

Далее, ну, допустим, проверили мы, можем сделать корректную обработку этой ситуации или для этого нужен контекст более высокий по стеку вызовов?

Могу ли я сделать вывод, что приведённый выше фрагмент не претендует на универсальность, а полезен лишь как пример, что так в некоторых ситуациях можно сделать, и закончим нашу дискуссию?
UFO just landed and posted this here
Можно ввести новый тип, «уже проверенный». Делать проверки при работе с непроверенным типом и при конвертации, а при работе с проверенным типом проверки будут не нужны. Примером подобного типа будет gsl::not_null.
Код ниже
if Ошибка1 {
// Обработка ошибки 1
result = 1;
} elsif Ошибка2 {
// Обработка ошибки 2
result = 2;
} elsif Ошибка3 {
// Обработка ошибки 3
result = 3;
} else {
// Все ок, работаем дальше.
result = 4;
}
return result;

лишен недостатков 1, 3 и соответствует идее, переданной в статье.
Личное мнение по недостатку 4 — ВСЕГДА лучше использовать промежуточную переменную вместо множества RETURN операторов.
В некоторых ЯП для этих целей даже неявно переменная объявляется.
Ага, проблема только в том, что проверочные условия могут выходить за рамки одной иерархии if-else. А также в том случае, когда возвращаемый результат не единообразен, как в случае с контроллером из статьи.
Если выходят за рамки одного уровня иерархии возможно стоит рассмотреть возможность вынести в отдельные функции?
Вы проверку каждого входного параметра будете в отдельную функцию выносить или что?
Если у вас вложенные иерархии if-else — то это уже явно не проверка одного параметра, а проверка каких то комбинаций условий. Которые лучше в отдельные функции вынести для снижения цикломатической сложности конкретной функции, имхо.
А я где-то говорил про вложенные иерархии if-else?
проблема только в том, что проверочные условия могут выходить за рамки одной иерархии if-else
За рамками одной иерархии собственно как раз вложенные иерархии. Или я что то не так понял?
как в случае с контроллером из статьи.

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

И чем же это хорошо? Оператор return сразу однозначно дает понять, что обработка этой ветви завершена. Присваивание result оставляет туман неизвестности — а что если дальше с этим результатом будет что-то происходить? Получается что-то типа switch с неполным набором брейков — иногда это может быть полезно, но в общем случае практика плохая.
Return может затеряться (визуально) в куче кода и человек, который сопровождает код будет искренне удивлен, почему выполнение не доходит до конца метода (функции).
Чтобы все-таки понять почему — ему придется заходить в отладчике во все if'ы и проверять «а не в этом ли условии программа выходит на уровень вверх?».
В случае с if-elsif-elsif-else достаточно поставить точку останова на 1м if-e и увидеть куда зайдет программа в процессе выполнения.
Честно говоря, не очень понял аргумент с точкой останова. Ясно же, что и в случае с return-ами вариант «встать на первое условие внутри функции и проследить выполнение до момента выхода» тоже сработает. А если функция такая громоздкая, что проходить её пошагово сначала — нерациональная трата времени, так и в случае с цепочкой elseif-ов результат будет ровно такой же (не говоря уж о вопросе, рационально ли её делать в этом случае одной функцией).
Ответ комментом ниже.
Т.е. if-elsif-elsif-else сразу дает человеку понять, что есть ситуации, когда не весь код выполнится.
А глядя на конструкции
if Условие1 {
return;
}

if Условие2 {
return;
}

if Условие3 {
return;
}

// все ок, работаем.


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

Да, можно вынести лесенку в отдельный метод.
И вариант оформления if-elseif с единственным return в конце принуждает это делать.
Иначе не написать так, чтобы вложенных условий не было.
Добавь в начале
bool ok = true;
а после всех проверок
if (ok)
{
}

Иногда так даже удобней, но обычно можно и проще
// If vse Ok
{
}
Этот вариант ни чем не лучше if-elsif-else.
Тем более обязывает нас заводить очередную дополнительную переменную.
Я бы сказал, что хуже. Т.к. менее устойчив к рефакторингу и unit тестированию.
Даже с таким подходом корректная логика и некорректная начинают смешиваться. По моему, по возможности, проще валидации выделять в отдельные функции, а после всех проверок исполнять уже непосредственно основную логику.
Выделять в отдельные функции стоит если эти проверки делаются более одного раза.
4 пример
Очевидное преимущество второго способа — не надо поднимать глаза и искать что за там $nextState объявлен, код четко разбит на 3 случая, читать проще будет.
Не знал, что Капитана Очевидность зовут Szymon Krajewski.

Неужели для кого-то это что-то новое? Удивляет, что такую мелочь можно раздуть до размеров статьи, которую начинают переводить и репостить.

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

Хотя эти вещи кажутся очевидными, в реальном мире, я не раз сталкивался с таким вот if/else hellом. А иногда и вообще без какой либо валидации.

если честно подобный подход считаю очевидным, пришел к следующему правилам для себя:


  1. Только один return (не считая исключений), можно использовать переменную.
  2. return это последняя строка
  3. Исключения только для исключительных ситуаций.
  4. Не более одного уровня отступов, не считая отступов функции => рефакторить в приватный метод.

все остальное это следствие из этих правил.

Не более одного уровня отступов, не считая отступов функции => рефакторить в приватный метод.

И как алгоритм Флойда должен выглядеть?))

так как php используется в вебе то такое решение вполне сойдет


<?php

class Floid
{
    private $w;

    public function __construct(array $w, int $n)
    {
        // если нужно проверяем данные
        $this->w = $w;
        $this->n = $n;
    }

    public function getResult()
    {
        for ($k = 1; $k <= $this->n; $k++) {
            $this->forByI($k);
        }
        return $this->w;
    }

    private function forByI($k)
    {
        for ($i = 1; $i <= $this->n; $i++) {
            $this->calc($k, $i);
        }
    }

    private function calc($k, $i)
    {
        for ($j = 1; $j <= $this->n; $j++) {
            $this->w[$i][$j] = min(
                $this->w[$i][$j],
                $this->w[$i][$k] + $this->w[$k][$j]
            );
        }
    }
}

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


ах да по поводу отсутуов внутри min забыл упомянуть что такое в функциях можно делать но в меру, могу убрать но не вижу смысла зачем от этого избавляться

Вам не кажется, что исходный вариант (4 строки) выглядит проще?
Ну т.е. понятно, что можно разбивать код до уровня "любая инструкция в отдельной ф-ции", а в С++ это еще и работать быстро будет. Но по-моему не имеет смысл резать на отдельные функции что-то цельное, например алгоритмы.

исходный вариант 4 строки которые в примере это на самом деле, + 1 строка на функцию + 2 строки на скобки этой функции(в psr открывающая с новой строки) + 1 строка закрывающая скобка для каждого цикла (итого 3) + 1 строка на return
итого получаем 4 полезных строки размазанные по еще 7 строкам получаем 11 входных строк.


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


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


Для примера цитата про "алгоритм установки gentoo" http://bash.im/quote/394695 если вам нравиться подобное в коде, то у всех свои вкусы и каждый вправе иметь свои взгляды.


по поводу разбивать на части то что цельное, вы когда запрос к бд посылаете вы ручками создаете сокет, подключаетесь к бд и посылаете запрос или все таки разделяете на уровень по работе с бд и потому что генерирует запросы?
ото алгоритм работы с бд можно назвать и это:


  1. Создать подключение
  2. Аутедентификация + авторизация
  3. выбрать бд
  4. цикл по запросам для бд

как бы тоже 4 строки, не надо все под одну гребенку нести.


а то что я разбил исходный текст на 3 метода это потому что у алгоритма 3 уровня отступов

При использовании готовых алгоритмов при таком подходе есть шанс усложнить поддержку. В одной функции алгоритм может быть узнаваем, а при разбитии на части — нет.

давайте я возьму очень популярный алгоритм которым большинство присутствующих пользуется и не задумывается (реализацию специально на пыхе найду) https://github.com/phpseclib/phpseclib/blob/master/phpseclib/Crypt/RSA.php


вот сразу как открываешь подобный файл и видишь родные $i, $j, $n прям сразу ясно становиться что за алгоритм


прям каждый день сниться когда все это в одной функции будет не разделено на вспомогательные вещи, прям сразу ясно что это RSA, а не DSA


теперь запишите его как вы говорили все в одну функцию, да да да decrypt, encrypt, sign, verify, createkey пусть там на входе массив будет и сказан тип операции что надо сделать и разные ключи с параметрами


я вам даже начну


<?php function rsa(array $params) { return ;/* тут надо его просто расчитать в зависимости от параметров и да я специально все в одну строку пишу, чтобы строки сэкономить это же так важно */}

и теперь ответьте честно будет выглядеть проще?


есть GodObject, но похоже можно еще создавать GodFunction или GodMethod, не знал о последних двух, спасибо что помогли их понять еще раз.

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


P.S. А экономия строк тут непричём, дело в разрыве знакомого многим процесса на выбранные из моей головы куски с придуманными мною названиями.

только что вот считать оригинальным описанием?
то что написано в rfc? RFC 2313, RFC 2437, RFC 3447 в каком именно?
или первая его оригинальная публикация в научных журналах ?


только вот в этих документах нет ничего конкретного по реализации (кроме математических формул) потому что парадигм программирования очень много:


  1. Императивное программирование
    1.1. ООП
    1.2. Процедурное и тд
  2. Декларативное программироваине
    2.1. Функциональное и тд

Вы будете искать rfc описывающее rsa в рамках ООП или ФП? таких нет, потому что rfc это описание и оно не привязано к реализации (парадигме или языку программирования), а как вы будете делать реализацию и в рамках какой парадигмы, зависит от вас и возможно от языка программирования что будете использовать (глупо например используя java писать в фп парадигме или использовать haskell и писать в ооп)


Даже если вы выберете ООП то тут есть еще куча вопрос:


  1. mutable или immutable ?
  2. stateless или statefull ?

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


Только вот проблема в том что на вики выложен пример реализации в виде псевдо кода или какой нибудь наиболее наглядный пример на популярном языке, например в ФП не принято использовать циклы и вместо циклов бы тут использовали array map и реализация была бы совсем другой (в вики нет примера алгоритма флойда в рамках ФП).


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

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

простите не вам про одну строку.

а как по вашему должен выглядеть алгоритм rsa со всеми основными вещами (decrypt, encrypt, sign, verify, createkey причем не разбивая на отдельные функции)
а также работу с BigInt тоже не выносить отдельно это же цельное, там ниже задал вопрос, но не вам к сожалению, поэтому и тут продублировал, а и еще постарайтесь сделать поменьше строк, "это очень важно".

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

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


вот например взять тот же алгоритм флойда и пхп, там есть операция +, если я выйду за пределы int во время этой операции?


Для работы с большими числами есть bc math или gmp, но это же алгоритм флойда надо ли их использовать, мне кажется да (если можно за пределы int уйти) но вы говорите что алгоритм флойда это то неделимое и надо смотреть на всю картину в целом, давайте тогда и работу с большими числами реализуем внутри циклов, как тогда будет выглядеть алгоритм без вспомогательных функций (методов) для работы с большими числами ?


по поводу изображений пример не корректен, если было так как вы говорите то в редакторах не было бы zoom (ведь удобней смотреть на все изображение без приближений вы так написали), поэтому пример с изображениями как раз и говорит что иногда один элемент (изображение) состоит из множества разных элементов (пикселей и векторов, ведь внезапно бывает еще и векторная графика), но мы не задумываемся о этих деталях когда смотрим на изображение пока нет в этом необходимости.


к сожалению я так и не вижу реализацию rsa от вас

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


не было бы zoom (ведь удобней смотреть на все изображение без приближений вы так написали)

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


к сожалению я так и не вижу реализацию rsa от вас

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

Я писал, что в режиме большого приближения изображение целиком сложно воспринять.

Понятие приближения зависит от изображения и от того что вы хотите увидеть, но обратите внимание вы сами начали говорить о


не имеет смысл резать на отдельные функции что-то цельное, например алгоритмы

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


И я вам показал пример алгоритма где имеет смысл делить на отдельные методы, это rsa.


И да, не вижу ни одной причины из обсуждения в этой ветке, почему кто-то должен всё объединять в одну функцию

Вот я это уже цитировал выше повторю "не имеет смысл резать на отдельные функции что-то цельное, например алгоритмы"


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

и от того что вы хотите увидеть

Правильно. В данном случае речь о том, чтобы увидеть алгоритм целиком.


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

Во-первых, это не я говорил.
Во-вторых, о том и речь, что нет места, где есть картина, есть только куча отдельных пикселей. В вашем примере нет места с двумя вложенными циклами, зато есть 3 места с одним циклом. Причем у одного из них название завязано на реализацию. А если мы for на foreach поменяем, или на map?


И я вам показал пример алгоритма где имеет смысл делить на отдельные методы, это rsa.

В этой ветке никто не говорил что делить на методы вообще не надо.


Вот я это уже цитировал выше повторю

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


а значит исходя из ваших же слов не имеет смысла резать на отдельные функции

Исходя из моих слов (а именно "Отдельные действия можно вынести в функции и использовать во многих других действиях") это никак не следует. Потому что написано прямо противоположное. Сложение можно вынести в функцию и использовать в умножении. Отдельные независимые части, из которых состоит сложение тоже можно вынести в функции и использовать в сложении.


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

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

Сложность зависит только от кол-ва функций? complexity(functionCount) или есть еще другие параметры ?


Например, в случае с алгоритмом Флойда, знающему его человеку ориентироваться будет проще, чем в длинном.

каким образом знание или незнание алгоритма зависит от его длинны? если я перепишу алгоритм флойда в одну строку вам будет проще в нем ориентироваться и он станет вам более знакомым?


кто вам сказал что псевдо код выложенный на сайте вики это единственная реализация?


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


попробуйте реализовать алгоритм флойда в рамках фп и посмотрите понадобится ли вам вспомогательные функции (в том числе и анонимные).

Не знаю, за что минусят — вполне адекватный подход

"умом Россию не понять ..."

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

При правильной органиизации кода return не теряется, наоборот, обработчики ошибок сразу видны в том месте где она происходит, а не после основного рабочего кода.
Resharper / IntelliJ Idea имеют автоматический рефакторинг для этого.
Хм. Всегда так писал. Всегда это казалось очевидным. Сначала проверить входные данные, а только потом их обрабатывать.
Не верить же на слово тому, кто вызывает функцию.
Тут вопрос не «верить/не верить», а как проверять, если не верить.
Некорректные данные на входе функции стоят часов отладки.
Ассерт на тестах помогает. А в продакшене нет. И почему-то именно в продакшене не корректные данные и приплывают в самых неожиданных местах.
Речь шла об отладке, в предположении что отлаживается Debug версия.
Во втором варианте контроллера уже намечается ситуация, из-за которой я иногда избегаю ранних возвратов — сложная логика в той же ветви, что и возврат. Тут ещё относительно легко сделать что-то вроде this->createWrongResponseWithFlash($mesaage) один раз и юзать его при фейлах. Если же логика разная, то часто не хватает условного return, типа return? value; которая возвращает если value приводится к fa
возвращает value, если приводится к fslse и игнорится, если к true. На C можно макрос написать, ав других есть языках подобное или я хочу чего-то странного?
Рекомендую прочитать книгу «Elegant Objects» Егора Бугаенко — там приведен прекрасный пример отделения валидации от бизнес-логики. Если вкратце, то вот как это выглядит.
Необходимо выделить интерфейс из вашего класса (который в текущей версии содержит и валидацию, и бизнес-логику).
У интерфейса следует сделать две реализации: первая реализует бизнес-логику без какой-либо валидации входных параметров, вторая — содержит всю необходимую валидацию входных параметров. При этом вторая реализация инкапсулирует экземпляр исходного интерфейса.
После всех манипуляций вы можете использовать либо экземпляр первой реализации — при этом у вас не будет валидации (иногда бывает необходимо для тестов) — или же можете завернуть этот экземпляр в инстанс второй реализации — в этом случае добавляется валидация.
В общем, используйте декораторы :)
Звучит, конечно, круто. К сожалению, не всегда удобно. В том же Perl сей подход вызовет больше сложностей в связи с корявостью ООП

Вот именно в Perl такой подход проблем не вызовет из-за нормальной реализации ООП. Другой вопрос, что на валидации входных параметров свет клином не сошёлся.

Видел такое доведенное до идиотизма. Все из себя представляет комманды, вызывающие другие комманды (Command Pattern) и так 5 уровней вложенности для того, чтобы сделать выборку по ID из базы данных.
Используем одну переменную и один return. И потом надеемся, что компилятор скомпилирует так, что не заставит программу пробегать по всей цепочке условий, а выполнит безусловный переход на return… иначе пользователь бежит и покупает новый процессор т.к. этот тормозить чего-то начал :)
А есть сведения о компиляторах, которые заставят проверять все ветки условия if-elsif-elsif-elsif-else если первое условие истинно?
В Паскале это была неявная переменная Result, а вместо возврата — псевдофункция Exit.
Не сказал бы, что писать в таком стиле доставляло удовольствие.
Если на вход функции данные поступают чаще правильные, чем неправильные, то в инструкциях для процессора эта концепция выглядит так.
Проверить условие → прыгнуть дальше → проверить условие → прыгнуть дальше → проверить условие → прыгнуть на выполнение полезного действия.

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

Что лучше: одноуровневые условия и лишние прыжки или вложенные условия и отсутствие прыжков? Умеет ли компилятор оптимизировать лишние прыжки?

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

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

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

Сейчас не найду точных цифр, но, помнится, что ошибочная ветка в if вместо else добавляет пару десятков тактов из-за предсказания ветвлений. Процессор идет по ветке обработки ошибки и потом, когда оказывается, что данные верны, вынужден откатиться назад. Хотя, когда все поставят патчи для Meltdown, эта проблема перестанет быть актуальной :)

UFO just landed and posted this here
Низкая вложенность — это очень удобно. Только желательно логировать выходы из функций, в таком случае будет понятно где обрубается, по типу такого:
Давно пишу в таком стиле. Как-то сам пришел к нему.
Вот в первые полгода работы, пока набирался опыта, писал в духе if else, потом это перестало нравиться, стал делать ранние выходы, аргументируя себе это тем, что функция раньше закончится, а, следовательно, выполнение программы быстрее выйдет.
А сейчас больше претензий к читаемости, конечно, для ранних выходов чётко видно, когда и что произойдёт этапами. Хотя где-то встречалось мнение, что для некоторых программ или приложений должен быть строго 1 оператор return, поэтому сомнения иногда закрадываются.
Вот мнение это встречалось, но непонятно на чем основано. Может быть какая-то оптимизация для компиляторов времен DOS. Или что-нибудь связанное с функциональщиной и доказуемостью алгоритма. Помню только, что что-то древнее.
UFO just landed and posted this here
Для ANSI C например, так? Звучит логично.
UFO just landed and posted this here
UFO just landed and posted this here

Стандарты пишут люди. Люди могут ошибаться.

UFO just landed and posted this here
А ещё стандарты развиваются, исправляются и дополняются
Именно это я и хотел сказать. То, что это стандарт, и «так принято», еще не означает, что больше не существует других более выгодных вариантов написания кода.
Неглупые люди приняли рекоммендации по валидации паролей. Все эти перемешивания заглавных и строчных букв, цифр и спецсимволов.
UFO just landed and posted this here
Вообще-то от програмных ошибок и ракеты взрывались. Но вот теперь уже — машины врезаются.

И те же АЭС взрываются и самолеты падают — хотя у меня нет статистики по тому, какая часть из проблем связана с плохим кодом.

Мне ваша аргументация кажется очень слабой.
UFO just landed and posted this here
> выводы делаются не только на голой теории, но и с учетом анализа
> обнаруженных дефектов в уже существующих и существовавших изделиях

Вы уверенны, что это имеет отношение к статье?

> Это к тому, что если что-то написано, то оно написано не «с потолка».

Мой пример с паролями — опровергает ваше утверждение.

> стандарты оперативно обновляются и перевыпускаются.

Вы в общем может быть правы, если исключить «оперативно».

> значит за десятки лет не нашлось ни одного события или существенного
> аргумента, обоснованно говорящих о его некорректности.

Утверждение никак не связано логически с тем, что вы написали до него. И в целом ложно.
UFO just landed and posted this here
> В статье предлагается идея, противоречащая утвержденной и
> выверенной практике в отраслях, критичных к безопасности и
> корректности ПО.

Это факт или мнение?

> Я специально выше отметил, что это совершенно разные сферы с
> совершенно разными методами.

Безопасность и безопасность — это разные сферы. А безопасность и кодирование — это одно и то же. Я правильно вас не понимаю?

> В авиации и энергетике такое в принципе невозможно.

Это факт или мнение?

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

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

> Вывод очевиден: «за все это время не нашлось ни одного события
> или аргумента, весомо и объективно говорящего о его
> неправильности».

Вывод очевиден. Формальную логику в школах отменили зря.
Безопасность и безопасность — это разные сферы.

Да )) Безопасность в области "постить котиков", личных и корпоративных финансов, медицины и АЭС — это разные безопасности, разная цена ошибки, следовательно разные ресурсы выделяются на их предотвращение.


Безопасность (в контексте безошибочности) и удобство обычно противоречат друг другу, и в отраслях, где цена ошибки относительно низка удобство побеждает. Вон, посмотрите в топике про malloc какие дебаты пошли из-за одного if, причём большинство (субъективно) склоняется к мнению, что можно доверять исторически сложившемуся поведению некоторых компиляторов и некоторых реализаций ОС с некоторыми настройками. Что какие-то частные случаи не стоят вставки if после каждого malloc для проверки на NULL, поскольку на подавляющем большинстве систем реальных пользователей проверка не гарантирует реального выделения памяти, а фактически сложившиеся традиции в реализации дадут аналогичный самой простой проверке результат. Что это неопределенное поведение, в любой момент могущее измениться, никого не парит.

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

Я надеюсь, что статью вы читали и заметили, что автор не предлагал не проверять какие-то условия, что там кто ленится делать — это их ответственность, главное чтоб не вредили намеренно.

В статье речь шла о выделении логических блоков при написании функции. F0iL написал, что это противоречит безопасности и выверенной практике в отраслях. Что это вообще такое — выверенная практика в разработке ПО? Ну хорошо бывают стандарты — которые устаревают и пересматриваются, бывают рекоммендации, бывает то, что для кого-то работает в определенных условиях и они эти подходы масштабируют. Я привел пример того, что рекоммендации в индустрии бывают ошибочными, обновляются не достаточно быстро, мало того реализации обновленных рекоммендаций затягиваются на годы.
F0iL продолжил настаивать, воспользовавшись аргументом что это другая сфера. Что вы хотели сказать/доказать я вообще не понимаю.

Вообще статья мало нового постулирует. Схожие идеи развиваются гораздо более формально в рамках фреймворков типа .NET, где часть проверок уходит в аттрибуты, что это как не частный случай раннего возврата? В более широком смысле aspect oriented prorgramming. Точнее будет сказать, то что автор описывает в статье — это poor man's aspect oriented programming — переизобретение велосипеда.

В связи с этим дважды не понятно чего такое F0iL выдумывает, про стандарты, устоявшиеся практики и подобное.
UFO just landed and posted this here
> Факт.

ФАКТ, факта, муж. (лат. factum).
1. Действительное событие, явление, то, что произошло в действительности. Исторический факт. Смело смотреть фактам в лицо. Это — факт, а не выдумка.…
2. Данное, являющееся материалом для какого-нибудь заключения, вывода или служащее проверкой предположения, теории. Изложив факты, автор переходит к их толкованию. Исследование опирается на факты. Проверить факты. Опровергнуть что-нибудь с фактами в руках. Обильный фактами рассказ. Искажать факты.…

МНЕ́НИЕ, мнения, ср.
1. Взгляд на что-нибудь, суждение о чем-нибудь, выраженное в словах. Интересное мнение. Придерживаться какого-нибудь мнения по вопросу о чем-нибудь. По моему мнению. Разделять чье-нибудь мнение. Обменяться мнениями. Усвоить чужое мнение.
|| Официальное заключение по какому-нибудь вопросу, требующему решения (офиц.). Мнение экспертизы. Запросить мнение комиссии. Особое мнение (см. особый).
2. только ед. Оценка, то или иное суждение о ценности чего-нибудь. Быть низкого мнения о чем-нибудь. Быть о себе высокого мнения. Я хорошего мнения о вашем друге.
❖ Общественное мнение — суждение общества о чем-нибудь, отношение общества к чему-нибудь; само общество, как источник этих суждений. Считаться с общественным мнением. Благоприятное общественное мнение. Успокоить общественное мнение. Подготавливать общественное мнение.

----

> В статье предлагается идея, противоречащая утвержденной и
> выверенной практике в отраслях, критичных к безопасности и
> корректности ПО.

> В авиации и энергетике такое в принципе невозможно.

Вы либо заблуждаетесь, либо нас обманываете.

> То есть обосновать вы не можете. Понятно.

Вам обосновать формальную логику? Вы образцово продемонстрировали логическую ошибку.
UFO just landed and posted this here
Давайте просто закончим дискуссию. Договориться о даже базовой терминологии нам с вами не удается. Как можно хоть чем-то оперировать, если вы под этим будете понимать что-то совершенно иное?!
UFO just landed and posted this here
UFO just landed and posted this here

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


То есть, вред конкретный и вещественный, а польза несколько виртуальная…

Это точно не преждевременная оптимизация некритичных фрагментов кода?

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


Оптимизировать раньше срока, конечно плохо. Но надо писать optimization-friendly код.

В 95% прикладных задач основной затык на I/O (ожидание выполнения запроса на СУБД, ответа какого нибудь http api, чтение/запись файлов и т.д.), а уж никак не там где вы пишете. Это важно на вычислительных задачах, сложных алгоритмах, в системном ПО.
В 95% прикладных задач основной затык на I/O (ожидание выполнения запроса на СУБД, ответа какого нибудь http api, чтение/запись файлов и т.д.), а уж никак не там где вы пишете. Это важно на вычислительных задачах, сложных алгоритмах, в системном ПО.

Так это же религиозная мантра. Мой опыт говорит наоборот – если пишешь оптимальный код, то программы получаются быстрые. Если всегда думаешь "преждевременная оптимизация зло!", то получаются медленные монстры, которые только убить можно, но никак не исправить. :)


Вот статья, человек просто сделал код на 20000% (sic!) быстрее, просто думая о оптимальности кода, а не написанием оптимального код:


https://medium.com/@okaleniuk/premature-optimization-is-the-root-of-all-evil-is-the-root-of-evil-a8ab8056c6b

От задач зависит все же. Например в моей области примерно 80-90% времени выполнения по данным профилировщика — как раз работа с базой (это при том что все естественно стараются запросы и их количество оптимизировать). Ну, конечно если совсем очевидных ляпов не допускать. Недавно мобильное приложение делал — тоже основной проблемой было оптимизировать загрузку по сети больших объемов данных.
От задач зависит все же.

А кто спорит? Но если человек сознательно начинает писать суб-оптимальный код, то он его будет писать всегда. Те же запросы к БД можно и нужно оптимизировать. И скорость реально повышается в десятки раз. Я писал форум на ассемблере и SQLite. Все говорят что SQLite медленная. Но нет, оказывается что SQLite очень даже быстрая, просто готовит запросы правильно надо. И теперь у меня есть самый быстрый форум в мире, на "самой медленной" БД.

Ага, только вы писали его месяц, хотя там работы на пару дней)
А поскольку ожидание ввода-вывода от языка не зависит, то на PHP тот же функционал будет работать ну может на несколько процентов медленнее.
Ага, только вы писали его месяц, хотя там работы на пару дней

Ну, ну, а разработчики например myBB, SMF или Flarum тоже так считают?

Дак там и функционала побольше
Да, читал статью вашу, очень круто конечно, но для реальных задач бизнеса имеет мало смысла, имхо. Но например в моем случае вообще весь код процентов на 50 из запросов состоит. И запросы на 1к+ строк из за сложности бизнес логики, особенно в отчетах, но не только, вполне нормальное явление. И поверьте, за ту четверть секунды что выполняется такой запрос — выхода отработают даже на том языке что я пишу десятки тысяч раз. Понятно что не стоит пихать экспоненциальную сложность алгоритмов там где можно реализовать что то с линейной или другой низкой сложностью, но экономить на тактах в таких системах не приходится, все же гораздо важнее читабельность.
«Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живёте.»
Автора знаете?
Писатель-классик Стив Макконнелл
image
Точно. Буквально год-полтора назад на Хабре была статья, что лучше писать if-elif-else, чем if-if-if, именно по причине, что во втором случае все условия будут проверется всегда.
В данном случае оба подхода идентичны, так как после любого верного if идет возврат из функции (то, что после уже не проверяется)
> Правильные данные случаются намного чаще, чем неправильные.

А не наоборот?

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

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

Он нужен, прежде всего, во время чтения кода.

Да нет там пенальти по производительности. Проверку все равно делать, условный переход все равно делать. Зато компилятору удобнее оптимизировать при инлайнинге (если он видит, что в конкретном месте условия заведомо ложны, он может их спокойно выкинуть)
Не факт. Представим себе, что функция вызывается в цикле, тогда проверку можно было бы вынести из цикла, но компилятор не сможет об этом догадаться кроме самых тривиальных случаев.
Вот вот, проверку все равно делать, из за криворукости программистов, так? Ведь если параметры не проверять, то такая функция приведет к чудесам. Но вот скажем ваша функция вызывается миллион раз и лишь в одном случае я передал не правильные параметры. Получается я потратил ресурсы процессора просто так 999,999 раз и только чтоб отловить и залогировать всего одну ошибку. Я это к общей концепции современного программирования.
Ну, можете не делать никаких проверок. Только потом может прийти мальчиш-плохиш и взломать вашу программу.
На самом деле, в языках с контрактами, а также хороших оптимизирующих компиляторах не надо делать 999999 проверок; они способны проанализировать вызывающую функцию и избыточные проверки отбросить.
Однако, это не спасает от необходимости проверять пользовательский ввод, в том числе с диска или по сети.
Получается я потратил ресурсы процессора просто так 999,999 раз и только чтоб отловить и залогировать всего одну ошибку.

А лог был "hack attempt!!!" в системе, которая ворочает деньгами на порядки бОльшими цена миллиардов тактов процессора.

Ещё можно через "… do {… } while (false);" сделать:
// setup
int status = 0;

do {
   // preconditions
   status = doSomething();
   if (status) break;
   status = doSomethingElse();
   if (status) break;

   // computation
   status = doWhatYouWantedToInTheFirstPlace();
} while (false);

// cleanup
return status;
Можно, но когда Вы увидите такой код в чужом проекте повсеместно, Вы вряд ли сильно обрадуетесь.
Тут как с музыкой.
Кто-то может играть на гитаре только по написанным аккордам и написанному бою (типа вверх-вниз-глушим).
А кто-то может сам понять на слух, какой аккорд взять и как бить по струнам в конкретной песне.
Писать в таком стиле удобно. А вот при отладке искать потом, где произошёл возврат — трудно. так что подход спорный.
Не понятно чем сложнее найти возврат из 2-го Return по сравнению с присвоением в третьем else. Уж точно меньше кода надо перелопатить.

Только такой подход надо сочетать с принципом RAII.
А то со всеми этими return'ами можно ресурсы растерять.

Не во всех языках есть концепции, близкие к RAII, хотя где-то есть поддержка на уровне синтаксического сахара.
Не во всех.
Но принцип «раннего возврата» — более-менее универсальный.
Потому там где есть RAII — нелишним будет ранний возврат сочетать с RAII.
А то будут всякие утечки памяти, незакрытые файлы и навечно заблокированные мьютексы.
Да и код самой функции с «ранним возвратом» будет более стройным.
А то по мере углубления в функцию — возвраты становятся всё более хлопотными:
на первом if — просто выход (ничего ещё не делали);
на втором if — перед выходом надо файл закрыть, который открыли после первого if;
на третьем if — перед выходом надо и файл закрыть и память освободить;
на четвертом if — всё то же самое, плюс не забыть освободить мьютекс
И т.д.

Можно конечно возразить, мол надо сначала проверить все условия, а потом делать работу, в т.ч. выделять и захватывать ресурсы.
Но, во-первых, не всегда это возможно. От алгоритма зависит.
А во-вторых, принцип «раннего возврата» — более универсален, чем просто «проверка условий перед тем как засучить рукава». Необходимость возврата может возникнуть уже «в глубине» функции, а не в «проверочном заголовке». И легко можно что-то забыть освободить/закрыть.
Если акцент на то, что если язык поддерживает RAII, то он очень полезен в связке с ранним возвратом — то я полностью согласен.
Но надо только понимать, когда сначала стоит проверить проверить предусловия, а потом уже выделять ресурсы и отдавать их под контроль Раи. Перемешивать проверку и выделение ресурсов может быть весьма неэффективным.

Вот, наверное, да, хороший принцип — ранний возврат в блоке проверки предусловий, а дальше уже вложенные проверки или try/catch/finally, когд аначинается реальная работа.

Это очень ограничивает применимость принципа «раннего возврата».
Т.е. мы «делаем красиво», только в небольшой части функции — в начале.
А дальше — всё та же «лестница» из ифов-элсов.
Полумеры.

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

А почему применение "раннего возврата" в любом месте функции (не только в "заголовке") — крайность?
При условии автоуправления ресурсами, конечно.

И при условии, что достаточно одного return, без, например, логирования, отката транзакций и т. п. В общем, при условии, что можно написать if (condition) return SOME_CONST или близкое по простоте выражение

Очень много сталкивался с таким кодом, как у студентов, так и у опытных программистов, где имеется вложеный в три-пять условий возврат или положительное выполнение, при этом вся функция представляет собой одно условие. Всегда предпочитаю такое переписывать и проверять на негативные варианты сначала. Во-первых таким образом уменьшается количество возможных ветвей исполнения. Во-вторых код намного удобнее читать и модифицировать. Стараюсь по возможности своих студентов учить такому же
Да вобщем-то довольно легко до него додуматься, даже без книг. Вобщем не удивительно что вещь известная и давняя
Вот ведь, а нам еще 15 лет назад руки за такое отрывали. Код возврата в отдельную переменную и все ветвления просто проверяют ее значение и не заходят. А return — только один. Хотя, надо признать, в некоторых случаях, когда знаешь, что функция будет вызываться очень часто наиболее вероятную входную комбинацию сразу отрезал. Последнее помогало производительности.
Sign up to leave a comment.

Articles