Как вы справляетесь с огромными if-условиями?

Использование join (в случае связей это приведет к более чем одной строке в группе):

import pyspark.sql.functions as F
from pyspark.sql.functions import count, col 

cnts = df.groupBy("id_sa", "id_sb").agg(count("*").alias("cnt")).alias("cnts")
maxs = cnts.groupBy("id_sa").agg(F.max("cnt").alias("mx")).alias("maxs")

cnts.join(maxs, 
  (col("cnt") == col("mx")) & (col("cnts.id_sa") == col("maxs.id_sa"))
).select(col("cnts.id_sa"), col("cnts.id_sb"))

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

from pyspark.sql.functions import row_number
from pyspark.sql.window import Window

w = Window().partitionBy("id_sa").orderBy(col("cnt").desc())

(cnts
  .withColumn("rn", row_number().over(w))
  .where(col("rn") == 1)
  .select("id_sa", "id_sb"))

Использование порядка struct:

from pyspark.sql.functions import struct

(cnts
  .groupBy("id_sa")
  .agg(F.max(struct(col("cnt"), col("id_sb"))).alias("max"))
  .select(col("id_sa"), col("max.id_sb")))

См. также Как выбрать первую строку каждой группы?

29
задан BalusC 10 May 2010 в 15:50
поделиться

21 ответ

Разделите условие на несколько логических значений, а затем используйте мастер-логическое значение в качестве условия.

bool isOpaque = object.Alpha == 1.0f;
bool isDrawable = object.CanDraw && object.Layer == currentLayer;
bool isHidden = hideList.Find(object);

bool isVisible = isOpaque && isDrawable && ! isHidden;

if(isVisible)
{
    // ...
}

Еще лучше:

public bool IsVisible {
    get
    {
        bool isOpaque = object.Alpha == 1.0f;
        bool isDrawable = object.CanDraw && object.Layer == currentLayer;
        bool isHidden = hideList.Find(object);

        return isOpaque && isDrawable && ! isHidden;
    }
}

void Draw()
{
     if(IsVisible)
     {
         // ...
     }
}

Удостоверьтесь, что вы даете имя вашей переменной, которое фактически указывает на намерение, а не функцию. Это очень поможет разработчику поддерживать ваш код ... это может быть ВЫ!

61
ответ дан Coincoin 10 May 2010 в 15:50
поделиться

Если вы сделаете это:

if (var1 == true) {
    if (var2 == true) {
        if (var3 == true) {
            ...
        }
    }
}

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

-2
ответ дан Ryan Fox 10 May 2010 в 15:50
поделиться

Я видел много людей и редакторов, которые делали отступ в каждом условии в вашем операторе if с одной вкладкой, или сопоставляли его с открытой пареню:

if (var1 == true
    && var2 == true
    && var3 == true
   ) {
    /* do something.. */
}

Я обычно ставил близкую пареню на та же строка, что и в последнем условии:

if (var1 == true
    && var2 == true
    && var3 == true) {
    /* do something.. */
}

Но я не думаю, что это так же чисто.

3
ответ дан pix0r 10 May 2010 в 15:50
поделиться

Совет Стива Макконелла, из Код завершен : используйте многомерную таблицу. Каждая переменная служит индексом для таблицы, а оператор if превращается в поиск в таблице. Например, если (размер == 3 & amp; вес> 70) приводит к решению о записи в таблице [размер] [весовая группа]

2
ответ дан Yuval F 10 May 2010 в 15:50
поделиться
    if (   (condition_A)
        && (condition_B)
        && (condition_C)
        && (condition_D)
        && (condition_E)
        && (condition_F)
       )
    {
       ...
    }

в отличие от

    if (condition_A) {
       if (condition_B) {
          if (condition_C) {
             if (condition_D) {
                if (condition_E) {
                   if (condition_F) {
                      ...
                   }
                }
             }
          }
       }
    }

и

    if (   (   (condition_A)
            && (condition_B)
           )
        || (   (condition_C)
            && (condition_D)
           )
        || (   (condition_E)
            && (condition_F)
           )
       )
    {
       do_this_same_thing();
    }

в отличие от

    if (condition_A && condition_B) {
       do_this_same_thing();
    }
    if (condition_C && (condition_D) {
       do_this_same_thing();
    }
    if (condition_E && condition_F) {
       do_this_same_thing();
    }

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

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

Правила приоритета оператора хитры, например: & Amp; & Amp; имеет более высокий приоритет, чем ||, но | имеет приоритет над & amp; &

Итак, ...

    if (expr_A & expr_B || expr_C | expr_D & expr_E || expr_E && expr_F & expr_G || expr_H {
    }

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

    if (   (  (expr_A)
            & (expr_B)
           )
        || (  (expr_C)
            | (  (expr_D)
               & (expr_E)
              )
           )
        || (   (expr_E)
            && (  (expr_F)
                & (expr_G)
               )
           )
        || (expr_H)
       )
    {
    }

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

0
ответ дан Sean 10 May 2010 в 15:50
поделиться

Я удивлен, что никто еще не получил это. Существует рефакторинг специально для этого типа проблемы:

http://www.refactoring.com/catalog/decomposeConditional.html

12
ответ дан Carl Manaster 10 May 2010 в 15:50
поделиться

McDowell,

Вы правы, что при использовании сингла '& amp;' оператор, который оценивают обе стороны выражения. Однако при использовании & amp; & amp; оператор (по крайней мере, в C #), то первое выражение, возвращающее ложь, является последним вычисленным выражением. Это делает размещение выражения перед оператором FOR таким же хорошим, как и любой другой способ сделать это.

0
ответ дан TimM 10 May 2010 в 15:50
поделиться

Мне нравится разбивать их по уровням, поэтому я отформатирую ваш пример следующим образом:

if (var1 = true
 && var2 = true
 && var2 = true
 && var3 = true
 && var4 = true
 && var5 = true
 && var6 = true){

Это удобно, когда у вас больше вложенности, как это (очевидно, реальные условия будут более интересными чем "= true" для всего):

if ((var1 = true && var2 = true)
 && ((var2 = true && var3 = true)
  && (var4 = true && var5 = true))
 && (var6 = true)){
0
ответ дан Nicholas Trandem 10 May 2010 в 15:50
поделиться

Попробуйте взглянуть на функторы и предикаты. Проект Apache Commons имеет большой набор объектов, позволяющих вам инкапсулировать условную логику в объекты. Пример их использования доступен на O'reilly здесь . Выдержка из примера кода:

import org.apache.commons.collections.ClosureUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.functors.NOPClosure;

Map predicateMap = new HashMap();

predicateMap.put( isHonorRoll, addToHonorRoll );
predicateMap.put( isProblem, flagForAttention );
predicateMap.put( null, ClosureUtils.nopClosure() );

Closure processStudents = 
    ClosureUtils.switchClosure( predicateMap );

CollectionUtils.forAllDo( allStudents, processStudents );

Теперь подробности всех этих предикатов isHonorRoll и замыканий, используемых для их оценки:

import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;

// Anonymous Predicate that decides if a student 
// has made the honor roll.
Predicate isHonorRoll = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return( ( s.getGrade().equals( "A" ) ) ||
            ( s.getGrade().equals( "B" ) && 
              s.getAttendance() == PERFECT ) );
  }
};

// Anonymous Predicate that decides if a student
// has a problem.
Predicate isProblem = new Predicate() {
  public boolean evaluate(Object object) {
    Student s = (Student) object;

    return ( ( s.getGrade().equals( "D" ) || 
               s.getGrade().equals( "F" ) ) ||
             s.getStatus() == SUSPENDED );
  }
};

// Anonymous Closure that adds a student to the 
// honor roll
Closure addToHonorRoll = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Add an award to student record
    s.addAward( "honor roll", 2005 );
    Database.saveStudent( s );
  }
};

// Anonymous Closure flags a student for attention
Closure flagForAttention = new Closure() {
  public void execute(Object object) {
    Student s = (Student) object;

    // Flag student for special attention
    s.addNote( "talk to student", 2005 );
    s.addNote( "meeting with parents", 2005 );
    Database.saveStudent( s );
  }
};
2
ответ дан Brian 10 May 2010 в 15:50
поделиться

В рефлексивных языках, таких как PHP, вы можете использовать переменные-переменные:

$vars = array('var1', 'var2', ... etc.);
foreach ($vars as $v)
    if ($v == true) {
        // do something
        break;
    }
1
ответ дан Milan Babuškov 10 May 2010 в 15:50
поделиться

Мне нравится разбивать каждое условие на описательные переменные.

bool isVar1Valid, isVar2Valid, isVar3Valid, isVar4Valid;
isVar1Valid = ( var1 == 1 )
isVar2Valid = ( var2.Count >= 2 )
isVar3Valid = ( var3 != null )
isVar4Valid = ( var4 != null && var4.IsEmpty() == false )
if ( isVar1Valid && isVar2Valid && isVar3Valid && isVar4Valid ) {
     //do code
}
0
ответ дан wusher 10 May 2010 в 15:50
поделиться

Во-первых, я удалил бы все == true части, что сделало бы его на 50% короче;)

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

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

5
ответ дан abyx 10 May 2010 в 15:50
поделиться

@tweakt

Не лучше, но то, что я делал в прошлом:

boolean ok = cond1; хорошо & = cond2; хорошо & = cond3; хорошо & = cond4; хорошо & = cond5; ok & amp; = cond6;

То же самое, что и:

ok = (cond1 & amp; cond2 & amp; cond3 & amp; cond4 & amp; cond5 & amp; & amp; cond6);

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

Для удобства чтения я лично предпочитаю предложение Майка Стоуна выше. Легко комментировать подробно и сохраняет все вычислительные преимущества возможности раннего запуска. Вы также можете сделать ту же технику встроенной в функцию, если это помешает организации вашего кода отодвинуть условную оценку далеко от вашей другой функции. Это немного глупо, но вы всегда можете сделать что-то вроде:

do {
    if (!cond1)
       break;
    if (!cond2)
       break;
    if (!cond3)
       break;
    ...
    DoSomething();
} while (false);

while (false) отчасти глупо. Мне бы хотелось, чтобы у языков был оператор видимости, называемый «один раз», или что-то такое, из чего вы могли бы легко выйти.

0
ответ дан fastcall 10 May 2010 в 15:50
поделиться

Если бы я делал это в Perl, я мог бы выполнять проверки.

{
  last unless $var1;
  last unless $var2;
  last unless $var3;
  last unless $var4;
  last unless $var5;
  last unless $var6;

  ... # Place Code Here
}

Если вы планируете использовать это в подпрограмме, замените каждый экземпляр last на return;

0
ответ дан Brad Gilbert 10 May 2010 в 15:50
поделиться

Я буду часто разбивать их на булевы переменные:

bool orderValid = orderDate < DateTime.Now && orderStatus != Status.Canceled;
bool custValid = customerBalance == 0 && customerName != "Mike";
if (orderValid && custValid)
{
...
6
ответ дан Mike Powell 10 May 2010 в 15:50
поделиться

Ну, во-первых, почему бы и нет:

if (var1 & amp; var2 & amp; var2 & amp; var3 & amp; var4 & amp; var4 & amp; var5 & amp; & amp; var6) {
...

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

Это не лучше, но то, что я делал в прошлом: (Следующий метод предотвращает короткое замыкание логического тестирования, все тесты запускаются, даже если первый неверен. Не рекомендуется, если вы не знаете, что нужно всегда выполнить весь код перед возвратом - спасибо ptomato за обнаружение моей ошибки!)

boolean ok = cond1;
ok & = cond2;
ok & amp; = cond3;
ok & = cond4;
ok & = cond5;
ok & = cond6;

Что совпадает с: (не то же самое, см. Примечание выше!)

ok = (cond1 & amp; cond2 & amp; cond3 & amp; cond4 & amp; cond5 & amp; cond6);

2
ответ дан Mark Renouf 10 May 2010 в 15:50
поделиться

Посмотрите Шаблоны реализации Кента Бека. Существует определенная схема, о которой я думаю, которая может помочь в этой ситуации ... она называется «Страж». Вместо того, чтобы иметь кучу условий, вы можете разбить их на охрану, что дает понять, какие условия являются неблагоприятными в методе.

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

public void doSomething() {
    if (condition1 && condition2 && condition3 && condition4) {
        // do something
    }
}

Вы можете изменить его на:

public void doSomething() {
    if (!condition1) {
        return;
    }

    if (!condition2) {
        return;
    }

    if (!condition3) {
        return;
    }

    if (!condition4) {
        return;
    }

    // do something
}

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

Кстати, я НАСТОЯТЕЛЬНО рекомендую эту книгу.

4
ответ дан Mike Stone 10 May 2010 в 15:50
поделиться

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

1
ответ дан Kevin 10 May 2010 в 15:50
поделиться

Я прибегаю к отдельным логическим значениям:

Bool cond1 == (var1 && var2);
Bool cond2 == (var3 && var4);

if ( cond1 && cond2 ) {}
1
ответ дан TimM 10 May 2010 в 15:50
поделиться

Здесь необходимо рассмотреть две проблемы: удобочитаемость и понятность

Решение «читабельность» - это вопрос стиля, и поэтому оно открыто для интерпретации. Я предпочитаю это:

if (var1 == true && // Explanation of the check
    var2 == true && // Explanation of the check
    var3 == true && // Explanation of the check
    var4 == true && // Explanation of the check
    var5 == true && // Explanation of the check
    var6 == true)   // Explanation of the check
    { }

или это:

if (var1 && // Explanation of the check
    var2 && // Explanation of the check
    var3 && // Explanation of the check
    var4 && // Explanation of the check
    var5 && // Explanation of the check
    var6)   // Explanation of the check
    { }

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

/// <Summary>
/// Tests whether all the conditions are appropriately met
/// </Summary>
private bool AreAllConditionsMet (
    bool var1,
    bool var2,
    bool var3,
    bool var4,
    bool var5,
    bool var6)
{
    return (
        var1 && // Explanation of the check
        var2 && // Explanation of the check
        var3 && // Explanation of the check
        var4 && // Explanation of the check
        var5 && // Explanation of the check
        var6);  // Explanation of the check
}

private void SomeMethod()
{
    // Do some stuff (including declare the required variables)
    if (AreAllConditionsMet (var1, var2, var3, var4, var5, var6))
    {
        // Do something
    }
}

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

Это формально известно как шаблон рефакторинга «Разобрать условно». Такие инструменты, как Resharper или Refactor Pro! может упростить выполнение такого рода рефакторинга!

Во всех случаях ключом к читаемому и понятному коду является использование реалистичных имен переменных. Хотя я понимаю, что это надуманный пример, «var1», «var2» и т. Д. не являются приемлемыми именами переменных. У них должно быть имя, которое отражает основную природу данных, которые они представляют.

7
ответ дан Simon Gillbee 10 May 2010 в 15:50
поделиться

Если вы программируете на Python, это подпружиненная функция со встроенной функцией all(), примененной к списку ваших переменных (я просто буду использовать здесь логические литералы):

>>> L = [True, True, True, False, True]
>>> all(L) # True, only if all elements of L are True.
False
>>> any(L) # True, if any elements of L are True.
True

Есть ли соответствующая функция на вашем языке (C #? Java?). Если так, то это, вероятно, самый чистый подход.

0
ответ дан yukondude 10 May 2010 в 15:50
поделиться
Другие вопросы по тегам:

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