【问题标题】:Is there a way to make Objective-C support a multi-variable switch construct?有没有办法让 Objective-C 支持多变量开关构造?
【发布时间】:2011-06-08 15:57:56
【问题描述】:

我想知道:有没有办法让 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 我很想知道哪些语言支持这种功能。
  • 这些:stackoverflow.com/questions/5436983/…。但我想这与 Objective-C 无关。
  • 简短回答是否定的。充其量你可以创建一个哈希生成器来打开,但是 obj-c 没有仿函数,你仍然需要在你的 vars 上调用一些东西来使用 switch。除非您使用预处理器宏,否则这似乎不值得付出努力。也许使用函子的 obj-c++ 模板解决方案会起作用,但它会很尴尬并且不会节省太多时间。在第一个 var 的 switch 中调用方法有什么问题吗? =)
  • 这个东西需要高度优化吗? else if 语句的列表是否足够?您是否对两者都进行了编码并比较了两者?
  • 我真的不需要高度优化的东西。只是一些易于编写且更易于维护的东西。 else if 的列表不是最好的解决方案,恕我直言。但这应该是一个更通用的问题。我想要一个最适合所有人的解决方案。

标签: objective-c switch-statement


【解决方案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;
}

注意:这里之前的代码不会工作,这工作。

【讨论】:

  • 我会说它非常完美。但它只适用于正值(可能不是一个大问题)。如果您找到处理case (n, *) 案例的方法,那将是完美的。也许将一些nil 值传递给自定义函数...
  • case(0, 11)case(1, 1) 将发生冲突,并且打印 one one 为预期的 zero eleven 是不可接受的。
【解决方案2】:

在您提到的问题中: “我真正想做的是这样的事情:”

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

如果您的可能值在 {0..n} 范围内,您可以使用两种方法。

  1. 你可以构造一个 选择器的多维数组 然后选择正确的选择器 使用你的 var1,var2。 (这种方法效率更高,因为选择器是在编译时构建的)

  2. 你可以构造选择器 基于值的名称 var,var2 变量。

这两种方法都在此代码 sn-p 中举例说明。

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

}

【讨论】:

  • 感谢您的回答,但这需要我在课堂上创建很多不同的方法,而我真的不想这样做。我正在寻找一种代码效率高且人类效率高的方法。顺便说一句,您的解决方案对于case (*,1) 假设无效。
  • 也许你可以使用块而不是选择器,以避免在其他方法中外部化你的代码?
【解决方案3】:

正如其他人所说,如果您发现自己想要这样做,那么您真的应该考虑一种更好地构建数据的方法。您似乎正在处理标量类型。我倾向于发现一点 C++ 在那里有很长的路要走,你可以使用 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: 子句堆叠在一起。在任何情况下,您都必须添加默认值并以某种方式中断-也许您可以使用一些额外的宏和 goto 中断来做到这一点。关于前处理器的标准警告适用:它不像 Objective-C 那样漂亮,而且很容易给你各种深不可测的语法错误。但如果你真的想要改变语言的语法,那么你的选择要么是这样,要么是在 Apple 工作。

【讨论】:

    【解决方案4】:

    不是解决方案,只是一种解决方法:您可能会想到这样的事情

    SInt32 var1, var2;
    /*...*/
    SInt64 var3 = var1<<32 + var2;
    
    switch(var3) {
     .
     .
     .
    }
    

    如果你的 vars 有一些特定的属性,你可以使用它来做一些轻微的简化,即如果 vars

     var3 = 10*var1+var2;
    

    【讨论】:

    • 这样你将无法正确处理所有可能的情况,我的意思是首先以人类可以理解的方式。此外,您将无法快速写下假设case (0,*) : 构造的相应测试。而且,最后但并非最不重要的一点是,对我来说很丑:D
    • 我很确定您可以处理所有可能的情况(您可以评论每个情况以使其更清晰)。我想你在谈论一个通用的构造,因为实际上(0,*) 意味着var1 == 0,所以没有必要检查var2,你可以做类似if (var1==0) {} else { switch...} 的事情。但是,当然,如果你不喜欢它,我能做的很少...... :)
    • 好吧,我现在知道这是该问题的常见解决方案。我有时会用这样的东西。我正在寻找的是更类似于“基于模式”的switch 构造(就像许多函数式语言一样)。感谢您的尝试!
    【解决方案5】:

    你确定这是一个很好的编程风格有这样的结构:) 吗? Switch 语句旨在加速多个 if () else if () 语句,但是当您需要比较多个变量时,优化就会进行。

    一种方法是使用逻辑移位/其他操作将多个变量放在一个中,另一种方法是使用 if ((a == 0) & (b == 0)) {...} else if ((a == 0) && (b == 1)).... 这不会占用太多空间。

    【讨论】:

    • 即使使用简单的if 结构而不是switch 也不需要更多的空间,但switch 确实更快。与其输入if ((a == 0) &amp; (b == 0)) {...},不如写case (0,0):。请注意,if 构造对于变量名称的更改并不“稳健”,而 switch 构造是。
    【解决方案6】:

    由于 ObjC-Runtime 的存在,应该可以使用诸如选择器类型 SEL/@selector()/performSelectorNSSelectorFromString、NSArrays/NSDictionaries 和 NSIndexPathes 之类的东西来构建开关替换,这样更强大比 c-switch 语句。
    但它不会驻留在语言级别,因为 Python-code 也不是这样。

    【讨论】:

    • 这里的主要问题是您可以在 Objective-C 中使用块,但例如旧的 iPhone OS SDK 不支持它们。在 Obj-C 中构建匿名函数相当困难......
    • 好吧,我不想为了模仿多变量开关构造而构建无限多的不同方法。如果这是唯一的解决方案,我最好继续使用嵌套开关。
    • 您可以拥有一组方法,它们接受一个对象,并将方法名称保存在带有这些对象的 NSIndexPathes 的嵌套数组中。不同的 indexPathes 可以引用相同的方法,这将使用不同的对象执行。
    • 是的,但我必须为我在 switch 中使用的每个不同的case 创建一个方法。我更喜欢使用块来执行此操作,因为缺少方法定义会减少开销。但并不总是支持使用块。顺便说一句,非常感谢您的提示
    【解决方案7】:

    比如:

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

    问题可能是 *,1 但你可以把它们全部拼出来,或者你可以写一个contains @",1"(我忘记了正确的语法 atm)

    【讨论】:

    • switch/Case不兼容对象比较,只能和原子C类型比较,所以这个方案不能用。
    猜你喜欢
    • 2020-04-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-27
    • 1970-01-01
    • 2012-06-26
    • 1970-01-01
    相关资源
    最近更新 更多