Por que não há xor lógico em JavaScript?

Respostas:

358

O JavaScript rastreia sua ascendência de volta a C, e C não possui um operador XOR lógico. Principalmente porque não é útil. O Bitwise XOR é extremamente útil, mas em todos os meus anos de programação nunca precisei de um XOR lógico.

Se você tiver duas variáveis ​​booleanas, poderá imitar o XOR com:

if (a != b)

Com duas variáveis ​​arbitrárias, você pode usar !para forçá-las a valores booleanos e, em seguida, usar o mesmo truque:

if (!a != !b)

Isso é bastante obscuro e certamente merece um comentário. Na verdade, você pode até usar o operador XOR bit a bit neste momento, embora isso seja muito inteligente para o meu gosto:

if (!a ^ !b)
John Kugelman
fonte
O único problema !=é que você não pode fazer o mesmo que a ^= b, porque a !== bé apenas o operador de desigualdade estrita .
mcpiroman 2/01
79

Javascript tem um operador XOR bit a bit: ^

var nb = 5^9 // = 12

Você pode usá-lo com booleanos e ele fornecerá o resultado como 0 ou 1 (que você pode converter novamente em booleano, por exemplo result = !!(op1 ^ op2)). Mas, como John disse, é equivalente result = (op1 != op2), o que é mais claro.

Pikrass
fonte
28
Que de bit a bit XOR, não XOR lógico
Ismail Badawi
66
Você pode usá-lo como um xor lógico. true^trueé 0 e false^trueé 1.
Pikrass
14
@Pikrass Você pode usá-lo como um operador lógico em booleanos , mas não em outros tipos. ||e &&pode ser usado como operadores lógicos em não-booleanos (por exemplo 5 || 7, "bob" && nullretorna um valor verdadeiro , retorna um valor falsey), mas ^não pode. Por exemplo, 5 ^ 7é igual a 2, que é verdade.
Mark Amery
10
@Pikrass Mas, infelizmente, (true ^ false) !== trueque o torna irritante com bibliotecas que exigem booleans reais
Izkata
2
@Pikrass Você nunca deve usá-lo como um operador lógico em booleano, porque a implementação depende do sistema operacional. Eu estava usando algum tipo de a ^= truealternância de booleanos e ele falha em algumas máquinas, como telefones.
Masadow
30

Não há operadores booleanos lógicos reais em Javascript (embora !cheguem bem perto). Um operador lógico usaria apenas trueou falsecomo operandos e retornaria trueoufalse .

Em Javascript &&e|| tomar todos os tipos de operandos e devolver todos os tipos de resultados engraçados (o que você alimentam-los).

Além disso, um operador lógico sempre deve levar em consideração os valores de ambos os operandos.

Em Javascript &&e ||tomar um atalho preguiçoso e que não avalia o segundo operando em certos casos e, assim, negligenciar seus efeitos colaterais. É impossível recriar esse comportamento com um xor lógico.


a() && b()avalia a()e retorna o resultado se for falso. Caso contrário, ele avalia b()e retorna o resultado. Portanto, o resultado retornado é verdadeiro se ambos forem verdadeiros e, caso contrário, falso.

a() || b()avalia a()e retorna o resultado, se for verdade. Caso contrário, ele avalia b()e retorna o resultado. Portanto, o resultado retornado é falso se ambos os resultados forem falsos e, de outra forma, verdadeiro.

Portanto, a ideia geral é avaliar primeiro o operando esquerdo. O operando certo é avaliado apenas se necessário. E o último valor é o resultado. Este resultado pode ser qualquer coisa. Objetos, números, strings .. tanto faz!

Isso possibilita escrever coisas como

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

ou

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

Mas o valor verdadeiro desse resultado também pode ser usado para decidir se um operador lógico "real" retornaria verdadeiro ou falso.

Isso possibilita escrever coisas como

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

ou

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

Mas um operador xor "lógico" ( ^^) sempre precisaria avaliar os dois operandos. Isso o torna diferente dos outros operadores "lógicos" que avaliam o segundo operando somente se necessário. Eu acho que é por isso que não há xor "lógico" em Javascript, para evitar confusão.


Então, o que deve acontecer se ambos os operandos forem falsos? Ambos podem ser devolvidos. Mas apenas um pode ser devolvido. Qual? O primeiro? Ou o segundo? Minha intuição me diz para retornar os primeiros operadores, mas geralmente "lógicos", da esquerda para a direita e retornar o último valor avaliado. Ou talvez uma matriz contendo os dois valores?

E se um operando é verdadeiro e o outro operando é falso, um xor deve retornar o verdadeiro. Ou talvez uma matriz contendo a verdade, para torná-la compatível com o caso anterior?

E, finalmente, o que deve acontecer se os dois operandos forem verdadeiros? Você esperaria algo falso. Mas não há resultados falsos. Portanto, a operação não deve retornar nada. Então talvez undefinedou .. uma matriz vazia? Mas uma matriz vazia ainda é verdadeira.

Tomando a abordagem de matriz, você acabaria com condições como if ((a ^^ b).length !== 1) {. Muito confuso.

Robert
fonte
O XOR / ^^ em qualquer idioma sempre terá que avaliar os dois operandos, pois sempre depende de ambos. O mesmo vale para AND / &&, já que todos os operandos devem ser verdadeiros (verdadeiro em JS). A exceção é OR / || pois ele deve apenas avaliar operandos até encontrar um valor verdadeiro. Se o primeiro operando em uma lista OR for verdadeiro, nenhum dos outros será avaliado.
Percy
No entanto, você argumenta que o XOR no JS precisaria quebrar a convenção estabelecida por AND e OR. Teria realmente que retornar um valor booleano adequado em vez de um dos dois operandos. Qualquer outra coisa pode causar confusão / complexidade.
Percy
9
O @Percy AND / && não avalia o segundo operando se o primeiro for falso. Ele avalia apenas operandos até encontrar um valor falso.
Robert
@DDS Obrigado por corrigir a resposta. Estou confusa sobre o motivo de eu não ter percebido isso. Talvez isso explique a confusão de Percy até certo ponto.
Robert
Minha edição foi rejeitada, após o que o @matts a reeditou exatamente da maneira que eu a corrigi, então perdi meus 2 pontos. 3 pessoas rejeitaram e estou desconcertado com o que eles usaram como critério. Thx matts.
DDS
16

O XOR de dois booleanos é simplesmente se eles são diferentes, portanto:

Boolean(a) !== Boolean(b)
DomQ
fonte
12

Converta valores no formato booleano e, em seguida, use o XOR bit a bit. Também ajudará com valores não-booleanos.

Boolean(a) ^ Boolean(b)
Aman Kaushal
fonte
10

Encoberto para booleano e, em seguida, execute xor como -

!!a ^ !!b
toyeca
fonte
1
Note que !!a ^ !!bé equivalente a !a ^ !b. Argumentos poderiam ser feitos sobre qual é mais fácil de ler.
tschwab 8/11/19
9

existe ... mais ou menos:

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

ou mais fácil de ler:

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

porque? Não sei.

porque os desenvolvedores de javascript pensaram que seria desnecessário, pois pode ser expresso por outros operadores lógicos já implementados.

você também pode simplesmente usar o nand e é isso, pode impressionar qualquer outra operação lógica possível a partir disso.

Pessoalmente, acho que há razões históricas que partem de linguagens de sintaxe baseadas em c, onde, até onde sei, xor não está presente ou, pelo menos, extremamente incomum.

O Surricano
fonte
Sim, o javascript possui operações ternárias.
mwilcox
C e Java possuem XOR usando o caractere ^ (circunflexo).
veidelis
7

Sim, faça o seguinte. Supondo que você esteja lidando com os booleanos A e B, o valor A XOR B pode ser calculado em JavaScript usando o seguinte

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

A linha anterior também é equivalente à seguinte

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

Pessoalmente, prefiro xor1, pois tenho que digitar menos caracteres. Eu acredito que o xor1 também é mais rápido também. É apenas realizar dois cálculos. O xor2 está realizando três cálculos.

Explicação visual ... Leia a tabela abaixo (onde 0 significa falso e 1 significa verdadeiro) e compare as 3ª e 5ª colunas.

! (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     |
------------------------------------------

Aproveitar.

asiby
fonte
6
var xor1 = !(a === b);é o mesmo quevar xor1 = a !== b;
daniel1426
Esta resposta não funcionará para todos os tipos de dados (como a resposta de Premchandra). por exemplo , !(2 === 3)é true, mas 2e 3é verdade , 2 XOR 3deveria ser false.
Mariano Desanze
2
Se você tivesse lido minha mensagem com mais cuidado, teria notado que eu escrevi "Supondo que você esteja lidando com os booleanos A e B ...".
asiby
5

Verificação de saída:

Você pode imitar algo assim:

if( ( foo && !bar ) || ( !foo && bar ) ) {
  ...
}
Sarfraz
fonte
3
Ei, se eles adicionassem um operador XOR lógico ao JavaScript, o exemplo de código pareceria muito mais limpo.
Danyal Aytekin
4

Que tal transformar o resultado int em um bool com dupla negação? Não é tão bonito, mas muito compacto.

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
fonte
Isso falhará se os operandos ainda não forem booleanos. Muito melhor idéia éB = ((!state1)!==(!state2))
Doin
É verdade, mas você sempre pode negar os operandos para convertê-los, como fez se não tiver certeza dos tipos: B =!!(!state1 ^ !state2); Além disso, por que tantos parênteses? B = !state1 !== !state2; Ou você pode até ignorar a negação:B = state1 !== state2;
Lajos Meszaros
Os parênteses são para maior clareza, e também para que eu não precise verificar os documentos sobre a precedência do operador ao escrever código! ;-) Sua última expressão sofre com a minha reclamação anterior: falha se os operandos não forem booleanos. Mas se você tem certeza de que é, é definitivamente a expressão mais simples e rápida do "xor lógico".
Doin
Se por última expressão você quer dizer state1 !== state2, não é necessário fazer nenhuma conversão lá, pois !==é um operador lógico, não um pouco. 12 !== 4é verdade 'xy' !== truetambém é verdade. Se você usasse em !=vez de !==, teria que fazer a transmissão.
Lajos Meszaros
1
O resultado de ambos !==e !=é sempre booleano ... não sei qual deveria ser a distinção que você está fazendo, esse não é absolutamente o problema. O problema é que o operador XOR que queremos é realmente a expressão (Boolean(state1) !== Boolean(state2)). Para booleanos, "xy", 12, 4 e true são todos os valores verdadeiros e devem ser convertidos em true. assim ("xy" XOR true)deve ser false, mas ("xy" !== true)é true, como você aponta. Portanto, !==ou !=são (ambos) equivalentes a "XOR lógico" se e somente se você converter seus argumentos em booleanos antes de aplicar.
Doin
2

Na função xor acima, ele resultará em resultado SIMILAR, pois xor lógico não é exatamente xor lógico, significa que resultará "falso para valores iguais" e "verdadeiro para valores diferentes", com a consideração do tipo de dados em consideração.

Esta função XOR funcionará como xor real ou operador lógico , meios que irão resultar verdadeira ou falsa acordo com os valores que passam são truthy ou Falsas . Use de acordo com suas necessidades

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

function xnor(x,y){return !xor(x,y);}
Premchandra Singh
fonte
"xnor" é o mesmo que "===".
Daniel1426
@ daniel1426 não é bem assim. É o mesmo que (!!x) === (!!y). A diferença é uma conversão para booleano. '' === 0é falso, enquanto xnor('', 0)é verdadeiro.
tschwab 8/11/19
2

Em Texto datilografado (o + muda para o valor numérico):

value : number = (+false ^ +true)

Assim:

value : boolean = (+false ^ +true) == 1
Witold Kaczurba
fonte
@ Sheraff em javascript normal, !!(false ^ true)funciona bem com booleanos. No texto datilografado, + é necessário para torná-lo válido !!(+false ^ +true).
pfg 23/02
1

cond1 xor cond2é equivalente a cond1 + cond 2 == 1:

Aqui está a prova:

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
fonte
0

A razão pela qual não existe um XOR lógico (^^) é porque diferente de && e || não oferece nenhuma vantagem de lógica preguiçosa. Esse é o estado de ambas as expressões à direita e à esquerda devem ser avaliadas.

user7163886
fonte
0

Aqui está uma solução alternativa que funciona com mais de 2 variáveis ​​e fornece contagem como bônus.

Aqui está uma solução mais geral para simular XOR lógico para quaisquer valores de verdade / falsey, como se você tivesse o operador nas instruções IF padrão:

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!` );

A razão de eu gostar disso é porque ela também responde "Quantas dessas variáveis ​​são verdadeiras?", Então eu costumo pré-armazenar esse resultado.

E para aqueles que desejam um comportamento estrito de verificação booleana-TRUE xor, basta fazer:

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

Se você não se importa com a contagem ou com o desempenho ideal: basta usar o xor bit a bit em valores coagidos a booleanos, para a solução de verdade / falsidade:

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

Ei, eu encontrei esta solução, para fazer e XOR em JavaScript e 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
fonte
-2

Experimente este breve e fácil de entender

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

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

Isso funcionará para qualquer tipo de dados

Premchandra Singh
fonte
3
Isso não funciona para todos os tipos de dados. Como em um operador de coação de tipo lógico, eu esperaria que "foo" xor "bar" fosse falso, porque ambos são verdadeiros. Atualmente, esse não é o seu caso. Geralmente, true == somebooleannão é necessário fazer, então, na verdade, o que você fez é agrupar os estritos não iguais em uma função.
Gijs
Oi GiJs, eu concordo com o seu argumento, "foo" e "bar" são verdadeiros valores. Mas escrevo a função tendo em mente que ela resultará em uma saída semelhante à do xor (valores não iguais resultam verdadeiros, valores iguais resultam falso), não apenas para o valor verdade / falsidade. E eu encontrei mais uso nesse cenário. Mas estou escrevendo o verdadeiro xor lógico em outra resposta abaixo.
Premchandra Singh