Есть ли способ заставить Objective-C поддерживать конструкцию переключателя с несколькими переменными? - PullRequest
16 голосов
/ 08 июня 2011

Мне было интересно: есть ли способ сделать поддержку 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: ...
}

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

Ответы [ 7 ]

10 голосов
/ 16 июня 2011

Мне нравится ответ от 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;
}

ПРИМЕЧАНИЕ: код, который был здесь ранее, не работал бы, это работает.

3 голосов
/ 10 июня 2011

В своем вопросе вы упоминаете: «Я бы очень хотел сделать что-то вроде этого:»

switch (var1, var2) {
    case (0,0) : ...
    case (1,0) : ...
    case (*,1) : ...
    default: ...
}

Если это так, ваши возможные значения находятся в диапазоне {0 ..n}, вы можете использовать два метода.

  1. Вы можете создать многомерный массив селекторов, а затем выбрать правильный селектор, используя ваши var1, var2.(Этот метод более эффективен из-за создания селекторов во время компиляции)

  2. Вы можете создать имя селектора на основе значений переменных var, var2.

    *Примеры 1013 *

ОБА здесь приведены в этом фрагменте кода.

- (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];

}
2 голосов
/ 16 июня 2011

Как уже говорили другие, если вы захотите это сделать, тогда вам следует подумать о том, как лучше структурировать ваши данные.Похоже, вы имеете дело со скалярными типами.Я нахожу, что немного с ++ проходит долгий путь, и вы можете интегрировать его в проект 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;
}

Это не совсем похоже на переключатель/ case, так как вы не можете сложить несколько выражений case: поверх одного другого.В любом случае вам нужно было бы добавить по умолчанию и разбить как-нибудь - возможно, вы могли бы сделать это с некоторыми дополнительными макросами и переходом на разрыв.Применяются стандартные предостережения, касающиеся препреоцессора: он не так хорош, как Objective-C, и будет склонен давать вам все виды непостижимых синтаксических ошибок.Но если вы действительно хотите изменить синтаксис языка, то вы можете выбрать что-то вроде этого или получить работу в Apple.

1 голос
/ 08 июня 2011

Не решение, а обходной путь: вы можете подумать о чем-то подобном

SInt32 var1, var2;
/*...*/
SInt64 var3 = var1<<32 + var2;

switch(var3) {
 .
 .
 .
}

, если у ваших переменных есть какое-то конкретное свойство, вы можете использовать его для некоторых небольших упрощений, т.е. если переменные <10тогда вы можете использовать </p>

 var3 = 10*var1+var2;
0 голосов
/ 17 июня 2011

Как насчет чего-то вроде:

switch ([NSString StringWithFormat: @"%d,%d", var1, var2]) {
    case (@"0,0") : ...
    case (@"1,0") : ...
    case (@"*,1") : ...
    default: ...
}

Проблема была бы *, 1, но вы могли бы либо прописать все это, либо вы могли бы сделать contains @",1" (я забыл правильный синтаксис atm)

0 голосов
/ 14 июня 2011

В связи с существованием ObjC-Runtime, должна быть возможность создать замену коммутатора такими вещами, как тип селектора SEL / @ selector () / executeSelector , NSSelectorFromString , NSArrays / NSDictionaries и NSIndexPathes , что намного мощнее, чем оператор c-switch.
Но он не будет находиться на уровне языка, так как этот Python-код не так хорош.

0 голосов
/ 08 июня 2011

Вы уверены, что это хороший стиль программирования, чтобы иметь такие конструкции :)? Оператор Switch предназначен для ускорения нескольких операторов if () и if (), но когда вам нужно будет сравнить несколько переменных, оптимизация пойдет.

Один из способов - использовать логические сдвиги / другие операции для размещения нескольких переменных в одной, а другой - использовать if ((a == 0) & (b == 0)) {...} else if ((a == 0) && (b == 1)) .... Это не займет много места.

...