Чи варто використовувати оператори перемикання або довго, якщо… інший ланцюжок?


36

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

Ось який-небудь код, який представляє потік, який я зазвичай пишу ( завдяки 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?


Тьфу. Так, це, безумовно, об'ємніше, але тільки в сім'ї С, оскільки його синтаксис для висловлювань справи такий некрасивий.
Мейсон Уілер

5
Про подібні речі багато обговорювалося в stackoverflow: - stackoverflow.com/questions/449273/… - stackoverflow.com/questions/767821/… - stackoverflow.com/questions/97987/switch-vs-if-else
Євгеній Брікман

1
Вам слід уникати обох, коли це можливо. Набагато краще створити структуру даних і зробити пошук, навіть якщо ціль пошуку - це функція або клас.
Кевін Клайн

перемикання швидше, принаймні в .net. Я не знаю про Java.
Кнерд

Можна переробити на словник випадків і методів і один "якщо"
Павло Єрмалович

Відповіді:


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операторів залишає компілятор з невеликим вибором, але генерувати код так, як ви його написали, тестуючи одне значення за іншим, поки воно не знайде збіг. Що стосується лише трьох реальних випадків, це навряд чи викликає занепокоєння, але при достатній кількості це може / може бути значним.


Цей приклад був лише прикладом BTW. Не знав, хоча компілятор може оптимізувати так
TheLQ

6
@JBRWilkinson. У цьому випадку значення поза межами можливо лише через помилку компілятора, на яку я не хочу витрачати багато часу (помилка в моєму коді, щоб перевірити результат, приблизно так само вірогідна, як і в генеруючому коді). У ситуації, коли значення поза межами викликало серйозне занепокоєння (наприклад, індекс надходив з іншого коду), я би просто перевірив межі спочатку і використовую його як індекс лише після перевірки.
Jerry Coffin

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;
}

Вищеописаний спосіб написання цього типу корпусу комутатора є досить поширеним. Причина, чому ви відчули випадок вимикача, якщо об'ємніше, це те, що ваше тіло було лише однією лінією, а з корпусом перемикача вам також потрібна заява про перерву. Тож корпус вимикача мав удвічі більший розмір корпусу, якщо ще. З більш суттєвим кодом, заява про перерву не додасть великої кількості тілу. Для корпусу одного рядка прийнято писати код у тому ж рядку, що і випадок з регістру.

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


1
Якщо ви поставите комутатор методом і маєте для кожного випадку returnвідповідний рядок, ви можете усунути breakоператори.
Роберт Харві

Ваше рішення також є найшвидшим.
Титу

8

У цьому випадку оператор switch чіткіше відповідає наміру коду: виберіть дію, яку слід здійснити, грунтуючись на одному значенні.

Висловлювання if з іншого боку читати набагато важче - ви повинні подивитися на всі вони, щоб переконатися, що відбувається. Для мене це менше коду (навіть якщо кількість символів може бути трохи більшою), оскільки розумового розбору менше.


8

Я погоджуюсь з Джеррі, що масив рядків є кращим для цього конкретного випадку, але загалом краще використовувати вимикач / випадок, ніж ланцюжок "інших". Це легше читати, а іноді компілятор може зробити кращу роботу з оптимізації цього шляху, але є й інша перевага: це набагато простіше налагодження.

Коли ви натискаєте на цей перемикач, вам потрібно лише один раз перейти, щоб опинитися на правій гілці, замість того, щоб обережно переходити по декілька, якщо заяви один за одним, і, можливо, надто швидко натискати на клавішу, переходити повз неї і щось пропускати і мати для початку.


3

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

switch Висловлювання також чистіші, в них легко приховати помилку друку ==

Також для великих блоків на C перемикання відбувається швидше.

else..ifможе бути більш доречним, коли у вас є щось на зразок діапазонів (між 1 і 100, зробіть це, між 100 і 200 зробіть це) або на C, коли ви намагаєтеся переключити елементи, такі як рядки (що можливо іншими мовами). Який же.

Я, як правило, використовую багато комутаторів, коли програмую в C.


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);
}

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

І це набагато приємніше, ніж якщо / ще, якщо / інше, якщо підхід.


3
Я особисто ненавиджу "покласти це в інший метод, тому що це виглядає некрасиво" спосіб вирішення речей. Список методів захаращеності і IMHO виглядає гірше. Я зробив би це лише в тому випадку, якщо A) код десь дублюється або B) Може бути корисний в іншому місці
TheLQ

1
який список методів? і чому ваш список методів захаращений гірше, ніж ваш код? Я думав, що ми пройшли повз "збережіть все одним методом, щоб ви могли побачити все одразу"
Сара

@TheLQ Я погоджуюся з вами загалом, але в цьому випадку "коментар =" насправді враховується пропозицією Піта.
Тіту

0

У python немає оператора перемикання, тому що якщо / 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 НЕ має комутатора, змушує тих, хто їх ненавидить (як я), думати, що вони не самотні.
Дан Розенстарк

Форматування 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 ifs більш гнучка, не кажучи вже про те, що вона є більш ефективною для набору та читання.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.