35 votes

Existe-t-il un moyen plus court d'écrire des conditions composées "si" ?

Juste au lieu de de :

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

Par exemple, pour le faire comme :

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

Y a-t-il même un plus court manière de résumer les conditions ?

85voto

usr Points 13665

strchr() peut être utilisé pour voir si le caractère est dans une liste.

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

39voto

dbush Points 8590

Dans ce cas, vous pouvez utiliser un switch :

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

Bien que cela soit légèrement plus verbeux que l'utilisation de strchr il n'y a pas d'appel de fonction. Elle fonctionne également pour le C et le C++.

Notez que la syntaxe alternative que vous avez suggérée ne fonctionnera pas comme vous l'attendez en raison de l'utilisation de l'opérateur virgule :

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

Cette première comparaison ch a 'A' et rejette ensuite le résultat. Ensuite, 'B' est évaluée et rejetée, alors 'C' et ainsi de suite jusqu'à ce que 'F' est évaluée. Ensuite, 'F' devient la valeur de la conditionnelle. Puisque toute valeur non nulle évaluée à true dans un contexte booléen (et 'F' est non nulle), alors l'expression ci-dessus sera toujours vraie.

33voto

Richard Hodges Points 1972

Les modèles nous permettent de nous exprimer de cette manière :

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

Cela nécessite un peu de plomberie, que vous pouvez mettre dans une bibliothèque.

En fait, la compilation est incroyablement efficace (au moins sur gcc et clang).

#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;
}

résultat attendu :

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 

Pour référence, voici ma réponse initiale :

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;
}

14voto

Xirema Points 677

Si vous devez vérifier un caractère par rapport à un ensemble arbitraire de caractères, vous pouvez essayer d'écrire ceci :

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

14voto

Jason C Points 14927

Encore une réponse à cette question trop souvent posée, que j'inclus juste pour être complète. Entre toutes les réponses ici, vous devriez trouver quelque chose qui fonctionne dans votre application.

Une autre option est donc une table de consultation :

// 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'.
}

Vous pouvez vous moquer des moules statiques de style C, mais ils font le travail. Je suppose que vous pourriez utiliser un std::vector<bool> si les réseaux vous empêchent de dormir la nuit. Vous pouvez également utiliser des types autres que bool . Mais vous comprenez l'idée.

Il est évident que cela devient encombrant avec, par exemple, les éléments suivants wchar_t et pratiquement inutilisable avec les codages multi-octets. Mais pour votre char par exemple, ou pour tout ce qui se prête à une table de consultation, il fera l'affaire. YMMV.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X