Иногда if
оператор может быть довольно сложным или длинным, поэтому для удобства чтения лучше извлекать сложные вызовы перед оператором if
.
например, это:
if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall())
{
// do stuff
}
в это
bool b1 = SomeComplicatedFunctionCall();
bool b2 = OtherComplicatedFunctionCall();
if (b1 || b2)
{
//do stuff
}
( при условии , пример не что плохо, это просто для иллюстрации ... представьте себе другие вызовы с несколькими аргументами, и т.д.)
Но с этим извлечением я потерял оценку короткого замыкания (SCE).
- Неужели я каждый раз теряю SCE? Есть ли сценарий, при котором компилятору разрешено «оптимизировать его» и по-прежнему предоставлять SCE?
- Есть ли способы сохранить улучшенную читаемость второго фрагмента без потери SCE?
Ответы:
Одно естественное решение могло бы выглядеть так:
bool b1 = SomeCondition(); bool b2 = b1 || SomeOtherCondition(); bool b3 = b2 || SomeThirdCondition(); // any other condition bool bn = bn_1 || SomeFinalCondition(); if (bn) { // do stuff }
Его преимущества заключаются в простоте понимания, применимости во всех случаях и поведении при коротком замыкании.
Это было моим первоначальным решением: вот хороший образец вызовов методов и тел цикла for:
if (!SomeComplicatedFunctionCall()) return; // or continue if (!SomeOtherComplicatedFunctionCall()) return; // or continue // do stuff
При оценке короткого замыкания получаются те же преимущества производительности, но код выглядит более читабельным.
источник
if
" также является признаком того, что ваша функция или метод слишком велики и должны быть разделены на более мелкие. Это не всегда лучший способ, но очень часто!b2
правильно, и у вас получитсяsomeConditionAndSomeotherConditionIsTrue
, не супер-значимый. Кроме того, я должен хранить кучу переменных в моем мысленном стеке во время этого упражнения (и т. Д., Пока я не перестану работать в этой области). Я бы выбралSJuan76
решение номер 2 или просто поместил все это в функцию.Я стараюсь разбивать условия на несколько строк, например:
if( SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall() ) {
Даже при работе с несколькими операторами (&&) вам просто нужно увеличить отступ в каждой паре скобок. SCE по-прежнему срабатывает - нет необходимости использовать переменные. Написание кода таким образом сделало его более читаемым уже много лет. Более сложный пример:
if( one() ||( two()> 1337 &&( three()== 'foo' || four() ) ) || five()!= 3.1415 ) {
источник
Если у вас длинные цепочки условий и то, что необходимо для предотвращения короткого замыкания, вы можете использовать временные переменные для объединения нескольких условий. Взяв ваш пример, можно было бы сделать, например,
bool b = SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall(); if (b && some_other_expression) { ... }
Если у вас есть компилятор с поддержкой C ++ 11, вы можете использовать лямбда-выражения для объединения выражений в функции, как показано выше:
auto e = []() { return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall(); }; if (e() && some_other_expression) { ... }
источник
1) Да у вас больше нет SCE. В противном случае у вас было бы это
bool b1 = SomeComplicatedFunctionCall(); bool b2 = OtherComplicatedFunctionCall();
работает так или иначе в зависимости от того, будет ли
if
заявление позже. Слишком сложно.2) Это основано на мнении, но для достаточно сложных выражений вы можете:
if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall()) {
Если это слишком сложно, очевидным решением будет создание функции, которая оценивает выражение и вызывает его.
источник
Вы также можете использовать:
bool b = someComplicatedStuff(); b = b || otherComplicatedStuff(); // it has to be: b = b || ...; b |= ...; is bitwise OR and SCE is not working then
и SCE будет работать.
Но это не намного читабельнее, чем например:
if ( someComplicatedStuff() || otherComplicatedStuff() )
источник
b = b || otherComplicatedStuff();
и @SargeBorsch внес изменения, чтобы удалить SCE. Спасибо, что заметили меня об этом изменении @Ant.Я не думаю, что такая оптимизация допустима; особенно
OtherComplicatedFunctionCall()
могут иметь некоторые побочные эффекты.Я предпочитаю преобразовать его в одну функцию или одну переменную с описательным именем; что сохранит как оценку короткого замыкания, так и читаемость:
bool getSomeResult() { return SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall(); } ... if (getSomeResult()) { //do stuff }
И поскольку мы реализуем
getSomeResult()
на основеSomeComplicatedFunctionCall()
иOtherComplicatedFunctionCall()
, мы можем рекурсивно декомпозировать их, если они все еще сложны.источник
Нет, нет, но это применяется по-другому:
if (SomeComplicatedFunctionCall() || OtherComplicatedFunctionCall()) { // do stuff }
Здесь компилятор даже не запустится,
OtherComplicatedFunctionCall()
еслиSomeComplicatedFunctionCall()
вернет true.bool b1 = SomeComplicatedFunctionCall(); bool b2 = OtherComplicatedFunctionCall(); if (b1 || b2) { //do stuff }
Здесь будут работать обе функции , потому что они должны быть сохранены в
b1
иb2
.b1 == true
Тогда Ffb2
не будет оцениваться (SCE). НоOtherComplicatedFunctionCall()
уже пробег.Если
b2
больше нигде не используется, компилятор может быть достаточно умен, чтобы встроить вызов функции внутри if, если функция не имеет наблюдаемых побочных эффектов.Это зависит от. Если вам нужно
OtherComplicatedFunctionCall()
запустить из-за побочных эффектов или снижение производительности функции минимально, вам следует использовать второй подход для удобства чтения. В противном случае придерживайтесь SCE в первом подходе.источник
Другая возможность короткого замыкания и условия в одном месте:
bool (* conditions [])()= {&a, &b, ...}; // list of conditions bool conditionsHold = true; for(int i= 0; i < sizeOf(conditions); i ++){ if (!conditions[i]()){; conditionsHold = false; break; } } //conditionsHold is true if all conditions were met, otherwise false
Вы можете поместить цикл в функцию и позволить функции принимать список условий и выводить логическое значение.
источник
Очень странно: вы говорите о удобочитаемости, когда никто не упоминает использование комментариев в коде:
if (somecomplicated_function() || // let me explain what this function does someother_function()) // this function does something else ...
Вдобавок к этому я всегда предшествовал своим функциям некоторыми комментариями о самой функции, о ее вводе и выводе, а иногда я помещаю пример, как вы можете видеть здесь:
/*---------------------------*/ /*! interpolates between values * @param[in] X_axis : contains X-values * @param[in] Y_axis : contains Y-values * @param[in] value : X-value, input to the interpolation process * @return[out] : the interpolated value * @example : interpolate([2,0],[3,2],2.4) -> 0.8 */ int interpolate(std::vector<int>& X_axis, std::vector<int>& Y_axis, int value)
Очевидно, что форматирование ваших комментариев может зависеть от вашей среды разработки (Visual studio, JavaDoc в Eclipse, ...)
Что касается SCE, я предполагаю, что вы имеете в виду следующее:
bool b1; b1 = somecomplicated_function(); // let me explain what this function does bool b2 = false; if (!b1) { // SCE : if first function call is already true, // no need to spend resources executing second function. b2 = someother_function(); // this function does something else } if (b1 || b2) { ... }
источник
Читаемость необходима, если вы работаете в компании и ваш код будет читать кто-то другой. Если вы пишете программу для себя, решать вам, хотите ли вы пожертвовать производительностью ради понятного кода.
источник