Por que não há xor lógico em JavaScript?
Por que não há xor lógico em JavaScript?
Respostas:
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)
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.
true^true
é 0 e false^true
é 1.
||
e &&
pode ser usado como operadores lógicos em não-booleanos (por exemplo 5 || 7
, "bob" && null
retorna um valor verdadeiro , retorna um valor falsey), mas ^
não pode. Por exemplo, 5 ^ 7
é igual a 2, que é verdade.
(true ^ false) !== true
que o torna irritante com bibliotecas que exigem booleans reais
a ^= true
alternância de booleanos e ele falha em algumas máquinas, como telefones.
Não há operadores booleanos lógicos reais em Javascript (embora !
cheguem bem perto). Um operador lógico usaria apenas true
ou false
como operandos e retornaria true
oufalse
.
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 undefined
ou .. 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.
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)
Encoberto para booleano e, em seguida, execute xor como -
!!a ^ !!b
!!a ^ !!b
é equivalente a !a ^ !b
. Argumentos poderiam ser feitos sobre qual é mais fácil de ler.
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.
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.
var xor1 = !(a === b);
é o mesmo quevar xor1 = a !== b;
!(2 === 3)
é true
, mas 2
e 3
é verdade , 2 XOR 3
deveria ser false
.
Verificação de saída:
Você pode imitar algo assim:
if( ( foo && !bar ) || ( !foo && bar ) ) {
...
}
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);
B = ((!state1)!==(!state2))
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;
state1 !== state2
, não é necessário fazer nenhuma conversão lá, pois !==
é um operador lógico, não um pouco. 12 !== 4
é verdade 'xy' !== true
também é verdade. Se você usasse em !=
vez de !==
, teria que fazer a transmissão.
!==
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.
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);}
(!!x) === (!!y)
. A diferença é uma conversão para booleano. '' === 0
é falso, enquanto xnor('', 0)
é verdadeiro.
Em Texto datilografado (o + muda para o valor numérico):
value : number = (+false ^ +true)
Assim:
value : boolean = (+false ^ +true) == 1
!!(false ^ true)
funciona bem com booleanos. No texto datilografado, + é necessário para torná-lo válido !!(+false ^ +true)
.
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])}`)
}
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.
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.
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
}
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
true == someboolean
não é necessário fazer, então, na verdade, o que você fez é agrupar os estritos não iguais em uma função.
!=
é que você não pode fazer o mesmo quea ^= b
, porquea !== b
é apenas o operador de desigualdade estrita .