Pourquoi n'y a-t-il pas de xor logique en JavaScript?

Réponses:

358

JavaScript fait remonter son ascendance à C et C n'a pas d'opérateur XOR logique. Principalement parce que ce n'est pas utile. Bitwise XOR est extrêmement utile, mais au cours de toutes mes années de programmation, je n'ai jamais eu besoin d'un XOR logique.

Si vous avez deux variables booléennes, vous pouvez imiter XOR avec:

if (a != b)

Avec deux variables arbitraires, vous pouvez les utiliser !pour les contraindre à des valeurs booléennes, puis utiliser la même astuce:

if (!a != !b)

C'est assez obscur et mériterait certainement un commentaire. En effet, vous pouvez même utiliser l'opérateur XOR au niveau du bit à ce stade, bien que ce soit beaucoup trop intelligent à mon goût:

if (!a ^ !b)
John Kugelman
la source
Le seul problème avec !=est que vous ne pouvez pas faire la même chose que a ^= b, car a !== bc'est juste l' opérateur d' inégalité stricte .
mcpiroman le
79

Javascript a un opérateur XOR au niveau du bit: ^

var nb = 5^9 // = 12

Vous pouvez l'utiliser avec des booléens et il donnera le résultat sous forme de 0 ou 1 (que vous pouvez reconvertir en booléen, par exemple result = !!(op1 ^ op2)). Mais comme John l'a dit, c'est équivalent à result = (op1 != op2), ce qui est plus clair.

Pikrass
la source
28
C'est XOR au niveau du bit, pas XOR logique
Ismail Badawi
66
Vous pouvez l'utiliser comme un xor logique. true^trueest 0, et false^trueest 1.
Pikrass
14
@Pikrass Vous pouvez l'utiliser comme opérateur logique sur les booléens , mais pas sur les autres types. ||et &&peut être utilisé comme opérateurs logiques sur des non-booléens (par exemple, 5 || 7renvoie une valeur de vérité, "bob" && nullrenvoie une valeur de faux) mais ^ne peut pas. Par exemple, 5 ^ 7égal à 2, ce qui est vrai.
Mark Amery
10
@Pikrass Mais malheureusement, (true ^ false) !== truece qui le rend ennuyeux avec les bibliothèques qui nécessitent des booléens réels
Izkata
2
@Pikrass Vous ne devez jamais l'utiliser comme opérateur logique sur booléen car l'implémentation dépend du système d'exploitation. J'utilisais une sorte de a ^= truepour basculer les booléens et cela échoue sur certaines machines telles que les téléphones.
Masadow
30

Il n'y a pas de véritables opérateurs booléens logiques en Javascript (bien que cela !soit assez proche). Un opérateur logique ne prendrait que trueou falsecomme opérandes et ne renverrait que trueou false.

En Javascript &&et ||prenez toutes sortes d'opérandes et retournez toutes sortes de résultats amusants (tout ce que vous leur donnez).

De plus, un opérateur logique doit toujours prendre en compte les valeurs des deux opérandes.

En Javascript &&et ||prenez un raccourci paresseux et n'évaluez pas le deuxième opérande dans certains cas et négligez ainsi ses effets secondaires. Ce comportement est impossible à recréer avec un xor logique.


a() && b()évalue a()et renvoie le résultat s'il est faux. Sinon, il évalue b()et renvoie le résultat. Par conséquent, le résultat renvoyé est véridique si les deux résultats sont véridiques et faux dans le cas contraire.

a() || b()évalue a()et renvoie le résultat s'il est véridique. Sinon, il évalue b()et renvoie le résultat. Par conséquent, le résultat renvoyé est faux si les deux résultats sont faux et véridique dans le cas contraire.

L'idée générale est donc d'évaluer d'abord l'opérande gauche. L'opérande correct n'est évalué que si nécessaire. Et la dernière valeur est le résultat. Ce résultat peut être n'importe quoi. Objets, nombres, chaînes ... peu importe!

Cela permet d'écrire des choses comme

image = image || new Image(); // default to a new Image

ou

src = image && image.src; // only read out src if we have an image

Mais la valeur de vérité de ce résultat peut également être utilisée pour décider si un opérateur logique "réel" aurait retourné vrai ou faux.

Cela permet d'écrire des choses comme

if (typeof image.hasAttribute === 'function' && image.hasAttribute('src')) {

ou

if (image.hasAttribute('alt') || image.hasAttribute('title')) {

Mais un opérateur xor "logique" ( ^^) devrait toujours évaluer les deux opérandes. Cela le rend différent des autres opérateurs "logiques" qui n'évaluent le deuxième opérande que si nécessaire. Je pense que c'est pourquoi il n'y a pas de xor "logique" en Javascript, pour éviter toute confusion.


Alors, que devrait-il se passer si les deux opérandes sont faux? Les deux pourraient être retournés. Mais un seul peut être retourné. Laquelle? Le premier? Ou le second? Mon intuition me dit de renvoyer le premier mais généralement les opérateurs «logiques» évalués de gauche à droite et renvoyer la dernière valeur évaluée. Ou peut-être un tableau contenant les deux valeurs?

Et si un opérande est vrai et que l'autre opérande est faux, un xor doit renvoyer celui qui est vrai. Ou peut-être un tableau contenant le vrai, pour le rendre compatible avec le cas précédent?

Et enfin, que devrait-il se passer si les deux opérandes sont véridiques? Vous vous attendriez à quelque chose de faux. Mais il n'y a pas de résultats erronés. L'opération ne doit donc rien renvoyer. Alors peut undefined- être ou… un tableau vide? Mais un tableau vide est toujours vrai.

En adoptant l'approche par tableau, vous vous retrouveriez avec des conditions telles que if ((a ^^ b).length !== 1) {. Très perturbant.

Robert
la source
XOR / ^^ dans n'importe quel langage devra toujours évaluer les deux opérandes car il dépend toujours des deux. Il en va de même pour AND / &&, puisque tous les opérandes doivent être true (véridique dans JS) return pass. L'exception est OR / || car il ne doit évaluer les opérandes que jusqu'à ce qu'il trouve une valeur de vérité. Si le premier opérande d'une liste OR est véridique, aucun des autres ne sera évalué.
Percy
Cependant, vous faites un bon point, que XOR dans JS devrait briser la convention énoncée par AND et OR. Il devrait en fait renvoyer une valeur booléenne appropriée plutôt que l'un des deux opérandes. Toute autre chose pourrait causer de la confusion / complexité.
Percy
9
@Percy AND / && n'évalue pas le deuxième opérande si le premier est faux. Il évalue uniquement les opérandes jusqu'à ce qu'il trouve une valeur erronée.
Robert
@DDS Merci d'avoir rectifié la réponse. Je ne sais pas pourquoi je ne l'ai pas remarqué moi-même. Peut-être que cela explique dans une certaine mesure la confusion de Percy.
Robert
Ma modification a été rejetée, après quoi @matts l'a réédité exactement comme je l'ai corrigé, donc j'ai raté mes (maigres) 2 points. 3 personnes l'ont rejeté et je suis déconcerté par ce qu'ils ont utilisé comme critère. Tapis Thx.
DDS
16

Le XOR de deux booléens est simplement de savoir s'ils sont différents, par conséquent:

Boolean(a) !== Boolean(b)
DomQ
la source
12

Convertissez les valeurs en forme booléenne puis prenez XOR au niveau du bit. Cela aidera également avec les valeurs non booléennes.

Boolean(a) ^ Boolean(b)
Aman Kaushal
la source
10

Covert en booléen, puis effectuez xor comme -

!!a ^ !!b
toyeca
la source
1
Notez que cela !!a ^ !!béquivaut à !a ^ !b. Des arguments pourraient être avancés pour savoir lequel est le plus facile à lire.
tschwab
9

il y a ... une sorte de:

if( foo ? !bar : bar ) {
  ...
}

ou plus facile à lire:

if( ( foo && !bar ) || ( !foo && bar ) ) {
  ...
}

Pourquoi? sais pas.

parce que les développeurs javascript pensaient que ce serait inutile car il peut être exprimé par d'autres opérateurs logiques déjà implémentés.

vous pourriez aussi bien avoir gon avec nand et c'est tout, vous pouvez impressionner toutes les autres opérations logiques possibles à partir de cela.

Je pense personnellement que cela a des raisons historiques qui découlent des langages de syntaxe basés sur c, où, à ma connaissance, xor n'est pas présent ou du moins extrêmement rare.

Le Surrican
la source
Oui, javascript a des opérations ternaires.
mwilcox
C et Java ont XOR utilisant le caractère ^ (caret).
veidelis
7

Oui, faites ce qui suit. En supposant que vous avez affaire aux booléens A et B, la valeur A XOR B peut être calculée en JavaScript à l'aide de ce qui suit

var xor1 = !(a === b);

La ligne précédente est également équivalente à la suivante

var xor2 = (!a !== !b);

Personnellement, je préfère xor1 car je dois taper moins de caractères. Je crois que xor1 est également plus rapide. Il s'agit simplement d'effectuer deux calculs. xor2 effectue trois calculs.

Explication visuelle ... Lisez le tableau ci-dessous (où 0 signifie faux et 1 signifie vrai) et comparez les 3e et 5e colonnes.

! (A === B):

| A | B | A XOR B | A === B | !(A === B) |
------------------------------------------
| 0 | 0 |    0    |    1    |      0     |
| 0 | 1 |    1    |    0    |      1     |
| 1 | 0 |    1    |    0    |      1     |
| 1 | 1 |    0    |    1    |      0     |
------------------------------------------

Prendre plaisir.

asiby
la source
6
var xor1 = !(a === b);est le même quevar xor1 = a !== b;
daniel1426
Cette réponse ne fonctionnera pas pour tous les types de données (comme la réponse de Premchandra). par exemple !(2 === 3)est true, mais 2et 3sont véridiques ainsi 2 XOR 3devrait l'être false.
Mariano Desanze le
2
Si vous aviez lu mon message plus attentivement, vous auriez remarqué que j'ai écrit "En supposant que vous ayez affaire aux booléens A et B ...".
dès le
5

Check-out:

Vous pouvez l'imiter quelque chose comme ceci:

if( ( foo && !bar ) || ( !foo && bar ) ) {
  ...
}
Sarfraz
la source
3
Hé, s'ils ajoutaient un opérateur XOR logique à JavaScript, l'exemple de code serait beaucoup plus propre.
Danyal Aytekin
4

Que diriez-vous de transformer le résultat int en booléen avec double négation? Pas si joli, mais vraiment compact.

var state1 = false,
    state2 = true;
    
var A = state1 ^ state2;     // will become 1
var B = !!(state1 ^ state2); // will become true
console.log(A);
console.log(B);

Lajos Meszaros
la source
Cela échouera si les opérandes ne sont pas déjà booléens. Une bien meilleure idée estB = ((!state1)!==(!state2))
Doin
C'est vrai, mais vous pouvez toujours nier les opérandes pour les convertir, comme vous l'avez fait si vous n'êtes pas sûr des types: B =!!(!state1 ^ !state2); Aussi, pourquoi tant de parenthèses? B = !state1 !== !state2; Ou vous pouvez même laisser tomber la négation:B = state1 !== state2;
Lajos Meszaros
Les parenthèses sont pour plus de clarté, et aussi pour ne pas avoir à vérifier la documentation sur la priorité des opérateurs lors de l'écriture du code! ;-) Votre dernière expression souffre de ma plainte précédente: elle échoue si les opérandes ne sont pas booléens. Mais si vous êtes sûr qu'ils le sont, alors c'est certainement l'expression "xor logique" la plus simple et la plus rapide.
Fait
Si vous voulez dire par dernière expression state1 !== state2, alors vous n'avez pas besoin de faire de transtypage là-bas, car il !==s'agit d'un opérateur logique, pas d'un bit. 12 !== 4est vrai 'xy' !== trueest également vrai. Si vous utilisiez à la !=place de !==, alors vous devrez faire du casting.
Lajos Meszaros
1
Le résultat des deux !==et !=est toujours booléen ... je ne sais pas quelle est la distinction que vous faites ici, ce n'est absolument pas le problème. Le problème est que l'opérateur XOR que nous voulons est vraiment l'expression (Boolean(state1) !== Boolean(state2)). Pour les booléens, "xy", 12, 4 et true sont toutes des valeurs de vérité et doivent être convertis en true. ainsi ("xy" XOR true)devrait être false, mais ("xy" !== true)est plutôt true, comme vous le faites remarquer. Donc !==ou !=sont (les deux) équivalents à "XOR logique" si et seulement si vous convertissez leurs arguments en booléens avant d' appliquer.
Fait
2

Dans la fonction xor ci-dessus, il en résultera un résultat SIMILAIRE car xor logique ne correspond pas exactement à xor logique, ce qui signifie qu'il en résultera "faux pour des valeurs égales" et "vrai pour différentes valeurs" avec le type de données correspondant en considération.

Cette fonction xor fonctionnera comme un xor réel ou un opérateur logique , ce qui signifie qu'il en résultera vrai ou faux selon que les valeurs de passage sont vraies ou fausses . Utiliser selon vos besoins

function xor(x,y){return true==(!!x!==!!y);}

function xnor(x,y){return !xor(x,y);}
Premchandra Singh
la source
"xnor" est identique à "===".
daniel1426
@ daniel1426 pas tout à fait. C'est la même chose que (!!x) === (!!y). La différence est une conversion en booléen. '' === 0est faux, tandis que xnor('', 0)est vrai.
tschwab
2

Dans Typescript (le + change en valeur numérique):

value : number = (+false ^ +true)

Alors:

value : boolean = (+false ^ +true) == 1
Witold Kaczurba
la source
@Sheraff en javascript normal, !!(false ^ true)fonctionne très bien avec les booléens. En dactylographié, + est nécessaire pour le rendre valide !!(+false ^ +true).
pfg le
1

cond1 xor cond2équivaut à cond1 + cond 2 == 1:

Voici la preuve:

let ops = [[false, false],[false, true], [true, false], [true, true]];

function xor(cond1, cond2){
  return cond1 + cond2 == 1;
}

for(op of ops){
  console.log(`${op[0]} xor ${op[1]} is ${xor(op[0], op[1])}`)
}

madjaoue
la source
0

La raison pour laquelle il n'y a pas de XOR logique (^^) est que contrairement à && et || cela ne donne aucun avantage paresseux. C'est l'état des deux expressions à droite et à gauche qui doivent être évaluées.

user7163886
la source
0

Voici une solution alternative qui fonctionne avec plus de 2 variables et fournit un compte comme bonus.

Voici une solution plus générale pour simuler un XOR logique pour toutes les valeurs de vérité / faux, comme si vous aviez l'opérateur dans des instructions IF standard:

const v1 = true;
const v2 = -1; // truthy (warning, as always)
const v3 = ""; // falsy
const v4 = 783; // truthy
const v5 = false;

if( ( !!v1 + !!v2 + !!v3 + !!v4 + !!v5 ) === 1 )
  document.write( `[ ${v1} XOR ${v2} XOR "${v3}" XOR ${v4} XOR ${v5} ] is TRUE!` );
else
  document.write( `[ ${v1} XOR ${v2} XOR "${v3}" XOR ${v4} XOR ${v5} ] is FALSE!` );

La raison pour laquelle j'aime cela, c'est parce qu'elle répond également "Combien de ces variables sont véridiques?", Donc je pré-stocke généralement ce résultat.

Et pour ceux qui veulent un comportement de vérification strict booléen-TRUE xor, faites simplement:

if( ( ( v1===true ) + ( v2===true ) + ( v3===true ) + ( v4===true ) + ( v5===true ) ) === 1 )
  // etc.

Si vous ne vous souciez pas du nombre, ou si vous vous souciez des performances optimales: utilisez simplement le xor au niveau du bit sur les valeurs forcées à booléen, pour la solution vérité / fausseté:

if( !!v1 ^ !!v2 ^ !!v3 ^ !!v4 ^ !!v5 )
  // etc.
Ciabaros
la source
0

Hé j'ai trouvé cette solution, pour faire et XOR sur JavaScript et TypeScript.

if( +!!a ^ +!!b )
{
  //This happens only when a is true and b is false or a is false and b is true.
}
else
{
  //This happens only when a is true and b is true or a is false and b is false
}
Lucas Solares
la source
-2

Essayez ce court et facile à comprendre

function xor(x,y){return true==(x!==y);}

function xnor(x,y){return !xor(x,y);}

Cela fonctionnera pour tout type de données

Premchandra Singh
la source
3
Cela ne fonctionne pas pour tous les types de données. Comme avec un opérateur de contrainte de type logique, je m'attendrais à ce que "foo" xor "bar" soit faux, car les deux sont véridiques. Ce n'est actuellement pas le cas de votre fonction. Généralement, ce true == somebooleann'est pas nécessaire, donc vraiment, ce que vous avez fait, c'est envelopper les stricts non-égaux dans une fonction.
Gijs
Salut GiJs, je suis d'accord avec votre argument, "foo" et "bar" sont des valeurs de vérité. Mais j'écris la fonction en gardant à l'esprit qu'il en résultera une sortie similaire à celle de xor (des valeurs non égales sont vraies, des valeurs égales sont fausses), pas uniquement pour une valeur vraie / falsifiée. Et j'ai trouvé plus d'utilisation dans un tel scénario. Mais j'écris le vrai xor logique dans une autre réponse ci-dessous.
Premchandra Singh