Элегантные способы обработки if (if else) else

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

 if(FileExists(file))
{
    contents = OpenFile(file); // <-- prevents inclusion in if
    if(SomeTest(contents))
    {
        DoSomething(contents);
    }
    else
    {
        DefaultAction();
    }
}
else
{
    DefaultAction();
}
  • Есть ли название для такой логики?
  • Я тоже немного OCD?

Я открыт для предложений злого кода, хотя бы ради любопытства ...

149 голосов | спросил 3 revs, 3 users 64%
Benjol
1 Jam1000000amThu, 01 Jan 1970 03:00:00 +030070 1970, 03:00:00

24 ответа


92

Извлеките его для разделения функции (метода) и используйте инструкцию return:

 if(FileExists(file))
{
    contents = OpenFile(file); // <-- prevents inclusion in if
    if(SomeTest(contents))
    {
        DoSomething(contents);
        return;
    }
}

DefaultAction();

Или, может быть, лучше, разделить содержимое и его обработку:

 contents_t get_contents(name_t file)
{
    if(!FileExists(file))
        return null;

    contents = OpenFile(file);
    if(!SomeTest(contents)) // like IsContentsValid
        return null;

    return contents;
}

...

contents = get_contents(file)
contents ? DoSomething(contents) : DefaultAction();

Upd:

Почему бы не исключения, почему OpenFile не выбрасывает исключение IO:
Я думаю, что это действительно общий вопрос, а не вопрос о файле IO. Такие имена, как FileExists, OpenFile, могут вводить в заблуждение, но если их заменить на Foo, Bar и т. Д. было бы понятно, что DefaultAction можно вызывать так часто, как DoSomething, поэтому это может быть случай, исключающий исключение. Pà © ter Török написал об этом в конце своего ответа

Почему в 2-м варианте существует тернарный условный оператор:
Если бы был тэг [C ++], я бы написал инструкцию if с объявлением contents в своей части условия:

 if(contents_t contents = get_contents(file))
    DoSomething(contents);
else
    DefaultAction();

Но для других (C-подобных) языков, if(contents) ...; else ...; точно такой же, как оператор выражения с тернарным условным оператором, но дольше. Поскольку основной частью кода была функция get_contents, я просто использовал более короткую версию (а также опустил contents). Во всяком случае, это выходит за рамки этого вопроса.

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
55

Если язык программирования вы используете (0) бинарные сравнения коротких замыканий (т.е. если не вызывает SomeTest, если FileExists возвращает false) и (1) присваивание возвращает значение (результат OpenFile присваивается contents), а затем это значение передается как аргумент SomeTest), вы можете использовать что-то например, следующее, но вам все же будет рекомендовано прокомментировать код, отмечающий, что одиночный = является намеренным.

 if( FileExists(file) && SomeTest(contents = OpenFile(file)) )
{
    DoSomething(contents);
}
else
{
    DefaultAction();
}

В зависимости от того, насколько запутанным является if, может быть лучше иметь переменную флага (которая в этом случае отделяет тестирование условий успеха /отказа с кодом, который обрабатывает ошибку DefaultAction)

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
25

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

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

 if(FileExists(file))
{
    if(! OnNetworkDisk(file))
    {
        contents = OpenFile(file); // <-- prevents inclusion in if
        if(SomeTest(contents))
        {
            DoSomething(contents);
        }
        else
        {
            DefaultAction();
        }
    }
    else
    {
        DefaultAction();
    }
}
else
{
    DefaultAction();
}

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

 if(FileExists(file))
{
    if(LessThan2Gb(file))
    {
        if(! OnNetworkDisk(file))
        {
            contents = OpenFile(file); // <-- prevents inclusion in if
            if(SomeTest(contents))
            {
                DoSomething(contents);
            }
            else
            {
                DefaultAction();
            }
        }
        else
        {
            DefaultAction();
        }
    else
    {
        DefaultAction();
    }
}
else
{
    DefaultAction();
}

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

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

 if(! LessThan2Gb(file))
    return null;

if(OnNetworkDisk(file))
    return null;

(или goto notexists; вместо return null;)), не влияет на какой-либо другой код, чем те, которые добавлены . Например. ортогональны.

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

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
23

Очевидно:

 Whatever(Arguments)
{
    if(!FileExists(file))
        goto notexists;
    contents = OpenFile(file); // <-- prevents inclusion in if
    if(!SomeTest(contents))
        goto notexists;
    DoSomething(contents);
    return;
notexists:
    DefaultAction();
}

Вы сказали, что открыты даже для злых решений, поэтому, используя злые goto, нет?

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

Если у вас есть исключения, их будет легче читать, особенно если вы можете заставить OpenFile и DoSomething просто исключать исключение, если условия не выполняются, поэтому вам не нужны явные проверки вообще. С другой стороны, в C ++ Java и C # бросают исключение - это медленная операция, поэтому с точки зрения производительности goto по-прежнему предпочтительнее.


Примечание о «зле»: C ++ FAQ 6.15 определяет «зло» как:

  

Это означает такой и такой - это то, что вам следует избегать most времени, но не то, что вам следует избегать all времени. Например, вы в конечном итоге будете использовать эти «злые» вещи, когда они будут «наименее злыми из злых альтернатив».

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

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
13
 function FileContentsExists(file) {
    return FileExists(file) ? OpenFile(file) : null;
}

...

 contents = FileContentExists(file);
if(contents && SomeTest(contents))
{
    DoSomething(contents);
}
else
{
    DefaultAction();
}
ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
12

Одна возможность:

 boolean handled = false;

if(FileExists(file))
{
    contents = OpenFile(file); // <-- prevents inclusion in if
    if(SomeTest(contents))
    {
        DoSomething(contents);
        handled = true;
    }
}
if (!handled)
{
    DefaultAction();
}

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

В другом подходе использовались исключения, например:

 try
{
    contents = OpenFile(file); // throws IO exception if file not found
    DoSomething(contents); // calls SomeTest() and throws exception on failure
}
catch(Exception e)
{
    DefaultAction();
    // and the exception should be at least logged...
}

Это выглядит проще, однако оно применимо только в том случае, если

  • мы точно знаем, какие исключения ожидать, а DefaultAction() подходит для каждого
  • мы ожидаем, что обработка файла будет успешной, а отсутствующий файл или неудачный SomeTest() явно ошибочное условие, поэтому для него целесообразно исключить исключение.
ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
11

Это на более высоком уровне абстракции:

 if (WeCanDoSomething(file))
{
   DoSomething(contents);
}
else
{
   DefaultAction();
} 

И это заполняет детали.

 boolean WeCanDoSomething(file)
{
    if FileExists(file)
    {
        contents = OpenFile(file);
        return (SomeTest(contents));
    }
    else
    {
        return FALSE;
    }
}
ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
11
  

Функции должны делать одно. Они должны делать это хорошо. Они должны делать это только.   Роберт Мартин в Чистый код

Некоторые люди находят этот подход немного экстремальным, но он также очень чист. Позвольте мне проиллюстрировать на Python:

 def processFile(self):
    if self.fileMeetsTest():
        self.doSomething()
    else:
        self.defaultAction()

def fileMeetsTest(self):
    return os.path.exists(self.path) and self.contentsTest()

def contentsTest(self):
    with open(self.path) as file:
        line = file.readline()
        return self.firstLineTest(line)

Когда он говорит, что функции должны делать одно, он означает one . processFile() выбирает действие, основанное на результатах теста, и это все, что он делает. fileMeetsTest() объединяет все условия теста, и это все, что он делает. contentsTest() переносит первую строку на firstLineTest(), и это все, что она делает.

Кажется, что много функций, но он читается практически как прямой английский:

  

Чтобы обработать файл, проверьте, соответствует ли он тесту. Если да, то сделайте что-нибудь. В противном случае выполните действие по умолчанию. Файл соответствует тесту, если он существует, и проходит тест содержимого. Чтобы проверить содержимое, откройте файл и проверьте первую строку. Тест для первой строки ...

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

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
6

Относительно того, что это называется, он может легко превратиться в антикварный указатель стрелки , поскольку ваш код растет для обработки большего количества требований (как показано в ответе, приведенном в https://softwareengineering.stackexchange.com/a/122625/33922 ), а затем попадает в ловушку наличия огромных секций кодов с вложенными условными которые напоминают стрелку.

См. такие ссылки, как;

http://codinghorror.com/blog/2006/01 /flattening-arrow-code.html

http://lostechies.com/chrismissal/2009/05/27/anti-patterns-and-worst-practices-the-arrowhead-anti-pattern/

В этом и других анти-шаблонах можно найти много других файлов.

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

  

1) Замените условия защитными предложениями.

     

2) Разложите условные блоки на отдельные функции.

     

3) Преобразование отрицательных проверок в положительные проверки

     

4) Всегда оппортунистически возвращайтесь как можно скорее из функции.

См. некоторые комментарии к блогу Джеффа относительно предложений Стива Макконнелла по ранним возвращениям;

  

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

     

...

     

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

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

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
5

Это соответствует правилам DRY, no-goto и no-multiple-returns, масштабируемым и читаемым, на мой взгляд:

 success = FileExists(file);
if (success)
{
    contents = OpenFile(file);
    success = SomeTest(contents);
}
if (success)
{
    DoSomething(contents);
}
else
{
    DefaultAction();
}
ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
3

Я извлечу его по отдельному методу, а затем:

 if(!FileExists(file))
{
    DefaultAction();
    return;
}

contents = OpenFile(file);
if(!SomeTest(contents))
{
    DefaultAction();
    return;
}

DoSomething(contents);

, что также позволяет

 if(!FileExists(file))
{
    DefaultAction();
    return Result.FileNotFound;
}

contents = OpenFile(file);
if(!SomeTest(contents))
{
    DefaultAction();
    return Result.TestFailed;
}

DoSomething(contents);
return Result.Success;            

, возможно, вы могли бы удалить вызовы DefaultAction и оставить выполнение DefaultAction для вызывающего:

 Result OurMethod(file)
{
    if(!FileExists(file))
    {
        return Result.FileNotFound;
    }

    contents = OpenFile(file);
    if(!SomeTest(contents))
    {
        return Result.TestFailed;
    }

    DoSomething(contents);
    return Result.Success;            
}

void Caller()
{
    // something, something...

    var result = OurMethod(file);
    // if (result == Result.FileNotFound || result == Result.TestFailed), or just
    if (result != Result.Success)        
    {
        DefaultAction();
    }
}

Мне нравится подход Жанны Пиндар .

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
3

В этом конкретном случае ответ достаточно прост ...

Существует условие гонки между FileExists и OpenFile: что произойдет, если файл будет удален?

Единственный разумный способ справиться с этим конкретным случаем - пропустить FileExists:

 contents = OpenFile(file);
if (!contents) // open failed
    DefaultAction();
else (SomeTest(contents))
    DoSomething(contents);

Это аккуратно решает эту проблему и делает код чистым.

В целом: Попробуйте переосмыслить проблему и придумайте другое решение, которое полностью устранит проблему.

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
2

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

Таким образом, ваш пример может стать:

 void DoABunchOfStuff()
{
    if(FileExists(file))
    {
        DoSomethingWithFileContent(file);
        return;
    }

    DefaultAction();
}

void DoSomethingWithFileContent(file)
{        
    var contents = GetFileContents(file)

    if(SomeTest(contents))
    {
        DoSomething(contents);
        return;
    }

    DefaultAction();
}

AReturnType GetFileContents(file)
{
    return OpenFile(file);
}

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

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
2

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

 file = OpenFile(path);
if(isValidFileHandle(file) && SomeTest(file)) {
    DoSomething(file);
} else {
    DefaultAction();
}

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

 OpenFileIfSomething(path:String) : FileHandle {
    file = OpenFile(path);
    if (file && SomeTest(file)) {
        return file;
    }
    return null;
}

...

if ((file = OpenFileIfSomething(path))) {
    DoSomething(file);
} else {
    DefaultAction();
}
ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
2

Я согласен с frozenkoi, однако, для C # в любом случае, я думал, что это поможет следовать синтаксису методов TryParse.

 if(FileExists(file) && TryOpenFile(file, out contents))
    DoSomething(contents);
else
    DefaultAction();
 bool TryOpenFile(object file, out object contents)
{
    try{
        contents = OpenFile(file);
    }
    catch{
        //something bad happened, computer probably exploded
        return false;
    }
    return true;
}
ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
1

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

 if (!ProcessFile(file)) { 
  DefaultAction(); 
}

Программисты Perl и Ruby пишут processFile(file) || defaultAction()

Теперь переходим к записи ProcessFile:

 if (FileExists(file)) { 
  contents = OpenFile(file);
  if (SomeTest(contents)) {
    processContents(contents);
    return true;
  }
}
return false;
ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
1

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

 interface File<T> {
    function isOK():Bool;
    function getData():T;
}

var appleFile:File<Apple> = appleStorage.get(fileURI);
if (appleFile.isOK())
    eat(file.getData());
else
    cry();

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

 var appleFile = appleStorage.get(fileURI, isEdible);
//isEdible is of type Apple->Bool and will be used internally to answer to the isOK call
if (appleFile.isOK())
    eat(file.getData());
else
    cry();

Хотя это может иметь смысл и так:

 function eat(apple:Apple) {
     if (isEdible(apple)) 
         digest(apple);
     else
         die();
}
var appleFile = appleStorage.get(fileURI);
if (appleFile.isOK())
    eat(appleFile.getData());
else
    cry();

Что лучше? Это зависит от проблемы реального мира , с которой вы сталкиваетесь.
Но вещь, которую нужно убрать, это: вы можете многое сделать с композицией и полиморфизмом.

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
1

Что не так с очевидным

 if(!FileExists(file)) {
    DefaultAction();
    return;
}
contents = OpenFile(file);
if(!SomeTest(contents))
{
    DefaultAction();
    return;
}        
DoSomething(contents);

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

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
0

Я полагаю, что это старый вопрос, но я заметил шаблон, который не упоминался; в основном, задание переменной для последующего определения метода /s, который вы хотите вызвать (вне if ... else ...).

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

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

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

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

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

 bool ActionDone = false;

if (Method_1(object_A)) // Test 1
{
    result_A = Method_2(object_A); // Result 1

    if (Method_3(result_A)) // Test 2
    {
        Method_4(result_A); // Action 1
        ActionDone = true;
    }
}

if (!ActionDone)
{
    Method_5(); // Default Action
}
ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
0

Чтобы уменьшить вложенный IF:

1 /раннее возвращение;

2 /составное выражение (с коротким замыканием)

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

 if( FileExists(file) && SomeTest(contents = OpenFile(file)) )
{
    DoSomething(contents);
    return;
}
DefaultAction();
ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
0

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

 while (1) {
    if (FileExists(file)) {
        contents = OpenFile(file);
        if (SomeTest(contents)) {
           DoSomething(contents);
           break;
        } 
    }
    DefaultAction();
    break;
}

Если вы хотите написать меньше строк или вы ненавидите бесконечные циклы как меня, вы можете изменить тип цикла на «do ... while (0)» и избежать последнего «перерыва».

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
0

Как насчет этого решения:

 content = NULL; //I presume OpenFile returns a pointer 
if(FileExists(file))
    contents = OpenFile(file);
if(content != NULL && SomeTest(contents))
    DoSomething(contents);
else
    DefaultAction();

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

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

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
0

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

 #define DOUBLE_ELSE(CODE) else { CODE } } else { CODE }

Что позволяет писать красивый код следующим образом:

 if(FileExists(file))
{
    contents = OpenFile(file);
    if(SomeTest(contents))
    {
        DoSomething(contents);
    }
    DOUBLE_ELSE(DefaultAction();)

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

ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14
-1

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

 let
  contents = ReadFile(file)
in
  if FileExists(file) && SomeTest(contents) 
    DoSomething(contents)
  else 
    DefaultAction()
ответил Steve Barnes 8 PMpSat, 08 Apr 2017 12:21:14 +030021Saturday 2017, 12:21:14

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132