J'ai quelques boucles dont j'ai besoin dans mon programme. Je peux écrire le pseudo-code mais je ne sais pas trop comment les écrire logiquement.
J'ai besoin de -
if (num is a multiple of 10) { do this }
if (num is within 11-20, 31-40, 51-60, 71-80, 91-100) { do this }
else { do this } //this part is for 1-10, 21-30, 41-50, 61-70, 81-90
Ceci est pour un jeu de société serpents et échelles, si cela a plus de sens pour ma question.
J'imagine que la première instruction si je devrais utiliser le module, est-ce que if (num == 100%10)
serait correct?
Le second je n'ai aucune idée. Je peux l'écrire comme if (num > 10 && num is < 21 || etc)
mais il doit y avoir quelque chose de plus intelligent que cela.
Pour le premier, vérifier si un nombre est un multiple d'utilisation:
if (num % 10 == 0) // its divisible by 10
Pour le second:
if(((num - 1) / 10) % 2 == 1 && num <= 100)
Mais c'est plutôt dense, il vaudrait peut-être mieux énumérer explicitement les options.
int getRow(int num) {
return (num - 1) / 10;
}
if (getRow(num) % 2 == 0) {
}
C'est la même logique, mais en utilisant la fonction, nous obtenons une idée plus claire de ce que cela signifie.
if (num est un multiple de 10) {do this}
if (num % 10 == 0) {
// Do something
}
if (num est compris entre 11-20, 31-40, 51-60, 71-80, 91-100) {faire ceci}
Le truc ici est de chercher une sorte de points communs entre les gammes. Bien sûr, vous pouvez toujours utiliser la méthode "force brute":
if ((num > 10 && num <= 20) ||
(num > 30 && num <= 40) ||
(num > 50 && num <= 60) ||
(num > 70 && num <= 80) ||
(num > 90 && num <= 100)) {
// Do something
}
Mais vous remarquerez peut-être cela si vous soustrayez 1
de num
, vous aurez les plages suivantes:
10-19, 30-39, 50-59, 70-79, 90-99
Autrement dit, tous les nombres à 2 chiffres dont le premier chiffre est impair. Ensuite, vous devez trouver une formule qui exprime cela. Vous pouvez obtenir le premier chiffre en divisant par 10, et vous pouvez vérifier qu'il est impair en vérifiant qu'il reste 1 lorsque vous divisez par 2. Rassemblez tout cela:
if ((num > 0) && (num <= 100) && (((num - 1) / 10) % 2 == 1)) {
// Do something
}
Compte tenu du compromis entre un code plus long mais maintenable et un code "intelligent" plus court, je choisirais chaque fois plus longtemps et plus clairement. À tout le moins, si vous essayez d'être intelligent, veuillez inclure un commentaire qui explique exactement ce que vous essayez d'accomplir.
Il est utile de supposer que le prochain développeur à travailler sur le code est armé et sait où vous habitez. :-)
Si vous utilisez GCC ou un compilateur prenant en charge Case Ranges , vous pouvez le faire, mais votre code ne sera pas portable .
switch(num)
{
case 11 ... 20:
case 31 ... 40:
case 51 ... 60:
case 71 ... 80:
case 91 ... 100:
// do something
break;
default:
// do something else
break;
}
C'est pour les futurs visiteurs plus que pour un débutant. Pour une solution plus générale, semblable à un algorithme, vous pouvez prendre une liste de valeurs de début et de fin et vérifier si une valeur transmise se trouve dans l'une d'elles:
template<typename It, typename Elem>
bool in_any_interval(It first, It last, const Elem &val) {
return std::any_of(first, last, [&val](const auto &p) {
return p.first <= val && val <= p.second;
});
}
Pour plus de simplicité, j'ai utilisé un lambda polymorphe (C++ 14) au lieu d'un argument explicite pair
. Cela devrait aussi probablement rester en utilisant <
et ==
être cohérent avec les algorithmes standard, mais cela fonctionne aussi longtemps que Elem
a <=
défini pour cela. Quoi qu'il en soit, il peut être utilisé comme ceci:
std::pair<int, int> intervals[]{
{11, 20}, {31, 40}, {51, 60}, {71, 80}, {91, 100}
};
const int num = 15;
std::cout << in_any_interval(std::begin(intervals), std::end(intervals), num);
Il y a un exemple en direct ici .
Avec quelques bons commentaires dans le code, il peut être écrit de manière assez concise et lisible.
// Check if it's a multiple of 10
if (num % 10 == 0) { ... }
// Check for whether tens digit is zero or even (1-10, 21-30, ...)
if ((num / 10) % 2 == 0) { ... }
else { ... }
La première est facile, il vous suffit d'appliquer l'opérateur modulo à votre valeur num:
if ( ( num % 10 ) == 0)
Comme c ++ évalue chaque nombre qui n'est pas 0 comme vrai, vous pouvez aussi écrire:
if ( ! ( num % 10 ) ) //does not have a residue when devided by 10
Pour le second, je pense que cela est plus propre à comprendre:
Le motif se répète tous les 20, vous pouvez donc calculer le modulo 20. Tous les éléments souhaités seront alignés, à l'exception de ceux qui peuvent être divisés par 20.
Pour les obtenir également, utilisez simplement num-1 ou mieux num + 19 pour éviter de traiter avec des nombres négatifs.
if ( ( ( num + 19 ) % 20 ) > 9 )
En supposant que le motif se répète éternellement, il s’appliquerait de nouveau pour 111-120, et ainsi de suite. Sinon, vous devez limiter le nombre à 100:
if ( ( ( ( num + 19 ) % 20 ) > 9 ) && ( num <= 100 ) )
En gros, vous avez expliqué la réponse vous-même, mais voici le code au cas où.
if((x % 10) == 0) {
//do this
}
if((x > 10 && x < 21) || (x > 30 && x < 41) || (x > 50 && x < 61) || (x > 70 && x < 81) || (x > 90 && x < 101)) {
//do this
}
Vous pensez peut-être cela.
if (x % 10)
{
.. code for 1..9 ..
} else
{
.. code for 0, 10, 20 etc.
}
La première ligne if (x % 10)
fonctionne car (a) une valeur multiple de 10 se calcule comme "0", les autres nombres donnent leur reste, (b) une valeur de 0 dans un if
est considéré comme false
, toute autre valeur est true
.
Modifier:
Pour basculer dans les années vingt, utilisez le même truc. Cette fois, le numéro pivot est 10
:
if (((x-1)/10) & 1)
{
.. code for 10, 30, ..
} else
{
.. code for 20, 40, etc.
}
x/10
Renvoie n'importe quel nombre compris entre 0 et 9 sous la forme 0
, 10 à 19 sous la forme 1
, Etc. Le test sur pair ou impair - le & 1
- vous dit s'il est pair ou impair. Étant donné que vos plages sont en réalité de "11 à 20", soustrayez 1 avant le test.
Pour le premier:
if (x % 10 == 0)
s'appliquera à:
10, 20, 30, .. 100 .. 1000 ...
pour le second:
if (((x-1) / 10) % 2 == 1)
s'appliquera pour:
11-20, 31-40, 51-60, ..
Nous faisons essentiellement d'abord x-1
obtenir:
10-19, 30-39, 50-59, ..
Puis on les divise par 10
obtenir:
1, 3, 5, ..
Nous vérifions donc si ce résultat est étrange.
n plaidoyer pour la lisibilité
Bien que vous ayez déjà de bonnes réponses, je voudrais recommander une technique de programmation qui rendra votre code plus lisible pour un futur lecteur - cela peut être vous dans six mois, un collègue invité à effectuer une révision du code, votre successeur, .. .
Cela consiste à envelopper toutes les déclarations "intelligentes" dans une fonction qui montre exactement (avec son nom) ce qu'elle fait. Bien qu’il y ait un impact minime sur les performances (de "charge d’appel de fonction"), ceci est vraiment négligeable dans une situation de jeu telle que celle-ci.
En cours de route, vous pouvez nettoyer vos entrées - par exemple, tester les valeurs "illégales". Ainsi, vous pourriez vous retrouver avec un code comme celui-ci - voir combien il est plus lisible? Les "fonctions d'assistance" peuvent être cachées quelque part (elles n'ont pas besoin d'être dans le module principal: leur nom l'indique clairement):
#include <stdio.h>
enum {NO, YES, WINNER};
enum {OUT_OF_RANGE=-1, ODD, EVEN};
int notInRange(int square) {
return(square < 1 || square > 100)?YES:NO;
}
int isEndOfRow(int square) {
if (notInRange(square)) return OUT_OF_RANGE;
if (square == 100) return WINNER; // I am making this up...
return (square % 10 == 0)? YES:NO;
}
int rowType(unsigned int square) {
// return 1 if square is in odd row (going to the right)
// and 0 if square is in even row (going to the left)
if (notInRange(square)) return OUT_OF_RANGE; // trap this error
int rowNum = (square - 1) / 10;
return (rowNum % 2 == 0) ? ODD:EVEN; // return 0 (ODD) for 1-10, 21-30 etc.
// and 1 (EVEN) for 11-20, 31-40, ...
}
int main(void) {
int a = 12;
int rt;
rt = rowType(a); // this replaces your obscure if statement
// and here is how you handle the possible return values:
switch(rt) {
case ODD:
printf("It is an odd row\n");
break;
case EVEN:
printf("It is an even row\n");
break;
case OUT_OF_RANGE:
printf("It is out of range\n");
break;
default:
printf("Unexpected return value from rowType!\n");
}
if(isEndOfRow(10)==YES) printf("10 is at the end of a row\n");
if(isEndOfRow(100)==WINNER) printf("We have a winner!\n");
}
Vous pouvez essayer ce qui suit:
// multiple of 10
if ((num % 10) == 0)
{
// Do something
}
else if (((num / 10) % 2) != 0)
{
//11-20, 31-40, 51-60, 71-80, 91-100
}
else
{
//other case
}
Comme d'autres l'ont fait remarquer, rendre les conditions plus concises n'accélérera pas la compilation ou l'exécution, et ne facilitera pas non plus la lisibilité.
Cela peut aider à rendre votre programme plus flexible, au cas où vous décideriez plus tard de vouloir une version du jeu pour tout-petit sur un tableau 6 x 6, ou une version avancée (que vous pouvez jouer toute la nuit) sur un format 40 x 50 conseil.
Donc, je le coderais comme suit:
// What is the size of the game board?
#define ROWS 10
#define COLUMNS 10
// The numbers of the squares go from 1 (bottom-left) to (ROWS * COLUMNS)
// (top-left if ROWS is even, or top-right if ROWS is odd)
#define firstSquare 1
#define lastSquare (ROWS * COLUMNS)
// We haven't started until we roll the die and move onto the first square,
// so there is an imaginary 'square zero'
#define notStarted(num) (num == 0)
// and we only win when we land exactly on the last square
#define finished(num) (num == lastSquare)
#define overShot(num) (num > lastSquare)
// We will number our rows from 1 to ROWS, and our columns from 1 to COLUMNS
// (apologies to C fanatics who believe the world should be zero-based, which would
// have simplified these expressions)
#define getRow(num) (((num - 1) / COLUMNS) + 1)
#define getCol(num) (((num - 1) % COLUMNS) + 1)
// What direction are we moving in?
// On rows 1, 3, 5, etc. we go from left to right
#define isLeftToRightRow(num) ((getRow(num) % 2) == 1)
// On rows 2, 4, 6, etc. we go from right to left
#define isRightToLeftRow(num) ((getRow(num) % 2) == 0)
// Are we on the last square in the row?
#define isLastInRow(num) (getCol(num) == COLUMNS)
// And finally we can get onto the code
if (notStarted(mySquare))
{
// Some code for when we haven't got our piece on the board yet
}
else
{
if (isLastInRow(mySquare))
{
// Some code for when we're on the last square in a row
}
if (isRightToLeftRow(mySquare))
{
// Some code for when we're travelling from right to left
}
else
{
// Some code for when we're travelling from left to right
}
}
Oui, c'est verbeux, mais cela clarifie exactement ce qui se passe sur le plateau de jeu.
Si je développais ce jeu pour l'afficher sur un téléphone ou une tablette, je créerais des variables ROWS et COLUMNS au lieu de constantes, afin qu'elles puissent être définies de manière dynamique (au début d'un jeu) en fonction de la taille et de l'orientation de l'écran. Je permettais également de modifier l'orientation de l'écran à tout moment, en cours de partie. Tout ce que vous avez à faire est de changer les valeurs de ROWS et de COLUMNS, tout en laissant le reste (le nombre de carrés actuel de chaque joueur, et le carrés de départ/fin de tous les serpents et des échelles) inchangés. Ensuite, il vous suffit de bien dessiner le tableau et d’écrire du code pour vos animations (je suppose que c’était le but de vos déclarations if
) ...
Je sais que cette question a beaucoup de réponses, mais je vais jeter la mienne ici de toute façon ... Tiré de la deuxième édition de Code Complete de Steve McConnell: "Stair-Step Access Tables:
Un autre type d’accès à la table est la méthode des marches. Cette méthode d’accès n’est pas aussi directe qu’une structure d’index, mais elle ne gaspille pas autant d’espace de données. L'idée générale des structures en escalier, illustrée à la figure 18-5, est que les entrées d'un tableau sont valables pour des plages de données plutôt que pour des points de données distincts.
Figure 18-5 L'approche par escalier catégorise chaque entrée en déterminant le niveau auquel elle atteint un "escalier". La "marche" à laquelle elle se trouve détermine sa catégorie.
Par exemple, si vous écrivez un programme de notation, la plage de saisie "B" peut aller de 75% à 90%. Voici une gamme de notes que vous pourriez avoir à programmer un jour:
Pour utiliser la méthode par étapes, insérez l'extrémité supérieure de chaque plage dans un tableau, puis écrivez une boucle pour comparer un score à l'extrémité supérieure de chaque plage. Lorsque vous trouvez le point où le score dépasse pour la première fois le sommet d'une plage, vous savez quelle est la note. Avec la technique en escalier, vous devez prendre soin de manipuler correctement les extrémités des plages. Voici le code dans Visual Basic qui attribue des notes à un groupe d’étudiants en fonction de cet exemple:
Bien que ce soit un exemple simple, vous pouvez facilement le généraliser pour traiter plusieurs étudiants, plusieurs systèmes de notation (par exemple, différentes notes pour différents niveaux de points sur différentes affectations) et des modifications du système de notation. "
Code Complete 2nd Edition pages 426 - 428 (Chapitre 18). J'espère que cela aide et désolé je n'ai pas converti le code en c ++ mais vous connaissez ce vieux dicton chinois: "Ne donnez pas de poisson à un mendiant, donnez-lui une canne à pêche!" :)