紧凑的方式来写如果(..)语句与许多平等
有没有更好的方式来编写这样的代码:
if (var == "first case" or var == "second case" or var == "third case" or ...)
在Python中,我可以写:
if var in ("first case", "second case", "third case", ...)
这也让我有机会轻松通过好的select列表:
good_values = "first case", "second case", "third case" if var in good_values
这只是一个例子: var
的types可能不同于string,但我只对替代( or
)比较感兴趣( ==
)。 var
可以是非常量,而选项列表在编译时是已知的。
专业奖金:
- 懒惰的
or
- 编译时间循环展开
- 容易扩展到其他运营商比
==
如果你想扩大编译时间,你可以使用这样的东西
template<class T1, class T2> bool isin(T1&& t1, T2&& t2) { return t1 == t2; } template<class T1, class T2, class... Ts> bool isin(T1&& t1 , T2&& t2, T2&&... ts) { return t1 == t2 || isin(t1, ts...); } std::string my_var = ...; // somewhere in the code ... bool b = isin(my_var, "fun", "gun", "hun");
我没有真正testing它,这个想法来自Alexandrescu的“可变模板是有趣的”谈话。 所以细节(和适当的实施)注意。
编辑:在C ++ 17中,他们引入了一个很好的折叠expression式语法
template<typename... Args> bool all(Args... args) { return (... && args); } bool b = all(true, true, true, false); // within all(), the unary left fold expands as // return ((true && true) && true) && false; // b is false
any_of
algorithm可以在这里合理地工作:
#include <algorithm> #include <initializer_list> auto tokens = { "abc", "def", "ghi" }; bool b = std::any_of(tokens.begin(), tokens.end(), [&var](const char * s) { return s == var; });
(您可能希望将tokens
的范围限制在最小的所需上下文中。)
或者你创build一个包装模板:
#include <algorithm> #include <initializer_list> #include <utility> template <typename T, typename F> bool any_of_c(const std::initializer_list<T> & il, F && f) { return std::any_of(il.begin(), il.end(), std::forward<F>(f)); }
用法:
bool b = any_of_c({"abc", "def", "ghi"}, [&var](const char * s) { return s == var; });
好的,那么你需要激进语言修改 。 具体来说,你想创build自己的运营商。 准备?
句法
我要修改语法来使用C和C ++风格的列表:
if (x in {x0, ...}) ...
此外,我们将让我们的new in运算符适用于定义了begin()
和end()
任何容器:
if (x in my_vector) ...
有一个警告:它不是一个真正的操作符,所以它必须总是加括号,因为它是自己的expression式:
bool ok = (x in my_array); my_function( (x in some_sequence) );
代码
首先要注意的是,RLM通常需要一些macros观和操作员的滥用。 幸运的是,对于一个简单的成员谓词,滥用实际上并没有那么糟糕。
#ifndef DUTHOMHAS_IN_OPERATOR_HPP #define DUTHOMHAS_IN_OPERATOR_HPP #include <algorithm> #include <initializer_list> #include <iterator> #include <type_traits> #include <vector> //---------------------------------------------------------------------------- // The 'in' operator is magically defined to operate on any container you give it #define in , in_container() = //---------------------------------------------------------------------------- // The reverse-argument membership predicate is defined as the lowest-precedence // operator available. And conveniently, it will not likely collide with anything. template <typename T, typename Container> typename std::enable_if <!std::is_same <Container, T> ::value, bool> ::type operator , ( const T& x, const Container& xs ) { using std::begin; using std::end; return std::find( begin(xs), end(xs), x ) != end(xs); } template <typename T, typename Container> typename std::enable_if <std::is_same <Container, T> ::value, bool> ::type operator , ( const T& x, const Container& y ) { return x == y; } //---------------------------------------------------------------------------- // This thunk is used to accept any type of container without need for // special syntax when used. struct in_container { template <typename Container> const Container& operator = ( const Container& container ) { return container; } template <typename T> std::vector <T> operator = ( std::initializer_list <T> xs ) { return std::vector <T> ( xs ); } }; #endif
用法
大! 现在我们可以用你期望在操作符中有用的所有方法来使用它。 根据您的特殊兴趣,请参阅示例3:
#include <iostream> #include <set> #include <string> using namespace std; void f( const string& s, const vector <string> & ss ) { cout << "nope\n\n"; } void f( bool b ) { cout << "fooey!\n\n"; } int main() { cout << "I understand three primes by digit or by name.\n" "Type \"q\" to \"quit\".\n\n"; while (true) { string s; cout << "s? "; getline( cin, s ); // Example 1: arrays const char* quits[] = { "quit", "q" }; if (s in quits) break; // Example 2: vectors vector <string> digits { "2", "3", "5" }; if (s in digits) { cout << "a prime digit\n\n"; continue; } // Example 3: literals if (s in {"two", "three", "five"}) { cout << "a prime name!\n\n"; continue; } // Example 4: sets set <const char*> favorites{ "7", "seven" }; if (s in favorites) { cout << "a favorite prime!\n\n"; continue; } // Example 5: sets, part deux if (s in set <string> { "TWO", "THREE", "FIVE", "SEVEN" }) { cout << "(ouch! don't shout!)\n\n"; continue; } // Example 6: operator weirdness if (s[0] in string("014") + "689") { cout << "not prime\n\n"; continue; } // Example 7: argument lists unaffected f( s, digits ); } cout << "bye\n"; }
潜在的改进
总有一些事情可以做,以改善你的特定目的的代码。 你可以添加一个ni (not-in)运算符(添加一个新的thunk容器types)。 你可以将thunk容器包装在一个命名空间中(一个好主意)。 你可以专注于像std::set
这样的东西来使用.count()
成员函数,而不是O(n)search。 等等。
你的其他问题
-
const
与mutable
:不是一个问题; 两者都可以与操作员一起使用 - 懒惰
or
:技术上or
不懒惰,它是短路的。std::find()
algorithm也以相同的方式短路。 - 编译时间循环展开:这里不适用。 你原来的代码没有使用循环; 而
std::find()
时候,任何可能发生的循环展开都取决于编译器。 - 容易扩展到除了
==
以外的其他操作符:这实际上是一个单独的问题; 你不再看一个简单的成员谓词,但现在正在考虑一个function折叠filter。 完全可以创build一个这样的algorithm,但标准库提供了any_of()
函数,完全可以。 (它不如我们在运算符中的RLM那样漂亮,也就是说,任何C ++程序员都会很容易理解,这里已经提到了这样的答案。
希望这可以帮助。
首先,我推荐使用
for
循环,这是最简单和最可读的解决scheme:for (i = 0; i < n; i++) { if (var == eq[i]) { // if true break; } }
但是,也可以使用其他一些方法,例如std::all_of
, std::any_of
, std::none_of
(在#include <algorithm>
)。
让我们看看包含所有上述关键字的简单示例程序
#include <vector> #include <numeric> #include <algorithm> #include <iterator> #include <iostream> #include <functional> int main() { std::vector<int> v(10, 2); std::partial_sum(v.cbegin(), v.cend(), v.begin()); std::cout << "Among the numbers: "; std::copy(v.cbegin(), v.cend(), std::ostream_iterator<int>(std::cout, " ")); std::cout << '\\n'; if (std::all_of(v.cbegin(), v.cend(), [](int i){ return i % 2 == 0; })) { std::cout << "All numbers are even\\n"; } if (std::none_of(v.cbegin(), v.cend(), std::bind(std::modulus<int>(), std::placeholders::_1, 2))) { std::cout << "None of them are odd\\n"; } struct DivisibleBy { const int d; DivisibleBy(int n) : d(n) {} bool operator()(int n) const { return n % d == 0; } }; if (std::any_of(v.cbegin(), v.cend(), DivisibleBy(7))) { std::cout << "At least one number is divisible by 7\\n"; } }
你可以使用std :: set来testingvar是否属于它。 (使用c ++ 11编译)
#include <iostream> #include <set> int main() { std::string el = "abc"; if (std::set<std::string>({"abc", "def", "ghi"}).count(el)) std::cout << "abc belongs to {\"abc\", \"def\", \"ghi\"}" << std::endl; return 0; }
优点是std::set<std::string>::count
在O(log(n))
时间(其中n
是要testing的string的数量)中工作, if
巫婆是O(n)
一般。 缺点是该集合的构造需要O(n*log(n))
。 所以,build造一次,如:
static std::set<std::string> the_set = {"abc", "def", "ghi"};
但是,国际海事组织最好是保持现状,除非包含10个以上的string来检查。 使用std :: set进行这种testing的性能优势只出现在big n
。 另外,简单的非紧凑if
更容易阅读一般的C ++开发人员。
最接近的东西会是这样的:
template <class K, class U, class = decltype(std::declval<K>() == std::declval<U>())> bool in(K&& key, std::initializer_list<U> vals) { return std::find(vals.begin(), vals.end(), key) != vals.end(); }
我们需要一个types为initializer_list<U>
的参数,以便我们可以像{a,b,c}
那样传递一个braced-init-list 。 这复制元素,但大概我们要这样做,因为我们提供文字,所以可能没有什么大不了的。
我们可以这样使用:
std::string var = "hi"; bool b = in(var, {"abc", "def", "ghi", "hi"}); std::cout << b << std::endl; // true
如果你有权访问C ++ 14(不确定这是否适用于C ++ 11),你可以这样写:
template <typename T, typename L = std::initializer_list<T>> constexpr bool is_one_of(const T& value, const L& list) { return std::any_of(std::begin(list), std::end(list), [&value](const T& element) { return element == value; }); };
一个电话会看起来像这样:
std::string test_case = ...; if (is_one_of<std::string>(test_case, { "first case", "second case", "third case" })) {...}
或者像这样
std::string test_case = ...; std::vector<std::string> allowedCases{ "first case", "second case", "third case" }; if (is_one_of<std::string>(test_case, allowedCases)) {...}
如果你不喜欢把允许的例子“包装”成列表types,你也可以写一个这样的辅助函数:
template <typename T, typename...L> constexpr bool is_one_of(const T& value, const T& first, const L&... next) //First is used to be distinct { return is_one_of(value, std::initializer_list<T>{first, next...}); };
这将允许你这样称呼它:
std::string test_case = ...; if (is_one_of<std::string>(test_case, "first case", "second case", "third case" )) {...}
完整的例子在Coliru
值得注意的是,在我见过的大多数Java和C ++代码中,列出3个左右的条件是可以接受的做法。 它比“聪明”的解决scheme更可读。 如果这种情况经常发生,这是一个主要的阻力,无论如何,这是一个devise气味,模板或多态的方法可能有助于避免这种情况。
所以我的答案是“空”操作。 只是继续做更详细的事情,这是最被接受的。
你可以使用开关盒。 而不是有一个单独的案件列表,你可以有:
包括
使用命名空间std;
int main(){char grade ='B';
switch(grade) { case 'A' : case 'B' : case 'C' : cout << "Well done" << endl; break; case 'D' : cout << "You passed" << endl; break; case 'F' : cout << "Better try again" << endl; break; default : cout << "Invalid grade" << endl; } cout << "Your grade is " << grade << endl; return 0;
}
所以你可以将结果分组在一起:A,B和C将输出“做得好”。 我从“教程点”中获取了这个示例: http : //www.tutorialspoint.com/cplusplus/cpp_switch_statement.htm