Partage
  • Partager sur Facebook
  • Partager sur Twitter

Conditions logiques : simplification, et #define

Sujet résolu
    17 janvier 2021 à 13:27:15

    Salut à tous.

    Deux questions en un sujet ici, concernant les conditions logiques. Ces interrogations découlent de l'écriture de conditions complexes pour une gestion des collisions en jeu et donc un désir de simplification et de propreté.

    • La simplification

    Je suis paresseux et je n'aime pas les répétitions donc je veux simplifier une condition du type if(a == x || a == y) en if(a == (x || y)).

    Comme je ne trouve rien sur le sujet en cherchant un peu, je fais quelques tests et conclue qu'à priori le C ne permet pas cette simplification.

    Ci-dessous mes tests :

    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc , char* argv[])
    {
        int a = 1 , b = 10 , c = 100 ;
        int x = 1 , y = 10 , z = 100 ;
    
        if(a == (x || y))
            printf("True\n") ;
    
        else
            printf("False\n") ;
    
    
        if(b == (x || y))
            printf("True\n") ;
    
        else
            printf("False\n") ;
    
    
        if(c == (x || y))
            printf("True\n") ;
    
        else
            printf("False\n") ;
    
        return 0 ;
    }

    Ceci rend True / False / False.

    Et avec des &&, même résultat.

    Je conclue donc que la condition teste la première partie a == (x et ignore le reste.

    Le cœur de ma première question est donc de savoir si cela est effectivement ce qu'il se passe (que la deuxième partie soit ignorée, ou s'il se passe autre chose que je ne comprends pas), et si ce genre de simplification est toujours interdit en C.

    • Ranger des conditions longues dans un #define

    Un autre effet de ces conditions complexes est qu'elles tiennent sur 5-6 lignes et je trouve qu'elles réduisent grandement la lisibilité de la fonction. Comme j'aime que mon code soit propre et lisible, je fait un #define CONDITION blablablaconditiongéantesurplusieurslignes.

    #define BLOCKING_TILE_LEFT playerPos.y + PLAYER_SHAVE_UP <= levelTiles[i + j*TILES_X].tilePos.y + TILE_SIZE\
                               && playerPos.y + PLAYER_SHAVE_UP > levelTiles[i + j*TILES_X].tilePos.y\
                                && levelTiles[i + j*TILES_X].tileType != FLOOR && levelTiles[i + j*TILES_X].tileType != CRATE\
                               || playerPos.y + TILE_SIZE - PLAYER_SHAVE_DOWN >= levelTiles[i + (j+1)*TILES_X].tilePos.y\
                                   && playerPos.y - PLAYER_SHAVE_DOWN < levelTiles[i + (j+1)*TILES_X].tilePos.y\
                                   && levelTiles[i + (j+1)*TILES_X].tileType != FLOOR && levelTiles[i + (j+1)*TILES_X].tileType != CRATE\



    La compilation me donne un warning, me suggérant d'entourer les conditions de chaque coté de l'opérateur || de parenthèses, alors que la même condition dans le code principal me rend un build log propre. Je teste tout de même et ça fonctionne comme prévu malgré le warning ignoré.

    Sur ce sujet j'ai trouvé un peu plus d'informations (https://gcc.gnu.org/onlinedocs/cpp/Operator-Precedence-Problems.html) qui suggère que les directives de préprocesseur ne traitent pas la priorité des opérateurs de la même façon que le code principal.

    Donc comme ci-dessus je fais quelques tests :

    #include <stdio.h>
    #include <stdlib.h>
    
    #define CONDITION_1 x == b || x == z && x == c || x == y || x == a      //Check && priority over ||
    #define CONDITION_2 x == a && x == c*y                                  //Check * priority over &&
    #define operation(y,z,b) y + z / 100 - b                                //Check / priority over + and -
    
    int main(int argc , char* argv[])
    {
        int a = 1 , b = 10 , c = 1 ;
        int x = 1 , y = 10 , z = 100 ;
    
        if(CONDITION_1)
            printf("True\n") ;
    
        else
            printf("False\n") ;
    
    
        if(CONDITION_2)
            printf("True\n") ;
    
        else
            printf("False\n") ;
    
    
        x = operation(y,z,b) ;
    
        if(x)
            printf("True : %d\n" , x) ;
    
        else
            printf("False: %d\n" , x) ;
    
        return 0 ;
    }
    



    Comme attendu en le faisant de tête on obtient True, False, True, en respectant la priorité des opérations mathématiques et logiques ; avec le même warning suggérant des parenthèses.

    Donc le cœur de ma deuxième question à présent : malgré le warning et les informations sur la page GCC GNU (qui ne donne qu'en exemple un cas avec l'opérateur bitwise &) le préprocesseur semble respecter l'ordre de priorité correctement, donc dans quels cas y a-t-il un risque que cela ne fonctionne pas comme attendu, et qu'il faille mettre des parenthèses dans tous les sens ? Est-ce une mauvaise pratique que de ranger ce que l'on ne veut pas voir dans un #define ?

    Merci !

    -
    Edité par DryGin 17 janvier 2021 à 13:29:13

    • Partager sur Facebook
    • Partager sur Twitter
      17 janvier 2021 à 13:39:33

      DryGin a écrit:

      Salut à tous.

      Deux questions en un sujet ici, concernant les conditions logiques. Ces interrogations découlent de l'écriture de conditions complexes pour une gestion des collisions en jeu et donc un désir de simplification et de propreté.

      • La simplification

      Je suis paresseux et je n'aime pas les répétitions donc je veux simplifier une condition du type if(a == x || a == y) en if(a == (x || y)).

      Comme je ne trouve rien sur le sujet en cherchant un peu, je fais quelques tests et conclue qu'à priori le C ne permet pas cette simplification.

      Bonjour,

      En effet on ne peut pas factoriser

      DryGin a écrit:

      Je conclue donc que la condition teste la première partie a == (x et ignore le reste.

      Non. En C voilà comment «ça se passe» :

      • a==x || a==y
        avec les précédences d'opérateurs, cette expression est évaluée comme : (a==x) || (a==y)
        si a==x est vraie alors la suite n'est pas évalué et vrai(1) est renvoyé
        si a!=x alors a==y est évalué et renvoyé (vrai/1 ou faux/0)

      • a==(x||y)
        tout d'abord (x||y) est évalué, cette expression est équivalente à (x!=0)||(y!=0)
        puis sa valeur (1 pour vrai, 0 pour faux) est comparée à la valeur de a

      DryGin a écrit:

      • Ranger des conditions longues dans un #define

      Un autre effet de ces conditions complexes est qu'elles tiennent sur 5-6 lignes et je trouve qu'elles réduisent grandement la lisibilité de la fonction. Comme j'aime que mon code soit propre et lisible, je fait un #define CONDITION blablablaconditiongéantesurplusieurslignes.

      Si tu veux un code lisible écris des fonctions, oublie les macros ; non seulement ça te simplifiera la vie mais ça t'évitera bon nombre de bugs. Rien ne sera plus lisible qu'un

      if        (is_move_legal( level, player, UP )) {
         ...
      } else if (is_move_legal( level, player, DOWN )) {
         ...
      

      DryGin a écrit:

      Est-ce une mauvaise pratique que de ranger ce que l'on ne veut pas voir dans un #define ?

      Merci !

      -
      Edité par DryGin il y a 10 minutes


      Oui !

      Réserve les define dans un premier temps juste aux constantes.

      -
      Edité par White Crow 17 janvier 2021 à 13:42:29

      • Partager sur Facebook
      • Partager sur Twitter
        17 janvier 2021 à 13:53:43

        Ce que tu ne comprend pas, c'est le résultat de l'opération (x || y) C'est une opération booléenne qui renvois 0 ou 1 (ici pour renvoyer 0 il faut que les deux valeur x et y soit à 0) !
        • Partager sur Facebook
        • Partager sur Twitter
          17 janvier 2021 à 15:53:23

          Tu es paresseux, mais tu fais du copier/coller de tes if/else et de tes printf :) Alors que justement tu pourras factoriser tout ça proprement :)
          • Partager sur Facebook
          • Partager sur Twitter

          Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

            18 janvier 2021 à 17:54:51

            DryGin a écrit:

            dans quels cas y a-t-il un risque que cela ne fonctionne pas comme attendu,

            attendu par qui ?

            Il y a une documentation, la norme du langage, qui explique laborieusement ce qu'est le langage C, et donc ce qu'on peut attendre des programmes si ils sont traduits par un compilateur qui fait son boulot correctement.

            Que ça fonctionne comme attendu par quelqu'un qui écrit des trucs au pif, c'est pas gagné. C'est pas que c'est un risque, c'est quasiment sûr que ça court aux emmerdements.

            PS

            if A = B or C or D
            

            ça existe, en Cobol. Cobol, c'est mieux.

            -
            Edité par michelbillaud 18 janvier 2021 à 18:22:30

            • Partager sur Facebook
            • Partager sur Twitter
              19 janvier 2021 à 18:26:44

              Merci pour les réponses !

              Le fait de comprendre que (x || y) soit une opération me manquait effectivement.

              • Partager sur Facebook
              • Partager sur Twitter

              Conditions logiques : simplification, et #define

              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
              • Editeur
              • Markdown