有没有办法缩短这个 while 条件?
Is there a way to shorten this while condition?
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
}
如果您关心性能,那么也许 table 查找:
#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
}
是的,确实可以!
将 valid-characters 存储到 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
}
您可以构造一个包含选项的字符串并搜索字符:
#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 = "+-*/";
。
"+" "-" "*" 和 "/" 是 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 */ }
C-style:
int cont = 1;
while(cont)
switch(temp->left->oper) {
case '+':
case '-':
...
case '/':
// Do something
break;
default:
cont = 0;
}
如果要声明变量,您可能需要用大括号将 // Do something
括起来。
正则表达式来拯救!
#include <regex>
while (
std::regex_match(temp->left->oper, std::regex("[\+\-\*\/]")) ||
std::regex_match(temp->right->oper, std::regex("[\+\-\*\/]"))
) {
// do something
}
说明:正则表达式括号 [] 表示正则表达式 "character class." 这意味着 "match any character listed inside the brackets." 例如,g[eiou]t
将匹配 "get," "git," "got," 和 "gut," 但不是 "gat." 需要反斜杠,因为加号 (+) 减号 (-) 和星号 (*) 和 forward-slash (/) 在字符 class.
免责声明:我没有时间 运行 此代码;您可能需要对其进行调整,但您明白了。您可能需要 declare/convert oper
从 char
到 std::string
。
参考资料
1. http://www.cplusplus.com/reference/regex/regex_match/
2. https://www.rexegg.com/regex-quickstart.html
3.https://www.amazon.com/Mastering-Regular-Expressions-Jeffrey-Friedl/dp/0596528124/ref=sr_1_1?keywords=regex&qid=1563904113&s=gateway&sr=8-1
用 space 与时间进行交易,您可以构建两个 "Boolean" 数组,索引分别为 temp->left->oper
和 temp->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['/'] = '[=12=]1';
与 array2
类似。
由于跳转对现代流水线 CPU 不利,您甚至可以像这样使用更大的 table:
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] = '[=15=]1';
}
}
init('+');
init('-');
init('*');
init('/');
编程就是发现冗余并消除冗余的过程。
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
}
这里的 "repeated unit" 是什么?嗯,我看到
的两个实例
(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行是循环条件。)
将运算符放在 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
:
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
成员。但这不是一个好主意,因为可以修改 temp
的 left
/right
的成员。
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)){
}
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
}
如果您关心性能,那么也许 table 查找:
#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
}
是的,确实可以!
将 valid-characters 存储到 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
}
您可以构造一个包含选项的字符串并搜索字符:
#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 = "+-*/";
。
"+" "-" "*" 和 "/" 是 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 */ }
C-style:
int cont = 1;
while(cont)
switch(temp->left->oper) {
case '+':
case '-':
...
case '/':
// Do something
break;
default:
cont = 0;
}
如果要声明变量,您可能需要用大括号将 // Do something
括起来。
正则表达式来拯救!
#include <regex>
while (
std::regex_match(temp->left->oper, std::regex("[\+\-\*\/]")) ||
std::regex_match(temp->right->oper, std::regex("[\+\-\*\/]"))
) {
// do something
}
说明:正则表达式括号 [] 表示正则表达式 "character class." 这意味着 "match any character listed inside the brackets." 例如,g[eiou]t
将匹配 "get," "git," "got," 和 "gut," 但不是 "gat." 需要反斜杠,因为加号 (+) 减号 (-) 和星号 (*) 和 forward-slash (/) 在字符 class.
免责声明:我没有时间 运行 此代码;您可能需要对其进行调整,但您明白了。您可能需要 declare/convert oper
从 char
到 std::string
。
参考资料
1. http://www.cplusplus.com/reference/regex/regex_match/
2. https://www.rexegg.com/regex-quickstart.html
3.https://www.amazon.com/Mastering-Regular-Expressions-Jeffrey-Friedl/dp/0596528124/ref=sr_1_1?keywords=regex&qid=1563904113&s=gateway&sr=8-1
用 space 与时间进行交易,您可以构建两个 "Boolean" 数组,索引分别为 temp->left->oper
和 temp->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['/'] = '[=12=]1';
与 array2
类似。
由于跳转对现代流水线 CPU 不利,您甚至可以像这样使用更大的 table:
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] = '[=15=]1';
}
}
init('+');
init('-');
init('*');
init('/');
编程就是发现冗余并消除冗余的过程。
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
}
这里的 "repeated unit" 是什么?嗯,我看到
的两个实例 (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行是循环条件。)
将运算符放在 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
:
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
成员。但这不是一个好主意,因为可以修改 temp
的 left
/right
的成员。
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)){
}