开发者

Is there a way to make Objective-C support a multi-variable switch construct?

开发者 https://www.devze.com 2023-03-11 17:58 出处:网络
I was wondering: is there a way to make Objective-C support a multi-variable switch construct? I mean, very often I have to deal with problems in which the solution depends on a pair of variables ins

I was wondering: is there a way to make Objective-C support a multi-variable switch construct?

I mean, very often I have to deal with problems in which the solution depends on a pair of variables instead of a single one. For a long list of single variable values one can use a simple switch/case construct:

switch (var) {
    case 0: ...
    case 1: ...
    default: ...
}

But when you have to deal with the combination of two values you often happen to do something l开发者_开发问答ike this:

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

And it gets more and more complicated ... What I'd really love to do is something like this:

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

that will result in a more simple and readable construct. Is there any solution to support such a structure? Or a slight variation of this one?


I like the answer from saphrosit but I'll make try to make it easy to understand.

Imagine the possible outcomes of your problem as squares in a grid where one edge of the edge of the grid represents values of var1 and the other edge represents the possible values of var2, then if you incrementialy counted through the squares of the of the grid you would it get something like this

      ||                              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
------||---------+-----------+-----------+-----+-----------+-----+-----------|

This would is called a row major matrix since you start by counting accross the rows. There is also a column major matrix where you start counting down first instead of across. This is how matrixes are stored in the C BLAS library so it should be very fimilar to many people.

In your case the outcome you're looking for can be addressed as var3 = var2*n + var1 you could lay this out in code as

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

NOTE: the code that was here earlier wouldn't have worked, this works.


In your question you mention: "What I'd really love to do is something like this:"

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

If it is the case that your possible values are in the range {0..n}, there are two methods you could use.

  1. You could construct a multidimensional array of selectors and then select the correct selector using your var1, var2. (This method is more efficient due to the constructing of the selectors at compile time)

  2. You could construct the selector name based on the values of the var,var2 variables.

BOTH methods are exemplified here in this code snippet.

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

}


As others have said, if you find yourself wanting to do this, then you should really think about a way of structuring your data better. You seem like you're dealing with scalar types. I tend to find a little c++ goes a long way there, and you can integrate that into an objective-c project using objective-c++. That said, if you're sure you want what you say you want and you're not averse to evilness with the preprocessor, you could try something like this:

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

It isn't quite like switch/case, as you can't stack multiple case: clauses on top of oneanother. You'd have to add in default and break somehow in any case -- maybe you could do this with some extra macros and a goto for break. Standard caveats about the prepreocessor apply: It isn't pretty as Objective-C, and will be prone to giving you all kinds of unfathomable syntax errors. But if you really want to alter the syntax of the language, then your options are either something like this or getting a job with Apple.


Not a solution, just a workaround: you may think to something like this

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

switch(var3) {
 .
 .
 .
}

if your vars have some particular property you may use it to do some slight simplifications, i.e. if vars are < 10 then you may use

 var3 = 10*var1+var2;


Are you sure that this is a good programming style to have such constructions :) ? Switch statement is designed to speedup multiple if () else if () statements, but when you will need to compare multiple variables that optimization will go.

The one way is to use logical shifts/other operations to place multiple variables in one, and the other is to use if ((a == 0) & (b == 0)) {...} else if ((a == 0) && (b == 1)).... That wouldn't take a lot of space.


Due to the existences of the ObjC-Runtime, it should be possible to build up a switch replacement with things like the selector type SEL/@selector()/performSelector, NSSelectorFromString, NSArrays/NSDictionaries and NSIndexPathes, that is way more powerful than the c-switch statement.
But it won't reside on language level, as this Python-code isn't as-well.


How about something like:

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

The problem would be *,1 but you could either spell those all out, or you could do a contains @",1" (I forget the right syntax atm)

0

精彩评论

暂无评论...
验证码 换一张
取 消