34 votes

Raccourcir la condition pour vérifier que x n'est pas l'un des quatre nombres

Y a-t-il un moyen de raccourcir la condition de cette déclaration if ?

int x;
if (x != 3 && x != 8 && x != 87 && x != 9){
  SomeStuff();
}

Je pense à quelque chose du genre :

if (x != 3, 8, 87, 9) {}

Mais j'ai essayé et ça ne marche pas. Dois-je simplement l'écrire de la manière longue ?

0 votes

Est-ce qu'il y a un motif mathématique entre les nombres que vous voulez tester? (Je n'en vois pas un pour l'exemple de 3,8,87,9)

1 votes

@Superlokkus, je ne pense pas, comme l'a mentionné l'auteur du sujet dans l'un des commentaires ci-dessous.

8 votes

if (((x - 3) * (x - 8) * (x - 87) * (x - 9)) != 0) devrait utiliser moins de conditions, mais peut échouer en cas de débordement.

43voto

Alexis Pierru Points 287

Si vous voulez savoir si un entier se trouve dans un ensemble donné d'entiers, utilisez std::set:

std::set accept { 1, 4, 6, 8, 255, 42 };
int x = 1;

if (!accept.count(x))
{
    // ...
}

3 votes

Si la condition est invariante, la déclaration comme statique fait en sorte que le accept ne soit créé qu'une seule fois.

2 votes

La perte de performance causée par ceci monte en flèche... -1

0 votes

@Mehrdad: Dépend de ce que vous voulez faire, celui-ci est plus flexible. Voir la version Lingxi pour une version à temps de compilation.

32voto

1201ProgramAlarm Points 4465

Juste pour être exhaustif, je vais proposer d'utiliser un switch :

switch (x) {
    case 1:
    case 2:
    case 37:
    case 42:
        break;
    default:
        SomeStuff();
        break;
}

Bien que ce soit assez verbeux, cela ne évalue x qu'une seule fois (s'il s'agit d'une expression) et génère probablement le code le plus efficace parmi toutes les solutions.

2 votes

Vous n'avez même pas besoin d'une pause ; si vous mettez d'abord le cas par défaut

1 votes

Extraire l'interrupteur vers une fonction (static inline) et utiliser return true ou return false. Ensuite, remplacer le code par if (matches(x)) SomeStuff();

1 votes

@JerryJeremiah une étiquette de cas doit être appliquée à une déclaration. Si vous avez commandé les cas numérotés en dernier et avez supprimé cette pause, vous devriez tout de même laisser une déclaration vide (un point-virgule). Alternativement, vous pourriez simplement supprimer la pause du cas par défaut sans réarranger les cas.

29voto

Lingxi Points 163

Voici ma solution en utilisant un modèle varadic. Les performances d'exécution sont aussi efficaces que d'écrire manuellement x != 3 && x != 8 && x != 87 && x != 9.

template 
bool not_equal(const T& t, const U& u) {
  return t != u;
}

template 
bool not_equal(const T& t, const U& u, const Vs&... vs) {
  return t != u && not_equal(t, vs...);
}

int main() {
  std::cout << not_equal( 3, 3, 8, 87, 9) << std::endl;
  std::cout << not_equal( 8, 3, 8, 87, 9) << std::endl;
  std::cout << not_equal(87, 3, 8, 87, 9) << std::endl;
  std::cout << not_equal( 9, 3, 8, 87, 9) << std::endl;
  std::cout << not_equal(10, 3, 8, 87, 9) << std::endl;
}

Depuis C++17, l'implémentation peut être simplifiée en utilisant des expressions fold:

template 
bool not_equal(const T& t, const Vs&... vs) {
  return ((t != vs) && ...);
}

3 votes

retourner ((t != us) && ...); en c++1z.

0 votes

@Jarod42 Pourriez-vous en dire plus? Je ne saisis pas très bien votre idée.

1 votes

J'ai dit que le code peut être simplifié en c++1z Demo

14voto

DarioP Points 1350

Que diriez-vous de ceci:

#include <iostream>
#include <initializer_list>
#include <algorithm>

template <typename T>
bool in(const T t, const std::initializer_list<T> & l) {
    return std::find(l.begin(), l.end(), t) != l.end();
}

int main() {
  std::cout << !in(3, {3, 8, 87, 9}) << std::endl;
  std::cout << !in(87, {3, 8, 87, 9}) << std::endl;
  std::cout << !in(10, {3, 8, 87, 9}) << std::endl;
}

ou en surchargeant operator!=:

template<typename T>
bool operator!=(const T t, const std::vector<T> & l) {
    return std::find(l.begin(), l.end(), t) == l.end();
}

int main() {
  std::cout << ( 3!=std::vector<int>{ 3, 8, 87, 9}) << std::endl;
  std::cout << ( 8!=std::vector<int>{ 3, 8, 87, 9}) << std::endl;
  std::cout << (10!=std::vector<int>{ 3, 8, 87, 9}) << std::endl;
}

Malheureusement pour le moment, les analyseurs n'aiment pas avoir initializer_list en argument des opérateurs, il n'est donc pas possible de se débarrasser de std::vector dans la deuxième solution.

0 votes

+1. La première solution a un bon équilibre entre efficacité et verbosité, l'une des meilleures présentées ici.

2 votes

Mauvais cas pour la surcharge de !=. !!= devrait être équivalent à ==, pour éviter toute confusion, ce qui n'est évidemment pas possible ici. De plus, l'opérateur != est censé être symétrique. Cela ne peut que créer de la confusion.

1 votes

Donnerais +1 si vous n'avez pas essayé de surcharger l'opérateur.

5voto

CroCo Points 666

Si vous ne voulez pas répéter cette condition encore et encore, utilisez une macro.

#include 

#define isTRUE(x, a, b, c, d)  ( x != a && x != b && x != c && x != d ) 

int main() 
{
    int x(2);
    std::cout << isTRUE(x,3,8,87,9) << std::endl;

    if ( isTRUE(x,3,8,87,9) ){
        // SomeStuff();
    }
    return 0;
}

0 votes

Les macros sont mal vues en C++. La solution avec des templates fait essentiellement la même chose et est flexible avec le nombre d'arguments.

1 votes

@CroCo : exemple dans ce cas : isTRUE(heavyComputationIntensive(), 1, 2, 3, 4) ou isTRUE(a && b, 2, 3, 4, 5)...

0 votes

@CroCo, il y a plusieurs raisons (comme indiqué ailleurs): 1) les macros sont difficiles à déboguer, 2) les macros peuvent avoir des effets secondaires étranges, 3) les macros ne sont pas conscients des espaces de noms, 4) les macros n'ont pas de sécurité de type. Outre ces points : avec la norme C++14 d'aujourd'hui, il reste très peu de raisons pour lesquelles vous auriez réellement besoin de macros. (la protection des fichiers inclus est à peu près la dernière, espérons qu'elle sera supprimée d'ici C++17)

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