동등성이 많은 if (..) 문을 작성하는 간결한 방법
다음과 같은 코드를 작성하는 더 좋은 방법이 있습니까?
if (var == "first case" or var == "second case" or var == "third case" or ...)
Python에서는 다음과 같이 작성할 수 있습니다.
if var in ("first case", "second case", "third case", ...)
또한 좋은 옵션 목록을 쉽게 전달할 수있는 기회를 제공합니다.
good_values = "first case", "second case", "third case"
if var in good_values
이것은 단지 예일뿐입니다. 유형은 var
문자열과 다를 수 있지만 대체 ( or
) 비교 ( ==
) 에만 관심이 있습니다. 옵션 목록은 컴파일 타임에 알려진 반면 var
non- const
일 수 있습니다 .
프로 보너스 :
- 게으름
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");
나는 그것을 실제로 테스트하지 않았고, 아이디어는 Alexandrescu의 'Variadic templates are funadic'토크에서 나왔습니다. 따라서 세부 사항 (및 적절한 구현)을 보려면 그것을보십시오.
편집 : C ++ 17에서는 멋진 폴드 표현식 구문 을 도입했습니다.
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
알고리즘은 잘 여기에 합리적으로 일할 수 :
#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
최소한의 필수 컨텍스트 로 제한 할 수 있습니다 .)
또는 래퍼 템플릿을 만듭니다.
#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; });
자, Radical Language Modification 을 원합니다 . 특히, 고유 한 연산자를 작성하려고합니다. 준비된?
통사론
C 및 C ++ 스타일 목록을 사용하도록 구문을 수정하겠습니다.
if (x in {x0, ...}) ...
또한, 우리는 우리의 새로운 드리겠습니다 에서의 운영자가되는 모든 컨테이너에 적용 begin()
하고 end()
정의된다 :
if (x in my_vector) ...
한 가지주의 할 점이 있습니다. 실제 연산자가 아니므로 항상 고유 한 표현식이므로 괄호로 묶어야합니다.
bool ok = (x in my_array);
my_function( (x in some_sequence) );
코드
가장 먼저 알아야 할 것은 RLM에는 종종 매크로 및 연산자 남용이 필요하다는 것입니다. 다행히도 단순한 회원 조건 자의 경우 남용은 실제로 그렇게 나쁘지 않습니다.
#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
용법
큰! 이제 in 연산자가 유용 할 것으로 기대하는 모든 방식으로 사용할 수 있습니다 . 특정 관심사에 따라 예 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) 연산자를 추가 할 수 있습니다 (새 썽크 컨테이너 유형 추가). 썽크 컨테이너를 네임 스페이스에 래핑 할 수 있습니다 (좋은 생각). O (n) 검색 대신 멤버 함수 std::set
를 사용하는 것과 같은 것에 특화 할 수 있습니다 .count()
. 기타.
다른 관심사
const
vsmutable
: 문제가 아닙니다. 둘 다 운영자와 함께 사용할 수 있습니다.- 의 게으름
or
: 기술적으로는,or
입니다 하지 가 단락되어, 게으른.std::find()
또한 알고리즘 같은 방법으로 단락. - 컴파일 타임 루프 언 롤링 : 여기에서는 실제로 적용 할 수 없습니다. 원래 코드는 루프를 사용하지 않았습니다. 하지만
std::find()
발생할 수있는 모든 루프 언 롤링은 컴파일러에 달려 있습니다. - 다른 연산자로 쉽게 확장 할 수 있습니다
==
. 이것은 실제로 별도의 문제입니다. 더 이상 단순한 멤버십 술어를 보지 않고 기능적 폴드 필터를 고려하고 있습니다. 이를 수행하는 알고리즘을 만드는 것은 전적으로 가능하지만 표준 라이브러리는any_of()
정확히 수행하는 기능을 제공합니다 . (우리의 RLM 'in'연산자만큼 예쁘지 않습니다. 즉, C ++ 프로그래머라면 누구나 쉽게 이해할 수 있습니다. 이러한 답변은 이미 여기에서 제공되었습니다.)
도움이 되었기를 바랍니다.
첫째,
for
가장 쉽고 가장 읽기 쉬운 솔루션 인 루프를 사용하는 것이 좋습니다 .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";
}
}
You may use std::set to test if var belongs to it. (Compile with c++11 enabled)
#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;
}
The advantage is that std::set<std::string>::count
works in O(log(n))
time (where is n
is number of strings to test) comparing to non compact if
witch is O(n)
in general. The disadvantage is that construction of the set takes O(n*log(n))
. So, construct it once, like:
static std::set<std::string> the_set = {"abc", "def", "ghi"};
But, IMO it would be better to leave the condition as is, unless it contains more than 10 strings to check. The performance advantages of using std::set for such a test appears only for big n
. Also, simple non compact if
is easier to read for average c++ developer.
The closest thing would be something like:
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();
}
We need to take an argument of type initializer_list<U>
so that we can pass in a braced-init-list like {a,b,c}
. This copies the elements, but presumably we're going doing this because we're providing literals so probably not a big deal.
We can use that like so:
std::string var = "hi";
bool b = in(var, {"abc", "def", "ghi", "hi"});
std::cout << b << std::endl; // true
If you have access to C++14 (not sure if this works with C++11) you could write something like this:
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; });
};
A call would look like this:
std::string test_case = ...;
if (is_one_of<std::string>(test_case, { "first case", "second case", "third case" })) {...}
or like this
std::string test_case = ...;
std::vector<std::string> allowedCases{ "first case", "second case", "third case" };
if (is_one_of<std::string>(test_case, allowedCases)) {...}
If you don't like to "wrap" the allowed cases into a list type you can also write a little helper function like this:
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...});
};
This will allow you to call it like this:
std::string test_case = ...;
if (is_one_of<std::string>(test_case, "first case", "second case", "third case" )) {...}
Worth noting that in most Java and C++ code I've seen, listing 3 or so conditionals out is the accepted practice. It's certainly more readable than "clever" solutions. If this happens so often it's a major drag, that's a design smell anyway and a templated or polymorphic approach would probably help avoid this.
So my answer is the "null" operation. Just keep doing the more verbose thing, it's most accepted.
You could use a switch case. Instead of having a list of separate cases you could have :
include
using namespace 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;
}
So you can group your results together: A, B and C will output "well done". I took this example from Tutorials Point: http://www.tutorialspoint.com/cplusplus/cpp_switch_statement.htm
ReferenceURL : https://stackoverflow.com/questions/35984196/compact-way-to-write-if-statement-with-many-equalities
'Programing' 카테고리의 다른 글
mockito에서 doThrow () doAnswer () doNothing () 및 doReturn () 사용법 (0) | 2021.01.07 |
---|---|
해결을 시작하지 않고 (ES6) 약속 만들기 (0) | 2021.01.07 |
ffmpeg가 진행률 표시 줄을 표시 할 수 있습니까? (0) | 2021.01.07 |
C #에서 암호화 보안 의사 난수를 생성하려면 어떻게해야합니까? (0) | 2021.01.07 |
ON 절의 MySQL 알 수없는 열 (0) | 2021.01.07 |