【问题标题】:How to shorten multiple && condition如何缩短多个&&条件
【发布时间】:2015-11-18 06:58:39
【问题描述】:

目前我可以运行我的程序,但我的代码包含很多重复,看起来像:

while(option != 'E' && option != 'D' && option != 'P' && option != 'Q' &&
      option != 'e' && option != 'd' && option != 'p' && option != 'q') {
  // Some code here
}

或:

while(cType != 'S' && cType != 'L' && cType != 'O' && cType != 'Q' &&
      cType != 's' && cType != 'l' && cType != 'o' && cType != 'q') {
  // Some code here
}

缩短上述代码的最快方法是什么?

(除了使用附加功能还有什么办法吗?)

【问题讨论】:

  • std::tolower可以摆脱一半的条件
  • 我建议将逻辑放在一个函数中并调用该函数。
  • 完全取决于用例。请选择一种语言。
  • 有一个无符号字符数组,其中查找数组[无符号] 提供所需的结果

标签: c++ c algorithm optimization conditional


【解决方案1】:

没有分支的候选快速测试。

1) 减少大小写。
2)形成4个差异的乘积。
3) 1 与 0 比较。

假设只有字母。
最多可使用 4 个 (sizeof int/sizeof char) 字母。
大写/小写的不同之处在于相同的位。 (这适用于 ASCII 和 EBCDIC)

#define CaseMaskBits ((unsigned char)~('A'^'a'))
#define Product4(ch, s) ((ch-s[0]) * (ch-s[1]) * (ch-s[2]) * (ch-s[3]))
#define TestEq4(ch, t, s)  (t=ch&CaseMaskBits, !Product4(t, s))

int main(void) {
  int ch;
  printf("%X\n", CaseMaskBits);
  for (ch = 0; ch < 256; ch++){
    int t;  // temp var for TestEQ4
    while (TestEq4(ch, t, "ELPQ")) {
      printf("%d %c\n", ch, ch);
      break;
    }
  }
  return 0;
}

280           while (TestEq4(ch, t, "ELPQ")) {
00402560:   mov %ebx,%eax
00402562:   and $0xdf,%eax
00402567:   lea -0x4c(%eax),%edx
0040256a:   lea -0x45(%eax),%ecx
0040256d:   imul %edx,%ecx
00402570:   lea -0x50(%eax),%edx
00402573:   sub $0x51,%eax
00402576:   imul %ecx,%edx
00402579:   imul %edx,%eax
0040257c:   test %eax,%eax
0040257e:   jne 0x402555 <main+37>
281             printf("%d %c\n", ch, ch);

【讨论】:

    【解决方案2】:

    如果option/cType 有 @Dieter Lücking

    unsigned char Table[UCHAR_MAX + 1] = {
      fill per needs };
    #define Table_OptionMask 1
    #define Table_cTypeMask 2
    #define Table_nextMask 4
    
    while (!(Table[(unsigned char)option] & Table_OptionMask)) ...
    while (!(Table[(unsigned char)cType] & Table_cTypeMask)) ...
    

    为了更简单的代码维护,在代码启动时通过调用Table_Setup()填充表格。

    static void Table_SetInsensitive(unsigned char *Table, unsigned mask, cnst char *src) {
      while (*src) {
        Table[toupper((unsigned char) *src)] |= mask;
        Table[tolower((unsigned char) *src)] |= mask;
        src++;  
      }
    }
    
    void Table_Setup(void) {
      memset(Table, 0, sizeof Table);
      Table_SetInsensitive(Table, Table_OptionMask, "EDPQ");
      Table_SetInsensitive(Table, Table_cTypeMask, "SLOQ");
      Table_SetInsensitive(Table, Table_cTypeMask, tbd);
    }
    

    【讨论】:

      【解决方案3】:
      const char invalidChars[] = "edpq";
      while (strchr(invalidChars, tolower(option)) != 0) {
          ...
      }
      

      【讨论】:

      • 不是我的反对意见,而是:您似乎在取消引用option,这是问题中的普通char,而不是指针(现已修复)。您还颠倒了字符的含义;我相信它们是有效的选择。您应该注意您的解决方案适用于 C(问题标记为 C 和 C++)。您应该考虑:const char validOptions[] = "edpq"; while (strchr(validOptions, tolower(option)) != 0) 搜索字符串的一半。
      • @JonathanLeffler 我唯一担心的是tolower 方法实际上比同时检查上下选项的成本更高。
      • 测量是解决这个问题的唯一方法。 tolower() 通常映射到对全局数组的引用。您的字符串搜索使用的是一个字符串常量,它也是一个全局数组。它们之间没有太多选择,但是搜索 4 个字符而不是 8 个字符应该是一些边际收益。也就是说,您很难衡量,更不用说注意到差异了。
      • 否;全局数组的长度为 256 字节,并且可以由 tolower(option)global_array[(option)] 直接访问(或多或少 - 有一些技巧与 EOF 等有关,因此它可能是 257 字节和 global_array[(option)+1]),但是要点几乎相同。没有搜索——只是一个直接的数组访问。
      • 您可能错误地使用了tolower;如果optionchar,那么它应该是tolower((unsigned char)option)。我建议在匹配字符串中只使用大写字符而不使用 tolower
      【解决方案4】:

      std::tolower 可以消除一半的条件

      while(std::tolower(option) != 'e' && std::tolower(option) != 'd' && std::tolower(option) != 'p' && std::tolower(option) != 'q')
      

      您也可以使用std::string,它是find 成员函数,例如:

      std::string options = "edpq";
      //...
      while (options.find(std::tolower(option)) == std::string::npos)
      

      【讨论】:

      • 不是我的反对意见,但调用tolower() 一次可能会改善情况......但每次比较调用它可能不太效率。并且字符串选项比原来的看起来更整洁,但可能更慢。
      • 为什么不:std::string options = "edpq"; while (options.find(std:tolower(option)) == std::string::npso) 搜索字符串的一半?
      • @JonathanLeffler 我什至没有想到要这样做。我更改了代码示例。谢谢。
      【解决方案5】:

      您可以初始化一个包含您要匹配的字符的字符串,然后使用find,如果没有找到匹配项,则返回npos

      string match = "SLOQsloq";
      while (match.find(cType) == string::npos)
        ...
      

      【讨论】:

        【解决方案6】:

        您可以通过使用std::set 并检查集合是否包含(或不包含)我们要比较的变量来简化逻辑并使事情更具可读性:

        std::set<char> someChars { 'a', 'b', 'c' };
        if(someChars.find(myChar) != someChars.end()) {
            // myChar is either 'a', 'b', or 'c'
        }
        

        在大多数其他语言中,条件会更简洁地写成 someChars.contains(myChar) 之类的东西(但 C++ 的 set 接口非常小)。

        但是,对于少量比较,您的方法可能更快。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-05-07
          • 1970-01-01
          • 2015-10-08
          • 2017-09-10
          • 2013-08-23
          • 1970-01-01
          • 2022-01-17
          • 2016-09-25
          相关资源
          最近更新 更多