J'essaie de simplifier ce qui suit:
function handleDirection(src) {
if (src === 'left') {
if (inverse) {
tracker--;
} else {
tracker++;
}
} else {
if (inverse) {
tracker++;
} else {
tracker--;
}
}
}
pour réduire le nombre de conditions. La src
sera toujours 'left'
ou 'right'
.
Vous pouvez vérifier avec le résultat du premier contrôle.
Ceci est un contrôle exclusif OR.
// typeof inverse === 'boolean'
function handleDirection(src) {
if (src === 'left' === inverse) {
tracker--;
} else {
tracker++;
}
}
La vérification évalue l'expression dans cet ordre (src === 'left') === inverse
:
src === 'left' === inverse
---- first --- returns a boolean value
--------- second --------- take result of former check & compairs it with another boolean
function handleDirection(src) {
var movement = 1;
if(src === 'left')
movement = -1;
if(inverse)
tracker += movement;
else
tracker -= movement;
}
Vous pouvez même le faire avec une seule ligne de code:
function getDirectionOffset(src) {
tracker += (src === 'left' ? 1 : -1) * (inverse ? -1 : 1);
}
Cela pourrait être simplifié en une expression ternaire qui renvoie 1
ou -1
en fonction de l'état. Ensuite, vous pouvez simplement ajouter cela à la tracker
.
function handleDirection(src) {
var delta = (src === 'left' && inverse) || (src !== 'left' && !inverse) ? -1 : 1;
tracker += delta;
}
Cela pourrait ensuite être simplifié davantage en utilisant la logique décrite par @NinaScholz dans sa réponse:
function handleDirection(src) {
var delta = (src === 'left') === inverse ? -1 : 1;
tracker += delta;
}
En supposant que inverse
est un drapeau que vous avez défini une fois, vous n'avez pas besoin de le prendre en compte à chaque fois, vous pouvez calculer son impact une fois et simplement l'utiliser tel quel, ce qui réduira vos branches de code et la logique. Si vous souhaitez le modifier au fur et à mesure, vous devrez peut-être séparer la logique de calcul afin de pouvoir la réutiliser.
Vous pouvez également extraire ensuite la direction du mouvement dans une fonction autonome et votre handleDirection
devient très simple: vous calculez la direction que vous voulez aller en fonction de src
et de la invert
.
let tracker = 0;
//extract logic for the movement offset based on direction
function getDirectionOffset(src) {
return src === 'left' ? 1 : -1;
}
//have a setter for the invert property
function setInverse(isInverse) {
movementModifier = isInverse ? -1 : 1
}
//declare the variable dependent on the inverse property
let movementModifier;
//initialise movementModifier variable
setInverse(false);
function handleDirection(src) {
const offset = getDirectionOffset(src) * movementModifier;
tracker += offset;
}
// usage
setInverse(true);
handleDirection("left");
handleDirection("left");
handleDirection("right");
console.log(tracker);
Cela dit, tout cela suggère que vous ne devriez pas utiliser une fonction, ou l’utiliser différemment. Vous pouvez collecter toutes ces fonctionnalités dans une classe ou faire en sorte que toutes les informations soient transmises autour des fonctions, de sorte que vous n'avez pas de globales. Voici un exemple d'implémentation orientée objet du concept:
class TrackerMover {
constructor(inverse) {
this.tracker = 0;
this.movementModifier = inverse ? 1 : -1
}
handleDirection(src) {
const offset = this.getDirectionOffset(src) * this.movementModifier;
this.tracker += offset;
}
getDirectionOffset(src) {
return src === 'left' ? -1 : 1;
}
getPosition() {
return this.tracker;
}
}
//usage
const mover = new TrackerMover(true);
mover.handleDirection("left");
mover.handleDirection("left");
mover.handleDirection("right");
console.log(mover.getPosition())
En passant, une autre alternative consiste à NE PAS calculer le mouvement à chaque fois. Vous savez en fait ce qui se passe à chaque fois. En fait, vous avez une table de vérité dans laquelle vos entrées sont src === left
et inverse
et les sorties indiquent comment vous modifiez votre suivi.
+--------+------------+--------+
| isLeft | isInverted | Offset |
+--------+------------+--------+
| true | true | -1 |
| true | false | 1 |
| false | true | 1 |
| false | false | -1 |
+--------+------------+--------+
Donc, vous pouvez simplement mettre cette table dans.
let tracker = 0;
let invert = false;
const movementLookupTable = {
"true": { },
"false": { },
}
//it can be initialised as part of the above expression but this is more readable
movementLookupTable[true ][true ] = -1;
movementLookupTable[true ][false] = 1;
movementLookupTable[false][true ] = 1;
movementLookupTable[false][false] = -1;
function handleDirection(src) {
const offset = movementLookupTable[src === "left"][invert];
tracker += offset;
}
// usage
invert = true;
handleDirection("left");
handleDirection("left");
handleDirection("right");
console.log(tracker);
Dans ce cas, cela peut être excessif, mais cette approche peut être utile s’il ya plus de drapeaux (y compris plus de valeurs pour les drapeaux) et/ou d’états finaux. Par exemple, vous souhaitez peut-être introduire quatre directions, mais vous ne modifiez pas la valeur tracker
s'il s'agit de up
ou down
.
+-----------+------------+--------+
| direction | isInverted | Offset |
+-----------+------------+--------+
| left | true | -1 |
| left | false | 1 |
| right | true | 1 |
| right | false | -1 |
| up | false | 0 |
| up | true | 0 |
| down | false | 0 |
| down | true | 0 |
+-----------+------------+--------+
Comme vous pouvez le constater, il ne s’agit plus uniquement de booléens, vous pouvez gérer n’importe quelle valeur. En utilisant une table, vous changez également invert
pour qu'il ressemble à windDirection
. Ainsi, si le mouvement est left
et que windDirection
est right
, le résultat est similaire à ce qu'il est maintenant, mais vous pouvez avoir une direction de left
et un vent qui tourne left
. déplacer plus loin . Ou vous pouvez déplacer up
et la direction du vent est left
, donc tracker
(à ce stade, les coordonnées X) va réellement être modifié.
+-----------+---------------+---------+
| direction | windDirection | OffsetX |
+-----------+---------------+---------+
| left | right | -1 |
| left | up | 1 |
| left | down | 1 |
| left | left | 2 |
| right | up | -1 |
| right | down | -1 |
| right | right | -2 |
| right | left | 1 |
| up | up | 0 |
| up | down | 0 |
| up | left | 1 |
| up | right | -1 |
| down | up | 0 |
| down | down | 0 |
| down | left | 1 |
| down | right | -1 |
+-----------+---------------+---------+
Avec quatre directions et quatre directions de vent à prendre en compte, la logique peut être assez gênante à la fois de lire et de maintenir à l’avenir, alors que si vous n’avez qu’une table de consultation, c’est facile et vous pouvez facilement l’étendre pour gérer les diagonales (supposons ils changent la valeur de 0.5
au lieu de 1
) et votre algorithme ne s’intéressera pas vraiment tant que vous récupérerez les valeurs de la table.
Vous n'avez besoin d'aucune phrase if
. La même opération peut être effectuée en calculant un incrément positif ou négatif en fonction de src et inverse À l’aide d’un opérateur ternaire.
function handleDirection(src) {
tracker += (src == "left" ? 1 : -1) * (inverse ? -1 : 1);
};
Btw. Par souci d'efficacité, je recommanderais d'utiliser directement des incréments/décréments numériques .__ au lieu de chaînes nécessitant un traitement supplémentaire pour être décodées Vous pouvez utiliser des constantes pour obtenir la même lisibilité:
De même, inverse peut être optimisé en tant que valeur numérique commutant entre 1 (pas inversé) Et -1 (inversé).
const left = 1;
const right = -1;
var direction = 1;
function handleDirection(src) {
tracker += src * direction;
}
function reverse() { // (Example)
direction = direction * -1;
}
... même si les mots clés "right" et "left" proviennent d'une sorte d'utilisateur textuel , vous pouvez simplement les traduire à partir d'un dictionnaire:
const steps = {
left = 1;
right = -1;
};
function handleDirection(src) {
tracker += steps[src] * direction;
}
Vous souhaitez augmenter le suivi si l'un des src == left
ou inverse
est vrai mais pas l'autre, et le diminuer sinon, comme le fait l'opérateur "XOR" ^
:
function handleDirection(src) {
if (src === 'left' ^ inverse) {
tracker++;
} else {
tracker--;
}
}
Vous pouvez réduire cela davantage en utilisant une expression ternaire:
function handleDirection(src) {
tracker += src === 'left' ^ inverse ? 1 : -1;
}
Ou si vous voulez éviter tout type de conditionnal, avec des conversions implicites et des arithmétiques "intelligentes":
function handleDirection(src) {
tracker += 1 - 2 * (src === 'right' ^ inverse); // either 1-0=1 or 1-2=-1
}
Cela n’a qu’une condition, et je trouve qu’il se lit de manière plus intuitive que les autres réponses:
function handleDirection(src) {
if (
((src === 'left') && !inverse) ||
((src === 'right') && inverse)
) {
tracker++;
}
else {
tracker--;
}
}
Je n'aime pas les elses et j'essaie d'éviter les nids si possible. Je pense que cela traduit l'idée de inverse
d'une manière plus naturelle:
function handleDirection(src)
{
let change = 1;
if ('right' == src)
change = -1;
if (inverse)
change = -change;
tracker += change;
}
Vous pouvez utiliser la syntaxe de court-circuit ou les opérateurs ternaires
// by using short circuiting
function handleDirection(src) {
if (src == 'left') tracker = inverse && tracker-1 || tracker +1
else tracker = inverse && tracker+1 || tracker -1
}
// by using ternary operator
function handleDirection(src) {
if (src == 'left') tracker = inverse ? tracker-1 : tracker +1
else tracker = inverse ? tracker+1 : tracker -1
}
Je sais que cela ne règle pas directement la question par une "simplification" directe, mais je voudrais vous fournir une réponse qui aborde plusieurs problèmes de qualité du code tout en rendant le code plus lisible.
À propos des effets secondaires
Tout d'abord, cette fonction donnée mute des valeurs externes. Ceci introduit le problème de effets secondaires :
Il est également beaucoup plus difficile de tester une telle fonction, car vous devez d'abord "créer l'environnement d'état" pour exécuter le test.
Un premier Tweak facile serait de faire accepter toutes les valeurs externes par paramètre et de renvoyer soit une valeur 1
, soit -1
attribuée à quelque chose (dans votre cas, tracker
).
Exclusif ou conditionnel avec des chaînes
Deuxièmement, l'utilisation d'un if/else
on String avec des valeurs exclusives ou peut conduire à un état non défini dans lequel src
pourrait être autre chose que 'right'
, mais la fonction se comporterait comme si elle était 'right'
. Au lieu de cela, il devrait lancer une exception. L'utilisation d'un commutateur est d'une aide précieuse ici.
Application de ces points à la fonction
Si les points ci-dessus sont pris en compte, la fonction globale ressemblerait à ceci:
function handleDirection (src, inverse) {
switch (src) {
case 'left':
return inverse ? -1 : 1
case 'right':
return inverse ? 1 : -1
default:
throw new Error(`Unknown src: ${src}`)
}
}
et vous pouvez facilement tester cette fonction:
handleDirection('left' , true) // -1
handleDirection('left' , false) // 1
handleDirection('right', true) // 1
handleDirection('right', false) // -1
handleDirection('middle',true) // Error: Unknown src: middle
Désormais, la fonction est clairement découplée de tracker
(pensez à votre temps précieux lors de la refactorisation), mais en outre, son rôle est parfaitement clair.
Remarque
En résumé, je voulais souligner qu’il ne s’agit pas toujours d’écrire le code le plus simple avec le moins de lignes mais un code qui est clair à lire/comprendre et à maintenir. Ce n’est pas aussi court que la plupart des solutions proposées, mais tout le monde devrait immédiatement comprendre ce qu’il fait.
Actuellement, vous comparez sur des chaînes, ce que je ne conseillerais pas. Si, par exemple, vous utilisez 'Left' au lieu de 'left', la première instruction if échouera. Peut-être qu'un booléen pourrait être utile ici, puisque vous pouvez garantir qu'il n'a que deux états.
Les instructions if peuvent être compressées via opérateurs conditionnels .
Peut-être que quelque chose comme ceci est ce que vous recherchez:
function handleDirection(src) {
if (src) {
inverse ? tracker-- : tracker++;
} else {
inverse ? tracker++ : tracker--;
}
}