Должен ли я использовать операторы switch или длинные цепочки if… else?

36

Часто, когда я слышу об операторе switch, его откладывают как способ заменить длинные цепочки if ... else. Но, похоже, что когда я использую оператор switch, я пишу больше кода, который я бы просто написал, если ... еще. У вас также есть другие проблемы, такие как хранение всех переменных для всех вызовов в одной области .

Вот некоторый код, который представляет поток, который я обычно пишу ( благодаря diam )

String comment;   // The generated insult.
int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

if (which == 0) {
    comment = "You look so much better than usual.";
} else if (which == 1) {
    comment = "Your work is up to its usual standards.";
} else if (which == 2) {
    comment = "You're quite competent for so little experience.";
} else {
    comment = "Oops -- something is wrong with this code.";
}

Затем они хотят, чтобы я заменил это на это:

String comment;   // The generated insult.
int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

switch (which) {
    case 0:  
             comment = "You look so much better than usual.";
    break;
    case 1:  
             comment = "Your work is up to its usual standards.";
    break;
    case 2:  
             comment = "You're quite competent for so little experience.";
    break;
    default: 
             comment = "Oops -- something is wrong with this code.";
}

Похоже, гораздо больше кода в гораздо более неудобном синтаксисе. Но есть ли преимущество в использовании оператора switch?

TheLQ
источник
Тьфу. Да, это, конечно, больше, но только в семействе C, потому что его синтаксис для операторов case очень уродлив.
Мейсон Уилер
5
Подобные вещи много обсуждались на stackoverflow: - stackoverflow.com/questions/449273/… - stackoverflow.com/questions/767821/… - stackoverflow.com/questions/97987/switch-vs-if-else
Евгений Брикман
1
Вы должны избегать обоих, когда это возможно. Гораздо лучше создать структуру данных и выполнить поиск, даже если целью поиска является функция или класс.
Кевин Клайн
Переключатель быстрее, по крайней мере, в .net. Я не знаю о Java.
Knerd
Может быть преобразован в словарь падежей и методов и одно «если»
Павел Ермалович

Ответы:

57

Для этой конкретной ситуации мне кажется, что и то, ifи другое case- плохой выбор. Я бы использовал простой массив:

String comments[] = {
    "You look so much better than usual.",
    "Your work is up to its usual standards.",
    "You're quite competent for so little experience."
};

String comment = comments[(int)(Math.random() * 3)];

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

Что касается , когда вы бы использовать случай / выключатель, отличие от каскада ifутверждений (или , по меньшей мере , одно существенное различие) является то , что switchможет полуавтоматически оптимизировать в зависимости от количества и плотности значений, в то время как каскад ifзаявлений листьев компилятор без особого выбора, кроме как генерировать код в том виде, в каком вы его написали, тестируя одно значение за другим, пока не найдете совпадение. Только с тремя реальными случаями это вряд ли вызывает беспокойство, но при достаточном количестве это может / может быть значительным.

Джерри Гроб
источник
Этот пример был только примером, кстати. Не знал, однако, что компилятор может оптимизировать таким образом
TheLQ 30.10.10
6
@JBRWilkinson. В этом случае значение out of bounds возможно только из-за ошибки компилятора, на которую я не склонен тратить много времени (ошибка в моем коде для проверки результата примерно такая же, как в генерирующем коде). В ситуации, когда значение вне границ представляло реальную проблему (например, индекс получался из другого кода), я сначала просто проверял границы и использовал его в качестве индекса только после проверки.
Джерри Гроб
4
Я думаю, что этот ответ очень специфичен для примера, в то время как вопрос был более общим, чем этот ...
Хелбен
3
@Khelben: мне кажется, ты не удосужился прочитать весь ответ. Последний абзац касается более широких вопросов. Существует проблема , хотя: я нахожу очень мало ситуаций , в которых я считаю либо с caseзаявлением или каскад ifзаявлений подходящих. В большинстве случаев они (посредственные) заменители какого-то типа карт / массивов, и вам лучше использовать один из последних напрямую.
Джерри Гроб
1
@Titou: если компилятор не является полностью мозговой мертвецом, массив будет собираться один раз во время компиляции, и после этого вы просто используете статическую структуру. Например, если вы делали это на C или C ++, вы бы хотели создать этот static constмассив, чтобы он всегда существовал (но в вопросе не было дано ни одного языка, поэтому я старался не использовать ни одного в ответе). ).
Джерри Гроб
23

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

if (a == 1) {
    // stuff
} else if (a == 2) {
    // stuff
} else if (a == 3) {
    // stuff
} else if (b == 1) {
    // stuff
} else if (b == 2) {
    // stuff
}

(очевидно, для такого количества утверждений это не так уж плохо)

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

В конце концов, однако, я бы предпочел ни один switchили цепь if...else if. Часто лучшим решением является какая-то таблица переходов или словарь для случаев, как в исходном вопросе, или полиморфизм (если ваш язык поддерживает это). Конечно, это не всегда возможно, но switchв качестве первого шага я бы искал решение, которое избегает ...

Дин Хардинг
источник
4
недостатком полиморфизма является фрагментация кода, что затрудняет его понимание по сравнению с тем, что происходит в одном месте. Следовательно, вы можете немного поколебаться, прежде чем перейти в это.
Почему таблица переходов / словарь лучше, чем переключатель?
Титу
14
switch (which) {
  case 0: comment = "String 1"; break;
  case 1: comment = "String 2"; break;
  case 2: comment = "String 3"; break;
  default: comment = "Oops"; break;
}

Приведенный выше способ написания этого типа случая переключения довольно распространен. Причина, по которой вы почувствовали регистр переключателя, если он более объемный, заключается в том, что ваше тело было только одной строкой, а в случае переключателя вам также потребовалось выражение break. Таким образом, корпус переключателя имел вдвое больший размер, чем корпус. При более содержательном коде оператор break не сильно прибавит к телу. Для однострочного тела обычной практикой является написание кода в той же строке, что и оператор case.

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

aufather
источник
1
Если вы поместите переключатель в метод и у каждого случая будет returnсоответствующая строка, вы можете исключить breakоператоры.
Роберт Харви
Ваше решение также самое быстрое.
Титу
8

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

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

FinnNk
источник
8

Я согласен с Джерри, что для этого конкретного случая лучше использовать массив строк, но в целом лучше использовать оператор switch / case, чем цепочку elseifs. Его легче читать, и иногда компилятор лучше справляется с оптимизацией, но есть и другое преимущество: его намного проще отлаживать.

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

Мейсон Уилер
источник
3

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

switch Заявления также чище, опечатку легко спрятать во всех этих ==

Кроме того, для больших блоков в C, переключение происходит быстрее.

else..ifможет быть более уместным, когда у вас есть что-то вроде диапазонов (между 1 и 100, делайте это, между 100 и 200, делайте это) или в C, когда вы пытаетесь переключаться с такими элементами, как строки (это возможно в других языках). Что то же самое.

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

Khelben
источник
2

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

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

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

Уолт Стоунбернер
источник
2

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

Я бы лично обернул такой код в отдельный вспомогательный метод.

private string GetInsult()
{
    int which = (int)(Math.random() * 3);  //  Result is 0, 1, or 2.

    switch (which) {
        case 0: return "You look so much better than usual.";
        case 1: return "Your work is up to its usual standards.";
        case 2: return "You're quite competent for so little experience.";
        default: return "Oops -- something is wrong with this code.";
    }
}

public void Foo()
{
    string comment = GetInsult();
    Print(comment);
}

Размещение переключателя в отдельном методе позволяет размещать операторы return непосредственно внутри оператора switch (по крайней мере, в c #), что также устраняет необходимость в операторах break, делая код намного проще для чтения.

И это намного лучше, чем подход if / else if / else if.

Пит
источник
3
Лично я ненавижу «ставить это другим способом, потому что это выглядит некрасиво», чтобы решать проблемы. Переполняет список методов и выглядит хуже ИМХО. Я сделал бы это только в том случае, если A) код где-то дублируется или B) может быть полезен в другом месте
TheLQ
1
какой метод списка? и почему ваш список методов загроможден хуже, чем ваш код загроможден? Я думал, что мы прошли мимо «держите все в одном методе, чтобы вы могли видеть все сразу», возраст
Сара
@TheLQ Я согласен с вами в целом, но в этом самом случае «комментарий =» действительно учитывается предложением Пита.
Титу,
0

В python нет оператора switch, потому что если / elif / else это хорошо:

a = 5

if a==1:
    print "do this"
elif a == 2:
    print "do that"
elif a == 3:
    print "do the other"
elif 3 < a < 9:
    print "do more"
elif 9 <= a < 15:
    print "do nothing"
else:
    print "say sorry"

Просто верно?

Кристофер Махан
источник
Elifэто просто оператор if с несколькими пропущенными буквами. Это определенно больше похоже на ifутверждение, чем на утверждение переключателя. Тот факт, что Питон НЕ имеет переключателя, заставляет того, кто ненавидит их (таких как я), думать, что они не одиноки.
Дэн Розенстарк
Форматирование Python работает в stackoverflow, но не на programmers.stackexchange.com :(
Кристофер Махан,
Вы должны предупредить их, metaесли это не известная тема. Спасибо, что дали мне свидетеля.
Дэн Розенстарк
да, узнал, что у них это происходит на meta.programmers.stackexchange.com/questions/308/…
Кристофер Махан
1
@ Яр, напоминает мне о моих админских днях в Википедии ... О, радость. (я уже совсем не в теме?)
Кристофер Махан
0

Одна из вещей, которая делает стиль C / C # switchособенно раздражающей, это настойчивое требование, чтобы caseзначение было литералом. Одна хорошая вещь в VB / VB.NET - это то, что select/caseкаждый случай может быть любым логическим выражением. Это удобно Поскольку ряд взаимоисключающих логических выражений часто бывает полезен, серия if / else if более гибкая, не говоря уже о том, что она более эффективна для ввода и чтения.

Джоэл Браун
источник