language agnostic - from - ¿Algún idioma tiene un operador de cambio booleano único?
c++ operator (12)
Así que esta es más una pregunta teórica. C ++ y los lenguajes (in) basados directamente en él (Java, C #, PHP) tienen operadores de acceso directo para asignar el resultado de la mayoría de los operadores binarios al primer operando, como
a += 3; // for a = a + 3
a *= 3; // for a = a * 3;
a <<= 3; // for a = a << 3;
pero cuando quiero cambiar una expresión booleana siempre me encuentro escribiendo algo así como
a = !a;
que se vuelve molesto cuando
a
es una expresión larga como.
this.dataSource.trackedObject.currentValue.booleanFlag =
!this.dataSource.trackedObject.currentValue.booleanFlag;
(Sí, la ley de Demeter, lo sé).
Así que me preguntaba:
¿hay algún lenguaje con un operador de cambio booleano único
que me permita abreviar
a = !a
sin repetir la expresión para
a
, por ejemplo
!=a;
// or
a!!;
Supongamos que nuestro lenguaje tiene un tipo booleano adecuado (como
bool
en C ++) y que
a
es de ese tipo (así que no hay estilo C
int a = TRUE
).
Si puede encontrar una fuente documentada, también me interesaría saber si, por ejemplo, los diseñadores de C ++ han considerado agregar un operador como ese cuando
bool
convirtió en un tipo integrado y, de ser así, por qué decidieron no hacerlo.
(Nota: Sé que algunas personas opinan que la asignación no debería usar
=
y que
++
y
+=
no son operadores útiles sino defectos de diseño; supongamos que estoy contento con ellos y nos centramos en por qué no se extenderían a los bools).
Activar el bit booleano
... eso me permitiría abreviar
a = !a
sin repetir la expresión paraa
...
Este enfoque no es realmente un operador puro de "mutación invertida", pero cumple con los criterios anteriores; El lado derecho de la expresión no implica la variable en sí.
Cualquier idioma con una asignación XOR booleana (por ejemplo,
^=
) permitiría cambiar el valor actual de una variable, por ejemplo
a
, por medio de la asignación XOR a
true
:
// type of a is bool
a ^= true; // if a was false, it is now true,
// if a was true, it is now false
Como lo señala @cmaster en los comentarios a continuación, lo anterior asume que
a
es de tipo
bool
, y no, por ejemplo, un entero o un puntero.
Si
a
es, de hecho, algo más (por ejemplo, algo que no es de tipo
bool
evaluando un valor "verdadero" o "falso", con una representación de bits que no es
0b1
o
0b0
, respectivamente), lo anterior no se cumple.
Para un ejemplo concreto, Java es un lenguaje en el que está bien definido y no está sujeto a conversiones silenciosas. Citando el comentario de @ Boann desde abajo:
En Java,
^
y^=
tienen un comportamiento definido explícitamente para booleanos y para enteros ( 15.22.2. Operadores lógicos booleanos&
,^
, y|
), donde ambos lados del operador deben ser booleanos, o ambos lados deben ser enteros. No hay conversión silenciosa entre esos tipos. Por lo tanto, no funcionará de forma silenciosa sia
se declara como un entero, sino que da un error de compilación. Entoncesa ^= true;
Es seguro y está bien definido en Java.
Swift:
toggle()
A partir de Swift 4.2, la siguiente propuesta de evolución ha sido aceptada e implementada:
Esto agrega una función nativa
toggle()
al tipo
Bool
en Swift.
toggle()
Alterna el valor de la variable booleana.
Declaración
mutating func toggle()
Discusión
Utilice este método para cambiar un valor booleano de
true
afalse
o defalse
atrue
.
var bools = [true, false] bools[0].toggle() // bools == [false, false]
Esto no es un operador, per se, pero permite un enfoque nativo de idioma para la activación booleana.
En pitón
Python admite dicha funcionalidad, si la variable tiene un
tipo bool
(que es Verdadero o Falso) con el operador
exclusive or (^=)
:
a = False
a ^= True
print(a) # --> True
a ^= True
print(a) # --> False
Disminuir un
bool
C99 tendrá el efecto deseado, al igual que aumentar o disminuir los tipos de
bit
admitidos en algunos dialectos de microcontroladores pequeños (que, según he observado, tratan los bits como campos de bits de un solo bit, por lo que todos los números pares se truncan a 0 y todos los números impares a 1).
No recomendaría particularmente este uso, en parte porque no soy un gran fanático de la semántica de tipo
bool
[IMHO, el tipo debería haber especificado que un
bool
para el que se almacena cualquier valor distinto de 0 o 1 puede comportarse cuando se lee como si tuviera un valor entero no especificado (no necesariamente consistente);
Si un programa está intentando almacenar un valor entero que no se sabe que es 0 o 1, ¡debería usarlo
!!
en él primero].
En Rust, puede crear su propio rasgo para extender los tipos que implementan el
Not
rasgo:
use std::ops::Not;
use std::mem::replace;
trait Flip {
fn flip(&mut self);
}
impl<T> Flip for T
where
T: Not<Output = T> + Default,
{
fn flip(&mut self) {
*self = replace(self, Default::default()).not();
}
}
#[test]
fn it_works() {
let mut b = true;
b.flip();
assert_eq!(b, false);
}
También puede usar
^= true
como se sugiere, y en el caso de Rust, no hay ningún problema posible para hacer esto porque
false
no es un entero "disfrazado" como en C o C ++:
fn main() {
let mut b = true;
b ^= true;
assert_eq!(b, false);
let mut b = false;
b ^= true;
assert_eq!(b, true);
}
En c ++ es posible cometer el Pecado Cardinal de redefinir el significado de los operadores. Con esto en mente, y un poco de ADL, todo lo que tenemos que hacer para desatar el caos en nuestra base de usuarios es esto:
#include <iostream>
namespace notstd
{
// define a flag type
struct invert_flag { };
// make it available in all translation units at zero cost
static constexpr auto invert = invert_flag{};
// for any T, (T << invert) ~= (T = !T)
template<class T>
constexpr T& operator<<(T& x, invert_flag)
{
x = !x;
return x;
}
}
int main()
{
// unleash Hell
using notstd::invert;
int a = 6;
std::cout << a << std::endl;
// let confusion reign amongst our hapless maintainers
a << invert;
std::cout << a << std::endl;
a << invert;
std::cout << a << std::endl;
auto b = false;
std::cout << b << std::endl;
b << invert;
std::cout << b << std::endl;
}
Rendimiento esperado:
6
0
1
0
1
Esta pregunta es ciertamente interesante desde un punto de vista puramente teórico. Dejando de lado si un operador de conmutador booleano mutante unario o no sería útil, o por qué muchos idiomas han optado por no proporcionar uno, me aventuré en una búsqueda para ver si realmente existe o no.
TL; DR aparentemente no, pero Swift te permite implementar uno. Si solo desea ver cómo se hace, puede desplazarse hasta el final de esta respuesta.
Después de una búsqueda (rápida) de las características de varios idiomas, me siento seguro de decir que ningún idioma ha implementado este operador como una operación in situ de mutación estricta (corríjame si encuentra una). Entonces, lo siguiente sería ver si hay idiomas que te permitan construir uno. Lo que esto requeriría es dos cosas:
- Poder implementar operadores (unarios) con funciones.
- permitiendo que dichas funciones tengan argumentos de paso por referencia (para que puedan mutar sus argumentos directamente)
Muchos idiomas se descartarán inmediatamente por no admitir ninguno de estos requisitos o ambos. Java for one no permite la sobrecarga de operadores (u operadores personalizados) y, además, todos los tipos primitivos se pasan por valor. Go no tiene soporte para la sobrecarga del operador (excepto por hacks ) en absoluto. La oxidación solo permite la sobrecarga del operador para tipos personalizados. Casi se podría lograr esto en Scala , que le permite usar funciones con un nombre muy creativo y también omitir paréntesis, pero lamentablemente no hay paso por referencia. Fortran se acerca mucho porque permite operadores personalizados, pero específicamente les prohíbe tener parámetros de entrada (que están permitidos en funciones normales y subrutinas).
Sin embargo, hay al menos un idioma que marca todas las casillas necesarias: Swift . Si bien algunas personas se han vinculado a la próxima función miembro .toggle () , también puede escribir su propio operador, que de hecho admite argumentos inout . He aquí y
prefix operator ^
prefix func ^ (b: inout Bool) {
b = !b
}
var foo = true
print(foo)
// true
^foo
print(foo)
// false
Mientras incluyamos el lenguaje ensamblador ...
ADELANTE
INVERT
para un complemento bit a bit.
0=
para un complemento lógico (verdadero / falso).
Supongo que no va a elegir un lenguaje basado únicamente en esto :-) En cualquier caso, puede hacer esto en C ++ con algo como:
inline void makenot(bool &b) { b = !b; }
Vea el siguiente programa completo por ejemplo:
#include <iostream>
inline void makenot(bool &b) { b = !b; }
inline void outBool(bool b) { std::cout << (b ? "true" : "false") << ''/n''; }
int main() {
bool this_dataSource_trackedObject_currentValue_booleanFlag = false;
outBool(this_dataSource_trackedObject_currentValue_booleanFlag);
makenot(this_dataSource_trackedObject_currentValue_booleanFlag);
outBool(this_dataSource_trackedObject_currentValue_booleanFlag);
makenot(this_dataSource_trackedObject_currentValue_booleanFlag);
outBool(this_dataSource_trackedObject_currentValue_booleanFlag);
}
Esto produce, como se esperaba:
false
true
false
Visual Basic.Net admite esto a través de un método de extensión.
Define el método de extensión así:
<Extension>
Public Sub Flip(ByRef someBool As Boolean)
someBool = Not someBool
End Sub
Y luego llámalo así:
Dim someVariable As Boolean
someVariable = True
someVariable.Flip
Entonces, tu ejemplo original se vería algo así como:
me.DataSource.TrackedObject.CurrentValue.BooleanFlag.Flip
En C # :
boolean.variable.down.here ^= true;
El operador booleano ^ es XOR, y XORing con verdadero es lo mismo que invertir.
PostScript , al ser un PostScript concatenative , stack-oriented , como Forth, tiene un interruptor único, no . El operador no cambia el valor en la parte superior de la pila. Por ejemplo,
true % push true onto the stack
not % invert the top of stack
% the top of stack is now false
Consulte el Manual de referencia del lenguaje PostScript (pdf) , pág. 458.
Lenguaje ensamblador
NOT eax
Consulte https://www.tutorialspoint.com/assembly_programming/assembly_logical_instructions.htm