Считается ли плохой формой выполнять функцию в условном выражении? - PullRequest
7 голосов
/ 24 марта 2010

Рассмотрим ситуацию, в которой вам нужно вызывать последовательные подпрограммы и останавливаться, как только возвращается значение, которое можно оценить как положительное (true, object, 1, str (1)).

Очень заманчиво сделать это:

if (fruit = getOrange())
elseif (fruit = getApple())
elseif (fruit = getMango())
else fruit = new Banana();

return fruit;

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

fruit = getOrange();
if(!fruit){
    fruit = getApple();
    if(!fruit){
        fruit = getMango();
        if(!fruit){
            fruit = new Banana();
        }
    }
}

return fruit;

Согласно догме об основных структурах, приемлема ли предыдущая форма? Вы бы порекомендовали это?

Edit:

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

Ответы [ 7 ]

9 голосов
/ 24 марта 2010

Если вам нужен краткий синтаксис, несколько языков позволяют использовать для этой цели «логическое или» (C # явно предоставляет оператор объединения, потому что нули не являются ложными).

Python:

fruit = ( getOrange() or 
          getApple()  or 
          getMango()  or 
          Banana() )

C #:

fruit = getOrange() ?? 
        getApple()  ?? 
        getMango()  ?? 
        new Banana();
4 голосов
/ 24 марта 2010

Я могу придумать две альтернативы.

Первое допустимо только в таких языках, как ваш (PHP?), Где в условном выражении допускается единственное = *. 1003 *

    if ( (fruit = getOrange()) != null)
    elseif ( (fruit = getApple()) != null)
    elseif ( (fruit = getMango()) != null)
    else fruit = new Banana();

Проясняет, что вы проводите сравнение и что одиночный = не является ошибкой.

    fruit = getOrange();
    if(!fruit) fruit = getApple();
    if(!fruit) fruit = getMango();
    if(!fruit) fruit = new Banana();

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

3 голосов
/ 24 марта 2010

В строго типизированном языке, который не приравнивает 0 / нуль к ложному и не-0 / не ноль к истинному, я бы сказал, что это, вероятно, безопасно, но незначительно менее читабельно в общем случае, где ваш метод имена и количество параметров могут быть больше. Я бы лично избегал этого, за исключением некоторых стандартных идиом, в случаях, когда 0 / null равняется false, а не-0 / non-null - true просто из-за потенциальной опасности спутать присваивание с проверкой равенства при чтении кода. Некоторые идиомы в слабо типизированных языках, такие как C, настолько распространены, что избегать их смысла нет, например,

 while ((line = getline()) != null) {
   ...
 }
1 голос
/ 24 марта 2010

В C или C ++ вы можете написать:

return (fruit = getOrange()) ? fruit :
       (fruit = getApple())  ? fruit :
       (fruit = getMango())  ? fruit :
        new Banana();

Причина, по которой следует избегать как этой, так и вашей первой версии, заключается не в «догмате о базовых структурах», а в том, что само по себе назначение в условии сбивает с толку. Не все языки поддерживают это, с одной стороны. С другой стороны, это просто неверно истолковано как ==, или читатель может быть не уверен, действительно ли вы это имели в виду, или, возможно, намеревались ==. Добавление != 0 к каждому условию становится довольно плотным и многословным.

GCC имеет расширение, позволяющее:

return getOrange() ? : getApple() ? : getMango() ? : new Banana();

Этого часто можно достичь с помощью || или or (но не в C или C ++).

Другая возможность:

do {
    fruit = getOrange();
    if (fruit) break;
    fruit = getApple();
    if (fruit) break;
    fruit = getMango();
    if (fruit) break;
    fruit = new Banana();
} while (false);

Это даже лучше в языке, где вы можете break из базового блока, который вы можете использовать с last в Perl, поскольку вы можете обойтись без do / while(false). Но, вероятно, это понравится только программистам на ассемблере.

1 голос
/ 24 марта 2010

Проблема, на мой взгляд, не в структуре, а в правилах вождения. Почему getOrange предшествует getApple и т. Д.

Скорее всего, вы увидите нечто более управляемое данными:

enum FruitEnum
{
  Orange, Apple, Mango, Banana
}

и отдельно

List<FruitEnum> orderedFruit = getOrderedFruit();
int i = 0;
FruitObj selectedFruit;
while(selectedFruit == null && i <= orderedFruit.Count)
{
    fruit = FruitFactory.Get(orderedFruit[i++]);
}
if(fruit == null)
{
    throw new FruitNotFoundException();
}

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

fruit = getOrange() ?? getApple() ?? getMango() ?? new Banana();
0 голосов
/ 24 марта 2010

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

0 голосов
/ 24 марта 2010

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

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

let constructors = [getOrange; getApple; getMango; fun () -> new Banana()]
foreach constructor in constructors
    let fruit = constructor()
    if fruit != null
        return fruit

Это похоже на оператор с нулевым слиянием, но более обобщенно. В C # вы, вероятно, будете использовать linq следующим образом:

 var fruit = constructors
     .Select(constructor => constructor())
     .Filter(x => x != null)
     .First();

По крайней мере, так вы можете передавать ваши конструкторы как фабричный класс, вместо того, чтобы жестко кодировать их с помощью оператора null-coalesce.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...