Как в case задать диапазон
Перейти к содержимому

Как в case задать диапазон

  • автор:

Switch для двух параметров в С++

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

Допустим есть две переменные n и m, каждая может принимать значение от 0 до 9, и мы имеем такую структуру:

switch (n) < case 0: < switch (m) < case 2: case 4: . break; case 5: . break; default: . break; >> break; . > 

Так как значение каждой переменной помещается в байт, то пусть хеш функция будет следующей ((n SHIFT) + m) (где SHIFT = 8).
И вот тут то и возникает проблема: если определены оба значения, то мы получим конкретное число после расчета хеша. Но если скажем n = 0, а m не равно 2,4,5 то что делать в этом случае? Перечислять остальные варианты для выполнения действий по умолчанию слишком накладно, ведь их диапазоны значений могут быть куда больше чем 0..9. То есть по сути надо словить попадание значения вычисленного хеша в некоторый диапазон.

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

Итак что же у меня получилось

Такую простую хеш функцию можно было бы реализовать обычным макросом, но так как в С++11 появились constexpr функции, то решил использовать их:

constexpr int hash(int n, int m)

, verifyValues это функция для проверки, что параметры лежат в заданном диапазоне. Для этого используются константы MAX_N, MAX_M, и в случае если параметры не валидны будет возвращено -1.

#define isInBound(min, value, max) ((value >= min) && (value

Глядя на хеш функцию также хорошо было бы добавить следующую проверку:

MAX_M < pow(2, SHIFT) 

Дополнительный код и проверки скроем с помощью макросов:

#define SWITCH(n, m) static_assert(MAX_M < pow(2, SHIFT), "shift value is not enough to cover all M values"); \ switch(hash(n, m)) #define CASE(n, m) static_assert(verifyValues(n, m), "N or M value is out of range"); \ case hash(n, m) 

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

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

#define DEFAULT(n) \ case -1: ASSERT(false); break; \ default: switch(n)

Так как при расчете хеш функция может вернуть -1 (в случае передаче ей неверных параметров), то был добавлен обработчик для case -1 (в моем примере это обычный ASSERT).

В итоге у меня получился вот такой switch с двумя параметрами:

SWITCH(a, b)

Тут главное не забыть, что после макроса DEFAULT не надо ставить двоеточие.

Весь код целиком

const int MAX_N = 10; const int MAX_M = 10; const int SHIFT = 8; #define isInBound(min, value, max) ((value >= min) && (value constexpr int hash(int n, int m) < return verifyValues(n, m) ? ((n #define SWITCH(n, m) static_assert(MAX_M < pow(2, SHIFT), "shift value is not enough to cover all M values"); \ switch(hash(n, m)) #define CASE(n, m) static_assert(verifyValues(n, m), "N or M value is out of range"); \ case hash(n, m) #define DEFAULT(n) \ case -1: Q_ASSERT(false); break; \ default: switch(n) < #define DEFAULT_CASE(n) case n #define END_DEFAULT >. SWITCH(a, b)

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

Надеюсь, что кому-нибудь эта реализация пригодится. Спасибо за внимание.

P.S. В принципе используя такую структуру можно написать switch не только для двух параметров, но и для трех и более.

Как в case задать диапазон

Форумчанин

Регистрация: 16.01.2008

Сообщений: 288

Диапазон оператора switch

Подскажите пожалуйсто, как сделать, что-бы работал такой код.

int a; switch (a)

Тупой студент

Форумчанин

Регистрация: 12.05.2007

Сообщений: 614

эм. наверна только так
switch (a)
case 1:
case 2:
case 3:
.
.
case 100:
cout break;
default: cout break;
>

для таких случаев нада if использовать

Перечисляемый тип, тип-диапазон и оператор CASE

Язык Turbo Pascal позволяет программисту определять свои собственные типы данных. Здесь будет рассмотрен перечисляемый тип данных, который задается перечислением тех значений, которые может принимать переменная этого типа.

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

Описание перечисляемого типа:

Пример: пусть нам необходима переменная, которая должна принимать значения, соответствующие названиям месяцев.

Определим тип month (месяц) и переменную m1 этого типа.

Month = (January, February, March, April,

May, June, July, August, September,

October, November, December);

Здесь каждое значение переменной m1 является строковой константой. К сожалению, русские слова употреблять нельзя!

Альтернативно, переменную перечисляемого типа можно объявлять в программе без предварительного описания самого типа.

значений через запятую>);

m2: (January, February, March, April, May,

June, July, August, September, October,

Перечислимый тип относится к порядковым типам данных.

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

Первое значение в списке, определяющем перечисляемый тип, получает порядковый номер 0, следующие значения ? 1, 2 и т.д.

Максимальное число возможных значений? 65536.

Ко всем порядковым типам применимы следующие функции:

возвращает порядковый номер значения выражения x;

возвращает предыдущее значение порядкового типа, соответствующее порядковому номеру ord (x) - 1;

возвращает следующее значение порядкового типа, соответствующее порядковому номеру ord (x) +1.

К порядковым типам относятся также все целые типы, логический (boolean) и символьный (char), а также тип-диапазон.

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

Описание типа-диапазона:

Пример: определим тип MonthNumber (номер месяца) как интервал целых значений от 1 до 12.

Альтернативно, переменную типа-диапазона можно объявлять в программе без предварительного описания самого типа.

К переменным типа-диапазона применимы следующие функции:

возвращает максимальное значение типа-диапазона, к которому принадлежит переменная x;

возвращает минимальное значение типа-диапазона, к которому принадлежит переменная x.

Использование перечисляемых типов и типов-диапазонов повышает надежность программы благодаря возможности контролировать множество значений, которые могут принимать переменные перечисляемых типов. Для контроля возможного выхода значений переменных за границы допустимого диапазона служит опция RANGE CHECKING (меню OPTIONS ? COMPILER ? RUNTIME ERRORS ? RANGE CHECKING). В активном состоянии она позволяет контролировать выход значений переменных за границы диапазона.

Существуют также директивы компилятора, которые позволяют включать (директива $R+>) и отключать (директива $R->) контроль границ диапазона.

Однако, значение переменной перечисляемого типа нельзя ввести с клавиатуры с помощью оператора read или вывести на экран с помощью оператора write, т.е. такие операторы, как

недопустимы. Для ввода-вывода данных перечисляемого типа необходимо писать специальные процедуры (см. ниже).

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

Оператор выбора:

else

end;

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

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

процедура функция turbo pascal

Процедура ReadMonthNumber присваивает значение переменной m1, когда пользователь вводит номер месяца. В программе процедура заменяет оператор read.

Процедуру присваивания значения переменной перечисляемого типа можно организовать и иначе. В следующей процедуре используется вспомогательная строковая переменная name (типа string, причем количество символов в строковой переменной не превышает 8). Строковая переменная не является переменной перечисляемого типа, поэтому в данном случае невозможно использовать оператор case.

Процедура WriteMonth выводит на экран название месяца, соответствующее значению переменной m1. В программе процедура заменяет оператор write.

Текст основной программы становится компактным благодаря использованию процедур.

Program Case_operator;

Procedure ReadMonthNumber (var m1: Month);

write ('Введите номер месяца: ');

  • 1: m1: = January;
  • 2: m1: = February;
  • 3: m1: = March;
  • 4: m1: = April;
  • 5: m1: = May;
  • 6: m1: = June;
  • 7: m1: = July;
  • 8: m1: = August;
  • 9: m1: = September;
  • 10: m1: = October;
  • 11: m1: = November;
  • 12: m1: = December

Procedure ReadMonthName (var m1: Month);

name: string [8];

write ('Введите название месяца: ');

if (name = 'январь') or (name = 'Январь')

or (name = 'ЯНВАРЬ')

then m1: = January;

if (name = 'февраль') or (name = 'Февраль')

or (name = 'ФЕВРАЛЬ')

then m1: = February;

if (name = 'март') or (name = 'Март')

or (name = 'МАРТ')

then m1: = March;

if (name = 'апрель') or (name = 'Апрель')

or (name = 'АПРЕЛЬ')

then m1: = April;

if (name = 'май') or (name = 'Май')

or (name = 'МАЙ')

then m1: = May;

if (name = 'июнь') or (name = 'Июнь')

or (name = 'ИЮНЬ')

then m1: = June;

if (name = 'июль') or (name = 'Июль')

or (name = 'ИЮЛЬ')

then m1: = July;

if (name = 'август') or (name = 'Август')

or (name = 'АВГУСТ')

then m1: = August;

if (name = 'сентябрь') or (name = 'Сентябрь')

or (name = 'СЕНТЯБРЬ')

then m1: = September;

if (name = 'октябрь') or (name = 'Октябрь')

or (name = 'ОКТЯБРЬ')

then m1: = October;

if (name = 'ноябрь') or (name = 'Ноябрь')

or (name = 'НОЯБРЬ')

then m1: = November;

if (name = 'декабрь') or (name = 'Декабрь')

or (name = 'ДЕКАБРЬ')

then m1: = December

Procedure WriteMonth (m1: Month);

January: writeln ('январь. ');

February: writeln ('февраль. ');

March: writeln ('март. ');

April: writeln ('апрель. ');

May: writeln ('май. ');

June: writeln ('июнь. ');

July: writeln ('июль. ');

August: writeln ('август. ');

September: writeln ('сентябрь. ');

October: writeln ('октябрь. ');

November: writeln ('ноябрь. ');

December: writeln ('декабрь. ')

write ('Этот месяц - ');

в этом месяце>

write ('В этом месяце ');

February: writeln ('28 или 29 дней. ');

April, June, September,

November: writeln ('30 дней. ');

else writeln ('31 день. ')

Как в case задать диапазон

Конструкция switch/case похожа на конструкцию if/else , так как позволяет обработать сразу несколько условий:

var num: Int = 22 switch num

После ключевого слова switch идет сравниваемое выражение. Это может быть переменная или константа. Значение этого выражения последовательно сравнивается со значениями, помещенными после оператора сase . И если совпадение будет найдено, то будет выполняться определенный блок сase .

Если совпадение не будет найдено, то выполняется оператор default .

В данном случае так как переменная num равна 22, будет выполняться следующий блок case:

case 22: print("Переменная равна 22")

В других языках программирования, в которых также есть конструкция switch/case , обычно в конце блока case ставится оператор break для прерывания выполнения и выхода из блока switch/case . В Swift использовать оператор break в подобных случаях необязательно. Однако бывают случаи, когда мы не хотим обрабатывать какие-то определенные значения и просто хотим выйти из конструкции switch. В этом случае после оператора case или default можно указать оператор break:

var num: Int = 0 switch num

В данном случае если num равно 10 или другому числу, отличному от 0 или 22, просто произойдет выход из switch.

С помощью знака подчеркивания _ можно задать соответствие всем остальным значениям:

let number = 5 switch number

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

var num: Int = 20 switch num < case 0, 10: // если num равно 0 или 10 print("Переменная равна 0 или 10") case 11..

Оператор case 0, 10 задает два сравниваемых значения 0 и 10 и срабатывает, если выражение равно одному из этих значений.

Оператор case 20. 30 определяет целый диапазон значений от 20 до 30 (включая оба числа) и срабатывает, если выражение равно значению из этого диапазона.

В версии Swift 4 мы можем опускать одну границу диапазона:

let i = 8 switch i

Кортежи в switch/case

Кроме выражений простых типов можно сравнивать кортежи:

let personInfo = ("Tom", 22) switch personInfo

Здесь кортеж personInfo последовательно сравнивается с тремя кортежами в операторах case. При сравнении мы можем задать полный кортеж:

case ("Bob", 33): print("Имя: Bob, возраст: 33")

Либо мы также можем опустить один из элементов кортежа, подставив вместо него знак подчеркивания _:

case (_, 22): print("Имя: \(personInfo.0) и возраст: 22")

В этом случае не имеет значение, чему равен первый элемент кортежа, главное, чтобы второй элемент кортежа был равен 22.

Для числовых данных мы также можем задать не точное значение, а диапазон значений:

case ("Tom", 1. 30): print("Имя: Tom и возраст от 1 до 30")

В этом случае второй элемент кортежа должен находиться в диапазоне от 1 до 30.

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

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

let personInfo = ("Tom", 22) switch personInfo

Связывание значений

Механизм связывания значений позволяет определить в блоках case переменные и константы, значения которых будут связаны со значением сравниваемого выражения:

let number = 5 switch number

В данном случае если значение number не равно 1 и 2, то оно передается константе n, которая используется в рамках своего блока case.

При этом привязка может выполняться к переменным и константам более сложных типов, например, кортежей:

let personInfo = ("Tom", 22) switch personInfo

Если второй элемент в personInfo равен 22, то срабатывает блок

case (let name, 22): print("Имя: \(name) и возраст: 22")

Здесь переменная name получает значение первого элемента из кортежа personInfo.

Причем в этой конструкции не используется блок default , так как блок

case let (name, age): print("Имя: \(name) и возраст: \(age)")

фактически перекрывает все возможные случаи, которые не попадают под предыдущие операторы case. В этом блоке определяется константа (хотя это может быть и переменная), которая состоит из элементов кортежа personInfo.

Оператор where

Если при выполнении блока case мы хотим задать дополнительные условия, то в этом случае нам поможет оператор where . Например:

let i = 8 switch i < case let k where k < 0: print("i - отрицательное число") case let k where k >0: print("i - положительное число") case 0: print("i is 0") default:break >

Пример с кортежами:

let personInfo = ("Tom", 22) switch personInfo < case ("Tom", _) where personInfo.1 >10 && personInfo.1 < 15: print("Имя: Tom и возраст от 10 до 15") case ("Tom", _) where personInfo.1 >= 20: print("Имя: Tom и возраст от 20 и выше") default: print("Неизвестно") >

Выражения where определяют дополнительные условия. И если они не выполняются, то соответственно блок case тоже не выполняется.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *