programing

compound 'if' 조건을 쓰는 더 짧은 방법이 있습니까?

css3 2023. 10. 17. 20:29

compound 'if' 조건을 쓰는 더 짧은 방법이 있습니까?

대신:

if  ( ch == 'A' || ch == 'B' || ch == 'C' || .....

예를 들어, 다음과 같이 하면 됩니다.

if  ( ch == 'A', 'B', 'C', ...

조건을 요약하는 더 짧은 방법이 있습니까?

strchr() 문자가 목록에 있는지 확인하는 데 사용할 수 있습니다.

const char* list = "ABCXZ";
if (strchr(list, ch)) {
  // 'ch' is 'A', 'B', 'C', 'X', or 'Z'
}

이 경우 당신은 A를 사용할 수 있습니다.switch:

switch (ch) {
case 'A':
case 'B':
case 'C':
    // do something
    break;
case 'D':
case 'E':
case 'F':
    // do something else
    break;
...
}

이것은 사용하는 것보다 약간 더 장황하지만,strchr, 함수 호출은 포함되지 않습니다.C와 C++에도 적용됩니다.

제안한 대체 구문은 쉼표 연산자를 사용하기 때문에 예상대로 작동하지 않습니다.

if  ( ch == 'A', 'B', 'C', 'D', 'E', 'F' )

이것은 먼저 비교합니다.ch.'A'그리고 결과를 버립니다.그리고나서'B'평가 및 폐기된 후'C''F'평가됩니다.그리고나서'F'조건의 값이 됩니다.부울 컨텍스트에서 0이 아닌 값이 참으로 평가된 이후(그리고'F'는 0이 아닙니다. 그러면 위 식은 항상 참이 됩니다.

템플릿을 사용하면 다음과 같은 방법으로 자신을 표현할 수 있습니다.

if (range("A-F").contains(ch)) { ... }

약간의 배관이 필요한데, 도서관에 설치할 수 있습니다.

이것은 실제로 (적어도 gcc와 클랑에서는) 믿을 수 없을 정도로 효율적으로 컴파일됩니다.

#include <cstdint>
#include <tuple>
#include <utility>
#include <iostream>

namespace detail {
    template<class T>
    struct range
    {
        constexpr range(T first, T last)
        : _begin(first), _end(last)
        {}

        constexpr T begin() const { return _begin; }
        constexpr T end() const { return _end; }

        template<class U>
        constexpr bool contains(const U& u) const
        {
            return _begin <= u and u <= _end;
        }

    private:
        T _begin;
        T _end;
    };

    template<class...Ranges>
    struct ranges
    {
        constexpr ranges(Ranges...ranges) : _ranges(std::make_tuple(ranges...)) {}

        template<class U>
        struct range_check
        {
            template<std::size_t I>
            bool contains_impl(std::integral_constant<std::size_t, I>,
                               const U& u,
                               const std::tuple<Ranges...>& ranges) const
            {
                return std::get<I>(ranges).contains(u)
                or contains_impl(std::integral_constant<std::size_t, I+1>(),u, ranges);
            }

            bool contains_impl(std::integral_constant<std::size_t, sizeof...(Ranges)>,
                               const U& u,
                               const std::tuple<Ranges...>& ranges) const
            {
                return false;
            }


            constexpr bool operator()(const U& u, std::tuple<Ranges...> const& ranges) const
            {
                return contains_impl(std::integral_constant<std::size_t, 0>(), u, ranges);
            }
        };

        template<class U>
        constexpr bool contains(const U& u) const
        {
            range_check<U> check {};
            return check(u, _ranges);
        }

        std::tuple<Ranges...> _ranges;
    };
}

template<class T>
constexpr auto range(T t) { return detail::range<T>(t, t); }

template<class T>
constexpr auto range(T from, T to) { return detail::range<T>(from, to); }

// this is the little trick which turns an ascii string into
// a range of characters at compile time. It's probably a bit naughty
// as I am not checking syntax. You could write "ApZ" and it would be
// interpreted as "A-Z".
constexpr auto range(const char (&s)[4])
{
    return range(s[0], s[2]);
}

template<class...Rs>
constexpr auto ranges(Rs...rs)
{
    return detail::ranges<Rs...>(rs...);
}

int main()
{
    std::cout << range(1,7).contains(5) << std::endl;
    std::cout << range("a-f").contains('b') << std::endl;

    auto az = ranges(range('a'), range('z'));
    std::cout << az.contains('a') << std::endl;
    std::cout << az.contains('z') << std::endl;
    std::cout << az.contains('p') << std::endl;

    auto rs = ranges(range("a-f"), range("p-z"));
    for (char ch = 'a' ; ch <= 'z' ; ++ch)
    {
        std::cout << ch << rs.contains(ch) << " ";
    }
    std::cout << std::endl;

    return 0;
}

예상 출력:

1
1
1
1
0
a1 b1 c1 d1 e1 f1 g0 h0 i0 j0 k0 l0 m0 n0 o0 p1 q1 r1 s1 t1 u1 v1 w1 x1 y1 z1 

참고로, 제 원래 답변은 다음과 같습니다.

template<class X, class Y>
bool in(X const& x, Y const& y)
{
    return x == y;
}

template<class X, class Y, class...Rest>
bool in(X const& x, Y const& y, Rest const&...rest)
{
    return in(x, y) or in(x, rest...);
}

int main()
{
    int ch = 6;
    std::cout << in(ch, 1,2,3,4,5,6,7) << std::endl;

    std::string foo = "foo";
    std::cout << in(foo, "bar", "foo", "baz") << std::endl;

    std::cout << in(foo, "bar", "baz") << std::endl;
}

임의의 문자 집합에 대해 문자를 확인해야 할 경우 다음 내용을 작성해 볼 수 있습니다.

std::set<char> allowed_chars = {'A', 'B', 'C', 'D', 'E', 'G', 'Q', '7', 'z'};
if(allowed_chars.find(ch) != allowed_chars.end()) {
    /*...*/
}

이 지나치게 대답된 질문에 대한 또 하나의 대답, 저는 단지 완전성을 위해 포함하고 있는 것입니다.여기에 있는 모든 답변 중에서 응용 프로그램에서 사용할 수 있는 을 찾아야 합니다.

따라서 또 다른 옵션은 룩업 테이블입니다.

// On initialization:
bool isAcceptable[256] = { false };
isAcceptable[(unsigned char)'A'] = true;
isAcceptable[(unsigned char)'B'] = true;
isAcceptable[(unsigned char)'C'] = true;

// When you want to check:
char c = ...;
if (isAcceptable[(unsigned char)c]) {
   // it's 'A', 'B', or 'C'.
}

필요하다면 C 스타일의 정적 캐스트를 비웃어 보세요. 하지만 그들은 그 일을 확실히 해낼 수 있습니다.제 생각에 당신은 당신이 사용할 수 있을 것 같습니다.std::vector<bool>밤에 잠을 못 자게 한다면요이외에도 타입을 사용할 수 있습니다.bool은 그 하지만 당신은 이해합니다.

물론 이것은 예를 들어 번거로워집니다.wchar_t, 멀티바이트 인코딩으로는 사실상 사용할 수 없습니다.하지만 당신을 위해서는char예를 들어, 또는 룩업 테이블에 자신을 빌려주는 모든 것에 대해서는 충분합니다.YMMV.

합니다.strchranswer, C++에서 문자열을 구성하고 문자를 내용과 비교하여 확인할 수 있습니다.

#include <string>
...
std::string("ABCDEFGIKZ").find(c) != std::string::npos;

위의 내용이 반환됩니다.true위해서'F'그리고.'Z'그렇지만false위해서'z'아니면'O' 이 코드는 문자를 연속적으로 표현하는 것을 가정하지 않습니다.

이것은 반환하기 때문에 작동합니다.std::string::npos문자열에서 문자를 찾을 수 없을 때.

콜리루 라이브

편집:

동적 할당을 수반하지 않지만 훨씬 더 긴 코드 조각을 수반하는 또 다른 C++ 방식이 있습니다.

#include <algorithm> // std::find
#include <iterator> // std::begin and std::end
...
char const chars[] = "ABCDEFGIKZ";
return std::find(std::begin(chars), std::end(chars), c) != std::end(chars);

이것은 첫 번째 코드 조각과 유사하게 작동합니다. 지정된 범위를 검색하고 항목을 찾을 수 없는 경우 특정 값을 반환합니다.여기서 특정 값은 범위의 끝입니다.

콜리루 라이브

하나의 옵션은unordered_set 관심있는 캐릭터를 세트에 넣습니다.그러면 문제가 되는 캐릭터의 개수만 확인해보세요.

#include <iostream>
#include <unordered_set>

using namespace std;

int main() {
  unordered_set<char> characters;
  characters.insert('A');
  characters.insert('B');
  characters.insert('C');
  // ...

  if (characters.count('A')) {
    cout << "found" << endl;
  } else {
    cout << "not found" << endl;
  }

  return 0;
}

언어가 아닌 코딩 실습 - 리팩토링으로 문제를 해결할 수 있습니다.

독자들은 이 대답이 매우 특이하다고 생각하겠지만, 리팩토링은 메소드 호출 뒤에 지저분한 코드 조각을 숨길 수 있고, 종종 사용됩니다.그 방법은 나중에 청소하거나 그대로 둘 수 있습니다.

다음 메서드를 생성할 수 있습니다.

private bool characterIsValid(char ch) {
    return (ch == 'A' || ch == 'B' || ch == 'C' || ..... );
}

다음과 같은 짧은 형태로 이 메서드를 호출할 수 있습니다.

if (characterIsValid(ch)) ...

이 메서드를 많은 검사와 함께 재사용하고 어디서나 부울만 반환합니다.

간편하고 효과적인 솔루션을 위해서는memchr():

#include <string.h>

const char list[] = "ABCXZ";
if (memchr(list, ch, sizeof(list) - 1)) {
    // 'ch' is 'A', 'B', 'C', 'X', or 'Z'
}

:memchr()보다 더 적합합니다.strchr()로서 이 일을 위하여strchr()null 문자를 찾을 것입니다.'\0'대부분의 경우 부정확한 문자열의 끝에 있습니다.

목록이 동적이거나 외부적이고 길이가 제공되지 않는 경우strchr()접근하는 것이 더 낫지만, 당신은 확인해야 합니다.ch와는 다릅니다.0~하듯이strchr()문자열 끝에서 찾을 수 있습니다.

#include <string.h>

extern char list[];
if (ch && strchr(list, ch)) {
    // 'ch' is one of the characters in the list
}

보다 효율적이지만 덜 간결한 또 다른 C99 솔루션은 어레이를 사용합니다.

#include <limits.h>

const char list[UCHAR_MAX + 1] = { ['A'] = 1, ['B'] = 1, ['C'] = 1, ['X'] = 1, ['Z'] = 1 };
if (list[(unsigned char)ch]) {
    /* ch is one of the matching characters */
}

그러나 위의 모든 솔루션은 다음을 가정합니다.ch가지기 위해char타이프. 만약에ch다른 유형을 가지고 있고, 그들은 거짓 양성을 받아들입니다.이 문제를 해결하는 방법은 다음과 같습니다.

#include <string.h>

extern char list[];
if (ch == (char)ch && ch && strchr(list, ch)) {
    // 'ch' is one of the characters in the list
}

또한 비교할 때 함정을 조심해야 합니다.unsigned char값:

unsigned char ch = 0xFF;
if (ch == '\xFF') {
    /* test fails if `char` is signed by default */
}
if (memchr("\xFF", ch, 1)) {
    /* test succeeds in all cases, is this OK? */
}

이 특정한 경우에 당신은 다음과 같은 사실을 사용할 수 있습니다.char는 정수이며 범위에 대한 검정입니다.

if(ch >= 'A' && ch <= 'C')
{
    ...
}

그러나 일반적으로 이것은 불행하게도 가능하지 않습니다.코드를 압축하려면 부울 함수를 사용합니다.

if(compare_char(ch))
{
    ...
}

대부분의 현대 시스템에 대한 X-Y 답변은 신경쓰지 않습니다.

오늘날 사용되는 거의 모든 문자 인코딩이 알파벳을 순차적으로 정렬된 연속 블록에 저장한다는 점을 활용할 수 있습니다.A 다음은 B, B 다음은 C 등...Z까지이를 통해 글자를 숫자로 변환하기 위해 글자에 간단한 수학 트릭을 사용할 수 있습니다.예를 들어 문자 C에서 문자 A를 뺀 'C' - 'A'는 c와 a 사이의 거리인 2입니다.

일부 문자 집합 EBCDIC는 위의 주석에서 설명했지만, 여기서 논의할 범위를 벗어난 이유로 순차적이거나 연속적이지 않습니다.드물지만 가끔 발견할 수 있을 겁니다.그럴 때는...여기 있는 다른 답변들은 대부분 적절한 해결책을 제시합니다.

이를 통해 간단한 배열로 문자 값을 문자에 매핑할 수 있습니다.

//                    a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p, q,r,s,t,u,v,w,x,y, z
int lettervalues[] = {1,3,3,2,1,4,2,4,1,8,5,1,3,1,1,3,10,1,1,1,1,4,4,8,4,10};

그렇게'c' - 'a'2와lettervalues[2]결과적으로 3, 즉 C의 문자 값이 됩니다.

문장이나 조건부 논리가 항상 필요하다면 아니요.당신이 해야 할 디버깅 작업은 증명 읽기 뿐입니다.lettervalues정확한 값을 입력했는지 확인합니다.

C++에서 더 공부하면 배울 것입니다.lettervalues그래야 한다static(현재 번역 단위 전용 액세스) 및const(cannot 변경), 가능한 경우constexpr(컴파일시 변경 및 수정할 수 없습니다) 제가 무슨 말을 하는지 걱정하지 제가 무슨 말을 하는지 모르시더라도 걱정하지 마세요.나중에 3개 다 커버해요.그렇지 않으면 구글에 검색합니다.모두 매우 유용한 도구입니다.

이 배열을 사용하는 것은 다음과 같이 간단할 수 있습니다.

int ComputeWordScore(std::string in)
{
    int score = 0;
    for (char ch: in) // for all characters in string
    {
        score += lettervalues[ch - 'a'];
    }
    return score;
}

하지만 여기에는 두 가지 치명적인 맹점이 있습니다.

첫번째는 대문자 입니다. '는 '가 아니고, 'A'는 'A'가 'A'는고인, 'A' A인 랜드하지만,'A'-'a'0이 아닙니다.이는 를 사용하거나 모든 입력을 알려진 경우로 변환하여 해결할 수 있습니다.

int ComputeWordScore(std::string in)
{
    int score = 0;
    for (char ch: in) // for all characters in string
    {
        score += lettervalues[std::tolower(ch) - 'a'];
    }
    return score;
}

다른 하나는 문자가 아닌 문자의 입력입니다.예를 들어 '1'입니다.'a' - '1'배열 인덱스가 배열에 없습니다.큰일이네요.운이 좋으면 프로그램이 중단되지만 프로그램이 작동하는 것처럼 보이는 것을 포함하여 모든 일이 일어날 수 있습니다.자세한 내용은 정의되지 않은 동작에 대해 읽어 보십시오.

다행히도 이것에는 간단한 해결책도 있습니다. 좋은 입력을 위해 점수만 계산해야 한다는 것입니다.에서 유효한 알파벳 문자를 테스트할 수 있습니다.

int ComputeWordScore(std::string in)
{
    int score = 0;
    for (char ch: in) // for all characters in string
    {
        if (std::isalpha(ch))
        {
            score += lettervalues[std::tolower(ch) - 'a'];
        }
        else
        {
            // do something that makes sense here. 
        }
    }
    return score;
}

것은 .return -1;. -1은 불가능한 단어 점수이기 때문에 전화하는 사람은 누구나ComputeWordScore는 -1을 테스트하고 사용자의 입력을 거부할 수 있습니다.그들이 그것으로 무엇을 하는지는 그렇지 않습니다.ComputeWordScore의 문제. 당신이 함수를 만들 수 있는 바보일수록 좋고, 오류는 결정을 내리는 데 필요한 모든 정보를 가진 가장 가까운 코드 조각에 의해 처리되어야 합니다.일반적으로 당신이 기능을 만들 수 있는 더 나은, 그리고 오류는 결정을 내리는 데 필요한 모든 정보를 가지고 있는 가장 가까운 코드 조각에 의해 처리되어야 합니다.이 경우, 문자열에서 읽혀진 것이 무엇이든 간에 나쁜 문자열로 무엇을 할지를 결정하는 임무를 수행할 것입니다.ComputeWordScore단어 점수를 계속 계산할 수 있습니다.

대부분의 terse version은 다루었으므로, 최적화된 사례들을 도우미 매크로 몇 가지로 다루어서 조금 더 terse로 만들어 보겠습니다.

마침 범위가 길이당 비트 수 내에 있으면 비트 마스크를 사용하여 모든 상수를 조합할 수 있으며 상수 비트 마스크를 사용하여 비트 값이 범위에 속하는지 확인하고 변수의 비트 마스크가 0이 아님을 확인할 수 있습니다.

/* This macro assumes the bits will fit in a long integer type,
 * if it needs to be larger (64 bits on x32 etc...),
 * you can change the shifted 1ULs to 1ULL or if range is > 64 bits,
 * split it into multiple ranges or use SIMD
 * It also assumes that a0 is the lowest and a9 is the highest,
 * You may want to add compile time assert that:
 * a9 (the highest value) - a0 (the lowest value) < max_bits
 * and that a1-a8 fall within a0 to a9
 */
#define RANGE_TO_BITMASK_10(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9) \
  (1 | (1UL<<((a1)-(a0))) | (1UL<<((a2)-(a0))) | (1UL<<((a3)-(a0))) | \
  (1UL<<((a4)-(a0))) | (1UL<<((a5)-(a0))) | (1UL<<((a6)-(a0))) | \
  (1UL<<((a7)-(a0))) | (1UL<<((a8)-(a0))) | (1UL<<((a9)-(a0))) )

/*static inline*/ bool checkx(int x){
    const unsigned long bitmask = /* assume 64 bits */
        RANGE_TO_BITMASK_10('A','B','C','F','G','H','c','f','y','z');
    unsigned temp = (unsigned)x-'A';
    return ( ( temp <= ('z'-'A') ) && !!( (1ULL<<temp) & bitmask ) );
}

모든 a# 값은 상수이므로 컴파일 시 1비트 마스크로 결합됩니다.그러면 범위에 대해 1 뺄셈과 1을 비교하고, 1 쉬프트와 1 비트 단위로 1 비트를 남깁니다.컴파일러가 더 이상 최적화할 수 없는 한, clang can(비트 테스트 명령어 BTQ를 사용함):

checkx:                                 # @checkx
        addl    $-65, %edi
        cmpl    $57, %edi
        ja      .LBB0_1
        movabsq $216172936732606695, %rax # imm = 0x3000024000000E7
        btq     %rdi, %rax
        setb    %al
        retq
.LBB0_1:
        xorl    %eax, %eax
        retq

C 쪽에서는 코드가 더 많아 보일 수 있지만 최적화를 원하는 경우 조립 쪽에서는 가치가 있을 것으로 보입니다.저는 누군가가 매크로를 가지고 창의력을 발휘해서 이 "개념 증명"보다 실제 프로그래밍 상황에서 더 유용하게 사용할 수 있습니다.

매크로로서 조금 복잡해지므로, 여기 C99 룩업 테이블을 설정하기 위한 대체 매크로 세트가 있습니다.

#include <limits.h>
#define INIT_1(v,a) [ a ] = v
#define INIT_2(v,a,...) [ a ] = v, INIT_1(v, __VA_ARGS__)
#define INIT_3(v,a,...) [ a ] = v, INIT_2(v, __VA_ARGS__)
#define INIT_4(v,a,...) [ a ] = v, INIT_3(v, __VA_ARGS__)
#define INIT_5(v,a,...) [ a ] = v, INIT_4(v, __VA_ARGS__)
#define INIT_6(v,a,...) [ a ] = v, INIT_5(v, __VA_ARGS__)
#define INIT_7(v,a,...) [ a ] = v, INIT_6(v, __VA_ARGS__)
#define INIT_8(v,a,...) [ a ] = v, INIT_7(v, __VA_ARGS__)
#define INIT_9(v,a,...) [ a ] = v, INIT_8(v, __VA_ARGS__)
#define INIT_10(v,a,...) [ a ] = v, INIT_9(v, __VA_ARGS__)
#define ISANY10(x,...) ((const unsigned char[UCHAR_MAX+1]){ \
                          INIT_10(-1, __VA_ARGS__) \
                       })[x]

bool checkX(int x){
  return ISANY10(x,'A','B','C','F','G','H','c','f','y','z');
}

이 메서드는 (일반적으로) 256 바이트 테이블과 gcc에서 다음과 같은 것으로 줄이는 룩업을 사용합니다.

checkX:
        movslq  %edi, %rdi  # x, x
        cmpb    $0, C.2.1300(%rdi)      #, C.2
        setne   %al   #, tmp93
        ret

참고: Clang은 각 함수 호출에서 스택의 내부 함수에 발생하는 고정 테이블을 설정하기 때문에 이 방법의 룩업 테이블에서도 마찬가지로 동작하지 않습니다. 따라서 INIT_10을 사용하여 a를 초기화해야 합니다.static const unsigned char [UCHAR_MAX+1]gcc와 유사한 최적화를 달성하기 위해 함수 밖에서.

언급URL : https://stackoverflow.com/questions/39150884/is-there-a-shorter-way-to-write-compound-if-conditions