有没有办法缩短这种情况

Is there a way to shorten this while condition?

本文关键字:情况 有没有      更新时间:2023-10-16
while (temp->left->oper == '+' || 
       temp->left->oper == '-' || 
       temp->left->oper == '*' || 
       temp->left->oper == '/' || 
       temp->right->oper == '+' || 
       temp->right->oper == '-' || 
       temp->right->oper == '*' || 
       temp->right->oper == '/')
{
    // do something
}

为清楚起见:temp 是一个指向以下node结构的指针:

struct node
{
    int num;
    char oper;
    node* left;
    node* right;
};

当然,您可以使用一串有效的运算符并搜索它。

#include <cstring>
// : :
const char* ops = "+-*/";
while(strchr(ops, temp->left->oper) || strchr(ops, temp->right->oper))
{
     // do something
}

如果您担心性能,那么也许是表查找:

#include <climits>
// : :
// Start with a table initialized to all zeroes.
char is_op[1 << CHAR_BIT] = {0};
// Build the table any way you please.  This way using a string is handy.
const char* ops = "+-*/";
for (const char* op = ops; *op; op++) is_op[*op] = 1;
// Then tests require no searching
while(is_op[temp->left->oper] || is_op[temp->right->oper])
{
     // do something
}

是的,确实可以!

将有效字符存储到std::array甚至纯数组中,并对其应用标准算法std::any_of以检查条件。

#include <array>     // std::array
#include <algorithm> // std::any_of
static constexpr std::array<char, 4> options{ '+', '-', '*', '/' };
const auto tester = [temp](const char c) { return temp->left->oper == c || temp->right->oper == c; };
const bool isValid = std::any_of(options.cbegin(), options.cend(), tester);
while(isValid) // now the while-loop is simplified to
{
    // do something
}
<小时 />

这可以通过打包到一个函数中来更好地清理,该函数接受要检查node对象。

#include <array>     // std::array
#include <algorithm> // std::any_of
bool isValid(const node *const temp) /* noexcept */
{
   static constexpr std::array<char, 4> options{ '+', '-', '*', '/' };
   const auto tester = [temp](const char c) { return temp->left->oper == c || temp->right->oper == c; };
   return std::any_of(options.cbegin(), options.cend(), tester);
}

可以在while-loop中调用

while (isValid(temp)) // pass the `node*` to be checked
{
    // do something
}

创建一个子函数,

bool is_arithmetic_char(char)
{
// Your implementation or one proposed in another answers.
}

然后:

while (is_arithmetic_char(temp->left->oper)
    || is_arithmetic_char(temp->right->oper))
{
    // do something
}

C 样式:

int cont = 1;
while(cont)
    switch(temp->left->oper) {
    case '+':
    case '-':
    ...
    case '/':
        // Do something
        break;
    default:
        cont = 0;
    }
如果要

声明变量,则可能需要用大括号将// Do something括起来。

您可以构造一个包含选项的字符串并搜索字符:

#include <string>
// ...
for (auto ops = "+-*/"s; ops.find(temp-> left->oper) != std::string::npos ||
                         ops.find(temp->right->oper) != std::string::npos;)
    /* ... */;

"+-*/"s是 C++14 功能。 在 C++14 之前使用std::string ops = "+-*/";

编程是查找冗余并消除冗余的过程。

struct node {
    int num;
    char oper;
    node* left;
    node* right;
};
while (temp->left->oper == '+' || 
       temp->left->oper == '-' || 
       temp->left->oper == '*' || 
       temp->left->oper == '/' || 
       temp->right->oper == '+' || 
       temp->right->oper == '-' || 
       temp->right->oper == '*' || 
       temp->right->oper == '/') {
    // do something
}

这里的"重复单元"是什么?好吧,我看到两个实例

   (something)->oper == '+' || 
   (something)->oper == '-' || 
   (something)->oper == '*' || 
   (something)->oper == '/'

因此,让我们将重复的部分分解为一个函数,这样我们只需要编写一次。

struct node {
    int num;
    char oper;
    node* left;
    node* right;
    bool oper_is_arithmetic() const {
        return this->oper == '+' || 
               this->oper == '-' || 
               this->oper == '*' || 
               this->oper == '/';
    }
};
while (temp->left->oper_is_arithmetic() ||
       temp->right->oper_is_arithmetic()) {
    // do something
}

哒!缩短!
(原始代码:17 行,其中 8 行是循环条件。修改后的代码:18 行,其中 2 行是循环条件。

"+" "-" "*" 和

"/" 是 ASCII 十进制值 42、43、45 和 47因此

#define IS_OPER(x) (x > 41 && x < 48 && x != 44 && x != 46)
while(IS_OPER(temp->left->oper || IS_OPER(temp->right->oper){ /* do something */ }

交易空间与时间的关系,您可以构建两个分别按 temp->left->opertemp->left->oper 索引的"布尔"数组。当满足条件时,相应的数组包含 true,否则包含 false。所以:

while (array1[temp->left->oper] || array1[temp->right->oper]) {
// do something
}

由于左和右的集合看起来相同,因此实际上一个数组就可以了。

初始化将是这样的:

static char array1[256]; // initialized to "all false"

array1['+'] = array1['-'] = array1['*'] = array1['/'] = '01';

array2相似.由于跳转对现代流水线 CPU 不利,您甚至可以使用更大的表,如下所示:

while (array1[temp->left->oper << 8 | temp->right->oper]) {
    // do something
}

但是初始化更棘手:

static char array1[256 * 256]; // initialized to "all false"

void init(char c) {
    for (unsigned char i = 0; i <= 255; ++i) {
        array1[(c << 8) | i] = array1[(i << 8) | c] = '01';
    }
}
init('+');
init('-');
init('*');
init('/');

则表达式来救援!

#include <regex>
while (
    std::regex_match(temp->left->oper, std::regex("[+-*/]")) ||
    std::regex_match(temp->right->oper, std::regex("[+-*/]"))
) { 
// do something
}

说明:正则表达式括号 [] 表示正则表达式"字符类"。这意味着"匹配括号内列出的任何字符"。例如,g[eiou]t匹配"get"、"git"、"got"和"gut",但不会匹配"gat"。反斜杠是必需的,因为加号 (+( 减号 (-( 和星号 (*( 和正斜杠 (/( 在字符类中有意义。

免责声明:我没有时间运行这段代码;你可能不得不调整它,但你明白了。您可能需要声明/将operchar转换为std::string

参考资料
1. http://www.cplusplus.com/reference/regex/regex_match/2. https://www.rexegg.com/regex-quickstart.html3. https://www.amazon.com/Mastering-Regular-Expressions-Jeffrey-Friedl/dp/0596528124/ref=sr_1_1?keywords=regex&qid=1563904113&s=gateway&sr=8-1

将运算符放在unordered_set中将非常高效,并将为运算符提供 O(1( 访问权限。

unordered_set<char> u_set;                                                                                                                                                   
u_set.insert('+');                                                                                                                                                           
u_set.insert('*');                                                                                                                                                           
u_set.insert('/');                                                                                                                                                           
u_set.insert('-');                                                                                                                                                           

if((u_set.find(temp->left->oper) != u_set.end()) || (u_set.find(temp->right->oper) != u_set.end())) {     
                 //do something                                                                                                                
}

Lambda & std::string_view

string_view提供了许多std::string功能,并且可以在文字上进行运算符,并且它不拥有string

对高度本地的代码使用 Lambda 而不是函数,该代码不适用于文件的其余部分。此外,当 lambda 可以捕获变量时,无需传递变量。还可以获得inline好处,而无需为您原本创建的函数指定它。

  • https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rf-capture-vs-overload

  • https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rstr-view

制作char const

  • https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rconst-immutable
auto is_arithm = [](const char c) {
  return std::string_view("+-/*").find_first_of(c) != std::string::npos;
};
while (is_arithm(temp->left->oper) || is_arithm(temp->right->oper)) {
}

您可以将const char c更改为const node *t访问 lambda 中的oper成员。 但这不是一个好主意,因为可以修改left/right的成员temp

auto is_arithm2 = [](const node *t) {
  return std::string_view("+-/*").find_first_of(t->oper) != std::string::npos;
};
while(is_arithm2(temp->left) || is_arithm2(temp->right)){
}
相关文章: