紧凑的方式来写如果(..)语句与许多平等

有没有更好的方式来编写这样的代码:

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_ofalgorithm可以在这里合理地工作:

 #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。 等等。

你的其他问题

  • constmutable :不是一个问题; 两者都可以与操作员一起使用
  • 懒惰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_ofstd::any_ofstd::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>::countO(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