Можно ли использовать switch в switch

Как заместить методом класса схему switch-in-switch?

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

4.115.*В некоторых странах Дальнего Востока (Китае, Японии и др.) использовался
(и неофициально используется в настоящее время) календарь, отличающийся
от применяемого нами. Этот календарь представляет собой 60-летнюю циклическую систему.

Составить программу, которая по заданному номеру года нашей эры n печатает его название по описанному календарю в виде: «Крыса, Зеленый».

Мое решение:

import java.util.Scanner;

class TaskCh04N115 {
public static void main(String[] args) {
Scanner str = new Scanner(System.in);
System.out.println(«Год: «);
int year = str.nextInt();
int year_east = (year % 60) — 4;
int element = (year % 60) / 12;
if (year_east < 0) {
year_east += 12;
}
else if (year_east > 11) {
year_east = year_east % 12;
}
switch (year_east) {
case 0:
System.out.println(«rat «);
switch (element) {
case 0:
System.out.println(«tree «);
break;
case 1:
System.out.println(«fire «);
break;
case 2:
System.out.println(«earth «);
break;
case 3:
System.out.println(«metall «);
break;
case 4:
System.out.println(«water «);
break;
}
break;
case 1:
System.out.println(«bull «);
switch (element) {
case 0:
System.out.println(«tree «);
break;
case 1:
System.out.println(«fire «);
break;
case 2:
System.out.println(«earth «);
break;
case 3:
System.out.println(«metall «);
break;
case 4:
System.out.println(«water «);
break;
}
break;
case 2:
System.out.println(«tiger «);
switch (element) {
case 0:
System.out.println(«tree «);
break;
case 1:
System.out.println(«fire «);
break;
case 2:
System.out.println(«earth «);
break;
case 3:
System.out.println(«metall «);
break;
case 4:
System.out.println(«water «);
break;
}
break;
case 3:
System.out.println(«rabbit «);
switch (element) {
case 0:
System.out.println(«tree «);
break;
case 1:
System.out.println(«fire «);
break;
case 2:
System.out.println(«earth «);
break;
case 3:
System.out.println(«metall «);
break;
case 4:
System.out.println(«water «);
break;
}
break;
case 4:
System.out.println(«dragon «);
switch (element) {
case 0:
System.out.println(«tree «);
break;
case 1:
System.out.println(«fire «);
break;
case 2:
System.out.println(«earth «);
break;
case 3:
System.out.println(«metall «);
break;
case 4:
System.out.println(«water «);
break;
}
break;
case 5:
System.out.println(«snake «);
switch (element) {
case 0:
System.out.println(«tree «);
break;
case 1:
System.out.println(«fire «);
break;
case 2:
System.out.println(«earth «);
break;
case 3:
System.out.println(«metall «);
break;
case 4:
System.out.println(«water «);
break;
}
break;
case 6:
System.out.println(«horse «);
switch (element) {
case 0:
System.out.println(«tree «);
break;
case 1:
System.out.println(«fire «);
break;
case 2:
System.out.println(«earth «);
break;
case 3:
System.out.println(«metall «);
break;
case 4:
System.out.println(«water «);
break;
}
break;
case 7:
System.out.println(«sheep «);
switch (element) {
case 0:
System.out.println(«tree «);
break;
case 1:
System.out.println(«fire «);
break;
case 2:
System.out.println(«earth «);
break;
case 3:
System.out.println(«metall «);
break;
case 4:
System.out.println(«water «);
break;
}
break;
case 8:
System.out.println(«monkey «);
switch (element) {
case 0:
System.out.println(«tree «);
break;
case 1:
System.out.println(«fire «);
break;
case 2:
System.out.println(«earth «);
break;
case 3:
System.out.println(«metall «);
break;
case 4:
System.out.println(«water «);
break;
}
break;
case 9:
System.out.println(«cock «);
switch (element) {
case 0:
System.out.println(«tree «);
break;
case 1:
System.out.println(«fire «);
break;
case 2:
System.out.println(«earth «);
break;
case 3:
System.out.println(«metall «);
break;
case 4:
System.out.println(«water «);
break;
}
break;
case 10:
System.out.println(«dog «);
switch (element) {
case 0:
System.out.println(«tree «);
break;
case 1:
System.out.println(«fire «);
break;
case 2:
System.out.println(«earth «);
break;
case 3:
System.out.println(«metall «);
break;
case 4:
System.out.println(«water «);
break;
}
break;
case 11:
System.out.println(«pig «);
switch (element) {
case 0:
System.out.println(«tree «);
break;
case 1:
System.out.println(«fire «);
break;
case 2:
System.out.println(«earth «);
break;
case 3:
System.out.println(«metall «);
break;
case 4:
System.out.println(«water «);
break;
}
break;
}
}
}

Код неудобочитаем, поэтому:

Вопросы:

  1. Как заместить внутренний switch-case методом какого-либо класса, либо как заместить методами класса обе switch-case конструкции?
  2. Как вывести выводимые слова в ОДНУ строку, в формате «Крыса, Зеленый»? Сейчас это два слова в двух строках.

Источник

C Урок 10. Оператор switch |

&nbsp

&nbsp

&nbsp

Очень часто при составлении исходного кода будущей программы мы сталкиваемся с ситуацией, когда дальнейший ход нашей программы зависит от конкретного значения какой-то переменной либо выражения, причём когда выбор таких значений, мягко говоря, немаленький. Мы, конечно же в данном случае можем воспользоваться условными конструкциями if, else, else if, но в данном случае это будет не совсем удобно и читабельно. Для этой ситуации существует такая конструкция, чтобы было очевидно, что мы выбираем какие-то конкретные варианты значений одной переменной или одного выражения.

И таким оператором и служит switch, который также можно отнести к оператору ветвления. Также данный оператор очень часто называют оператором вариантов. Ещё в народе его зовут переключателем в соответствии с его переводом на русский.

switch – оператор, который сравнивает последовательно значение переменной, находящейся в скобках со всеми вариантами значений, находящимися после каждого ключевого слова case. При совпадении данных значений выполняется код, следующий за case. Затем, если встретится ключевое слово break либо закрывающая фигурная скобка, произойдет выход из конструкции switch. Если со значением переменной не совпадет ни одно из значений, выполнится код после необязательной инструкции default

Читайте также:  Если есть в собственности земля можно ли использовать материнский капитал

Можно ли использовать switch в switch

Оператор break в ветвях case является необязательным. Если его не будет в какой-то из ветвей, то выхода из тела оператора не произойдёт и код продолжит выполнение дальше внутри тела. Причём, если дальше встретится следующая ветвь case, то её код выполнится независимо от сравниваемого значения. Приём без break используется как правило для того, чтобы выполнить ту или иную ветвь в зависимости не от одного, а от нескольких вариантов значения переменной (выражения), находящейся(гося) в скобках. Выглядеть такая ветвь будет примерно вот так

Можно ли использовать switch в switch

или вот так

Можно ли использовать switch в switch

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

Также нелишним будет заметить что ветви case и default можно располагать в любом порядке, хотя мы привыкли, что ветвь default, которая, кстати быть должна только одна, всегда в конструкции switch расположена в самом низу тела.

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

И давайте теперь перейдём к нашей практической части. Я постараюсь дать такой простой пример, который будет понятен каждому. Пусть он не пригодится в будущем, но сейчас самое важное для нас то, чтобы мы уяснили, как именно работает конструкция switch.

Создадим новый проект из проекта прошлого занятия с именем MYPROG09 и присвоим ему имя MYPROG10.

Откроем файл main.c и в функции main(), как обычно, удалим весь код тела кроме возврата нуля, останется от него вот это

int main()

{

  return 0; //Return an integer from a function

}

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

int main()

{

  int n = 0;

  while(1)

  {

    printf(«Enter an integer of 10, 20, 30, 40, or 50rn»);

    scanf(«%d», &n);

    switch(n)

    {

      case 10:

        printf(«You entered number 10.rn»);

        break;

      case 20:

        printf(«You entered number 20.rn»);

        break;

      case 30:

        printf(«You entered number 30.rn»);

        break;

      case 40:

        printf(«You entered number 40.rn»);

        break;

      case 50:

        printf(«You entered number 50.rn»);

        break;

      default:

        printf(«The entered number does not match with any of the proposed.rn»);

        return 0;

    }

  }

Бесконечный цикл получился условным. Если мы введём число, которое не совпадёт ни с одним из вариантов case, то мы попадаем в ветвь default, в которой мы после вывода пользователю соответствующего сообщения всё-же выйдем из программы благодаря оператору return. Вообще данный оператор предназначен для выхода из функции, но в нашем случае это будет и выход из программы.

Проверим работу нашего кода

Можно ли использовать switch в switch

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

Теперь давайте к «кейсу» с числом 20 добавим ещё один вариант (кейс) и сделаем так, что в данную ветвь мы будем попадать не только при вводе числа 20, но и при вводе числа 25. Также исправим и сообщение, которое программа будет в данном случае выдавать пользователю

      case 20:

      case 25:

        printf(«You entered the number 20 or 25.rn»);

Также в сообщении давайте тоже включим число 25

printf(«Enter an integer of 10, 20, 25, 30, 40, or 50rn»);

Проверим, как это работает

Можно ли использовать switch в switch

Всё отлично срабатывает!

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

Всем спасибо за внимание!

Читайте также:  Можно ли использовать системный блок с ноутбуком

Предыдущий урок Программирование на C Следующий урок

Исходный код

Смотреть ВИДЕОУРОК (нажмите на картинку)

C Оператор switch

Post Views:
6 388

Источник

Switch для строк в C++11

К сожалению, стандарт C++ не допускает применения операторов switch-case к строковым константам. Хотя в других языках, вроде C#, такая возможность имеется прямо «из коробки». Поэтому, понятное дело, многие C++ программисты пытались написать свою версию «switch для строк» — и я не исключение.
Для C++03 решения не отличались красотой и лишь усложняли код, дополнительно нагружая приложение в рантайме. Однако с появлением C++11 наконец-то появилась возможность реализовать такой код:

std::string month;
std::string days;

std::cout << «Enter month name: «;
std::cin >> month;

SWITCH (month)
{
CASE(«february»):
days = «28 or 29»;
break;

CASE(«april»):
CASE(«june»):
CASE(«september»):
CASE(«november»):
days = «30»;
break;

CASE(«january»):
CASE(«march»):
CASE(«may»):
CASE(«july»):
CASE(«august»):
CASE(«october»):
CASE(«december»):
days = «31»;
break;

DEFAULT:
days = «?»;
break;
}

std::cout << month << » has » << days << » days.» << std::endl;

Реализация этой конструкции весьма проста. Она основана на constexpr-функциях из C++11, благодаря чему почти все вычисления производятся ещё на этапе компиляции. Если кого-то интересуют её детали, добро пожаловать под кат — благо на Хабре о «switch для строк» почему-то ничего не сказано.

Чего же мы хотим?

Прежде всего — чтобы это был полноценный switch, а не его «эмуляция» путём скрытия if-операторов и функций для сравнения строк внутри макросов, поскольку сравнение строк в рантайме — дорогостоящая операция, и проводить её для каждой строки из CASE слишком расточительно. Поэтому такое решение нас не устроит — к тому же, в нём неизбежно появляются непонятные макросы типа END_STRING_SWITCH.

Кроме того, очень желательно по-максимуму задействовать компилятор. Например, что будет с «обычным» switch-case в случае, когда аргументы двух case окажутся одинаковыми? Правильно, компилятор тут же обругает нас: «duplicate case value», и прекратит компиляцию. А в примере по вышеуказанной ссылке, разумеется, ни один компилятор не сможет заметить эту ошибку.

Важен и итоговый синтаксис, простой и без лишних конструкций. Именно поэтому известный вариант «std::map со строковым ключом» нас тоже не устраивает: во-первых, аргументы case в нём не выглядят наглядными — а во-вторых, он требует обязательной инициализации используемого std::map в рантайме. Функция этой инициализации может находиться где угодно, постоянно глядеть в неё слишком утомительно.

Начинаем вычислять хэш

Остаётся последний вариант: вычислять хэш строки в switch, и сравнивать его с хэшем каждой строки в case. То есть, всё сводится к сравнению двух целых чисел, для которых switch-case прекрасно работает. Однако стандарт C++ говорит, что аргумент каждого case должен быть известен ещё при компиляции — поэтому функция «вычисления хэша от строки» должна работать именно в compile-time. В C++03 её можно реализовать лишь с помощью шаблонов, наподобие вычисления CRC в этой статье. Но в C++11, к счастью, появились более понятные constexpr-функции, значения которых также могут вычисляться компилятором.

Итак, нам нужно написать constexpr-функцию, которая бы оперировала числовыми кодами char-символов. Как известно, тело такой функции представляет из себя «return <известное в compile-time выражение>«. Попробуем реализовать самый «вырожденный» её вариант, а именно — функцию вычисления длины const char* строки. Но уже здесь нас поджидают первые трудности:

constexpr unsigned char str_len(const char* const str)
{
return *str ? (1 + str_len(str + 1)) : 0;
}

std::cout << (int) str_len(«qwerty») << » » << (int) str_len(«йцукен») << std::endl; // проверяем

Компилятор не ругается, функция корректна. Однако у меня она почему-то вывела не «6 6», а «6 12». Отчего так? А всё дело в том, что я набрал этот исходный код под Windows в «линуксовой» кодировке UTF-8, а не в стандартной Win-1251 — и поэтому каждый «кириллический» символ воспринялся как два. Вот если сменить кодировку на стандартную, тогда действительно выведется «6 6». Что ж получается, наша задумка потерпела крах? Ведь это не дело, когда при разных кодировках получаются разные хэши…

Проверяем содержимое строки

Но зачем нам кириллица или азиатские иероглифы? В подавляющем большинстве случаев, для исходников достаточно лишь английских букв и стандартных знаков пунктуации — то есть символов, умещающихся в диапазоне от 0 до 127 в ASCII-таблице. А их char-коды при смене кодировки не изменятся — и поэтому хэш от строки, составленной лишь из них, всегда будет одинаков. Но как быть, если программист случайно всё же введёт один из таких символов? На помощь нам приходит следующая compile-time функция:

constexpr bool str_is_correct(const char* const str)
{
return (static_cast<signed char>(*str) > 0) ? str_is_correct(str + 1) : (*str ? false : true);
}

Читайте также:  Можно ли использовать шампунь срок годности истек

Она проверяет, содержит ли известная на стадии компиляции строка только символы из диапазона 0-127, и возвращает false в случае нахождения «запретного» символа. Зачем нужен принудительный каст к signed char? Дело в том, что в стандарте C++ не определено, чем же именно является тип char — он может быть как знаковым, так и беззнаковым. А вот его sizeof всегда будет равен 1, отчего мы смещаемся вправо на единицу. Таким образом, нужный нам макрос CASE будет иметь вид:

#define CASE(str) static_assert(str_is_correct(str), «CASE string contains wrong characters»);
case str_hash(…)

Тут используется ещё одна фича C++11 — assert при компиляции. То есть, если строка-аргумент макроса CASE будет содержать хотя бы один «запретный» символ — компиляция остановится со вполне понятной ошибкой. Иначе, будет вычислен хэш, значение которого подставится в case. Это решение избавит нас от проблем с кодировкой. Осталось лишь написать саму функцию str_hash(), которая и вычислит нужный хэш.

Возвращаемся к вычислению хэша

Выбрать хэш-функцию можно по-разному, и самый главный вопрос тут — это возможность коллизий. Если хэши двух различных строк совпадут, то программа может перепрыгнуть со switch на ложную case-ветку. И спутник упадёт в океан… Поэтому будем использовать хэш-функцию, не имеющую коллизий вообще. Так как уже установлено, что все символы строки расположены в диапазоне 0-127, то функция будет иметь вид: . Её реализация такова:

typedef unsigned char uchar;
typedef unsigned long long ullong;

constexpr ullong str_hash(const char* const str, const uchar current_len)
{
return *str ? (raise_128_to(current_len — 1) * static_cast<uchar>(*str)
+ str_hash(str + 1, current_len — 1)) : 0;
}

Здесь raise_128_to() — это compile-time функция возведения 128 в степень, а current_len — это длина текущей строки. Конечно, длину можно вычислять и на каждом шаге рекурсии, но это лишь замедлит компиляцию — лучше сосчитать её до первого запуска str_hash(), и затем всегда подставлять как дополнительный аргумент. При какой же максимальной длине строки эта функция не будет иметь коллизий? Очевидно, лишь тогда, когда полученное ею значение всегда уместится в диапазоне типа unsigned long long (также окончательно введённого в C++11), то есть если оно не превышает 264-1.

Можно подсчитать, что эта максимальная длина будет равна 9 (обозначим это число как MAX_LEN). А вот максимально возможное значение хэша составит ровно 263 для строки, все символы которой имеют код 127 (и являются нечитаемыми в ASCII, так что под CASE мы её всё равно не загоним). Но как быть, если под CASE будет стоять строка из 10 символов? Если мы действительно не хотим возникновения коллизий, то нужно запретить и такую возможность — то есть, расширить уже используемый нами static_assert:

#define CASE(str) static_assert(str_is_correct(str) && (str_len(str) <= MAX_LEN),
«CASE string contains wrong characters, or its length is greater than 9»);
case str_hash(str, str_len(str))

Производим финальные штрихи

Всё, с макросом CASE покончено. Он либо выдаст нам ошибку компиляции, либо вычислит уникальное значение хэша. Вот для подсчёта хэша в макросе SWITCH нам придётся сделать отдельную функцию, поскольку она будет работать уже в рантайме. А если её строка-аргумент будет иметь длину более 9 символов, то договоримся возвращать 264-1 (обозначим это число как N_HASH). Итак:

#define SWITCH(str) switch(str_hash_for_switch(str))
const ullong N_HASH = static_cast<ullong>(-1); // по аналогии с std::string::npos

inline ullong str_hash_for_switch(const char* const str)
{
return (str_is_correct(str) && (str_len(str) <= MAX_LEN)) ? str_hash(str, str_len(str)) : N_HASH;
}

Собственно, вот и всё. В рантайме вычислится хэш для строки в SWITCH, и если одна строк в CASE имеет такой же хэш, исполнение пойдёт на неё. Если какая-то строка из CASE содержит «запретные» символы, или её длина больше 9 символов — мы получим надёжную ошибку компиляции. Нагрузки в рантайме почти нет (за исключением однократного вычисление хэша для SWITCH), читаемость кода не страдает. Осталось лишь перегрузить функцию str_hash_for_switch() для строк std::string, и заключить всё внутрь namespace.

Итоговый h-файл исходников лежит на Гитхабе. Он подойдёт для любого компилятора с поддержкой C++11. Для использования «switch для строк» просто сделайте инклуд str_switch.h куда хотите, и всё — макросы SWITCH, CASE и DEFAULT перед вами. Не забудьте про ограничение на длину строки в CASE (9 символов).
В общем — надеюсь, что кому-нибудь эта реализация пригодится 😉

  P. S. Upd: в коде была обнаружена пара неточностей — поэтому сейчас он обновлён, вместе со статьёй. В комментариях также предлагалось использовать другую функцию для вычисления хэша — разумеется, каждый может написать свою её реализацию.

Источник