Есть ли способ заставить Objective-C поддерживать конструкцию переключателей с несколькими переменными?
Мне было интересно: существует ли способ сделать Objective-C поддержку конструкции с несколькими переменными switch
?
Я имею в виду, что очень часто мне приходится иметь дело с проблемами, в которых решение зависит от пары переменных, а не от одной. Для длинного списка значений одной переменной можно использовать простую конструкцию switch/case
:
switch (var) {
case 0: ...
case 1: ...
default: ...
}
Но когда вам приходится иметь дело с комбинацией двух значений, вам часто бывает что-то вроде этого:
switch (var1) {
case 0:
switch (var2) {
case 0: ...
case 1: ...
}
case 1:
switch (var2) {
case 0: ...
case 1: ...
}
...
}
И это становится все сложнее... Мне бы очень хотелось сделать что-то вроде этого:
switch (var1, var2) {
case (0,0) : ...
case (1,0) : ...
case (*,1) : ...
default: ...
}
что приведет к созданию более простой и понятной конструкции. Существует ли какое-либо решение для поддержки такой структуры? Или небольшое изменение этого?
Ответы
Ответ 1
Мне нравится ответ от saphrosit, но я постараюсь сделать его понятным.
Представьте возможные результаты вашей проблемы как квадраты в сетке, где один край края сетки представляет значения var1, а другой край представляет возможные значения var2, а затем, если вы увеличите счетчику по квадратам в сетке вы бы получили что-то вроде этого
|| var1 |
|| 0 | 1 | 2 | ... | j | ... | n-1 |
======++=====================================================================|
0 || 0 | 1 | 2 | ... | j | ... | n-1 |
---||---------+-----------+-----------+-----+-----------+-----+-----------|
1 || n | n+1 | n+2 | ... | n+j | ... | n+(n-1) |
---||---------+-----------+-----------+-----+-----------+-----+-----------|
2 || 2*n | 2*n+1 | 2*n+2 | ... | 2*n+j | ... | 2*n+(n-1) |
v ---||---------+-----------+-----------+-----+-----------+-----+-----------|
a || . | . | . | | . | | . |
r ---||---------+-----------+-----------+-----+-----------+-----+-----------|
2 i || i*n | i*n+1 | i*n+2 | ... | i*n+j | ... | i*n+(n-1) |
---||---------+-----------+-----------+-----+-----------+-----+-----------|
|| . | . | . | | . | | . |
----||---------+-----------+-----------+-----+-----------+-----+-----------|
m-1 || (m-1)*n | (m-1)*n+1 | (m-1)*n+2 | ... | (m-1)*n+j | ... | mn-1 | <-- (m-1)*n+(n-1) = m*n-n + (n-1) = mn-1
------||---------+-----------+-----------+-----+-----------+-----+-----------|
Это называется основной матрицей строк, так как вы начинаете с подсчета по строкам. Существует также основная матрица столбцов, в которой вы начинаете считать первый, а не наоборот. Вот как матрицы хранятся в библиотеке C BLAS, поэтому они должны быть очень похожими на многих людей.
В вашем случае результат, который вы ищете, может быть адресован как var3 = var2*n + var1
, вы можете проложить это в коде как
#define N 10 // Maximum size of var1
int main() {
int var1 = 1;
int var2 = 1;
switch(var1 + var2 * N){
case 1 + 1 * N: printf("One One"); break;
case 2 + 2 * N: printf("Two Two"); break;
default:
printf("Bada Bing");
}
return 0;
}
ПРИМЕЧАНИЕ: код, который был здесь ранее, не работал бы, это работает.
Ответ 2
В вашем вопросе вы упомянули:
"То, что я действительно хотел бы сделать, это что-то вроде этого:"
switch (var1, var2) {
case (0,0) : ...
case (1,0) : ...
case (*,1) : ...
default: ...
}
Если это так, ваши возможные значения находятся в диапазоне {0..n}, вы можете использовать два метода.
-
Вы можете создать
многомерный массив селекторов
и затем выберите правильный селектор
используя var1, var2. (Этот метод более эффективен благодаря построению селекторов во время компиляции)
-
Вы можете создать селектор имя, основанное на значениях var, var2.
В этом фрагменте кода приведены примеры BOTH.
- (void) case00 {
NSLog(@"Case ZeroZero");
}
- (void) testSelectorIdea {
NSInteger var1 = 0;
NSInteger var2 = 0;
// ----- ARRAY OF SELECTORS METHOD ----
SEL selectors[2][2] = {@selector(case00),@selector(case01), @selector(case10), @selector(case11)};
[self performSelector:selectors[var1][var2]];
// ----- SELECTOR CONSTRUCTION METHOD ----
NSString * selectorName = [NSString stringWithFormat:@"case%d%d",var1,var2];
SEL selector = NSSelectorFromString(selectorName);
[self performSelector:selector];
}
Ответ 3
Как говорили другие, если вы захотите это сделать, вам следует подумать о том, как лучше структурировать ваши данные. Вам кажется, что вы имеете дело со скалярными типами. Я, как правило, немного разбираюсь в С++, и вы можете интегрировать его в проект objective-c, используя objective-c ++. Тем не менее, если вы уверены, что хотите, что вы говорите, и хотите, чтобы вы не противились злу препроцессору, вы можете попробовать что-то вроде этого:
#define BEGIN_SWITCH(type,...) ({ typedef type T; T switchVars[] = { __VA_ARGS__ }; BOOL performAction;
#define CASE(...) { \
T caseVars[] = { __VA_ARGS__ }; \
performAction = YES; \
size_t count = MIN(sizeof(switchVars), sizeof(caseVars)) / sizeof(T); \
for (size_t i = 0 ; i < count / sizeof(T) ; i++) \
{ \
if (caseVars[i] != switchVars[i]) \
{ \
performAction = NO; \
break; \
} \
} \
} \
if (performAction)
#define END_SWITCH });
int main (int argc, char const *argv[])
{
id pool = [[NSAutoreleasePool alloc] init];
int a1 = 0;
int a2 = 5;
int a3 = 10;
BEGIN_SWITCH(int, a1, a2, a3)
CASE(0,5) NSLog(@"0,5");
CASE(1,2,3) NSLog(@"1,2,3");
CASE(0,5,10) NSLog(@"0,5,10");
CASE(1) NSLog(@"1");
END_SWITCH
[pool drain];
return 0;
}
Это не совсем так, как switch/case, поскольку вы не можете складывать несколько case: clauses поверх oneanother. Вам придется добавить по умолчанию и как-то сломаться - возможно, вы могли бы сделать это с помощью некоторых дополнительных макросов и перейти к перерыву. Стандартные оговорки в отношении prereocessor применяются: это не так, как Objective-C, и будет подвержен сильным синтаксическим ошибкам. Но если вы действительно хотите изменить синтаксис языка, то ваши варианты либо что-то вроде этого, либо получить работу с Apple.
Ответ 4
Не решение, просто обходное решение: вы можете подумать над чем-то вроде этого
SInt32 var1, var2;
/*...*/
SInt64 var3 = var1<<32 + var2;
switch(var3) {
.
.
.
}
если ваши вары имеют какое-то определенное свойство, которое вы можете использовать, чтобы сделать некоторые небольшие упрощения, то есть, если vars являются < 10, то вы можете использовать
var3 = 10*var1+var2;
Ответ 5
Вы уверены, что это хороший стиль программирования, чтобы иметь такие конструкции:)?
Оператор switch предназначен для ускорения нескольких операторов if() else if(), но когда вам нужно будет сравнить несколько переменных, оптимизация которых будет идти.
Один способ - использовать логические сдвиги/другие операции для размещения нескольких переменных в одном, а другой - if ((a == 0) и (b == 0)) {...} else if ((a == 0) && (b == 1))....
Это не займет много места.
Ответ 6
Из-за существования ObjC-Runtime должно быть возможно создать замену коммутатора такими вещами, как селекторный тип SEL/@selector ( )/performSelector, NSSelectorFromString, NSArrays/NSdictionaries и NSIndexPathes, то есть более мощный, чем оператор c-switch.
Но он не будет проживать на языковом уровне, так как этот код Python не соответствует.
Ответ 7
Как насчет чего-то типа:
switch ([NSString StringWithFormat: @"%d,%d", var1, var2]) {
case (@"0,0") : ...
case (@"1,0") : ...
case (@"*,1") : ...
default: ...
}
Проблема заключалась бы в *, 1, но вы могли бы либо записать все, либо сделать contains @",1"
(я забыл правильный синтаксис atm)