Donc, c'est plus une question théorique. C++ et les langages (in) directement basés sur celui-ci (Java, C #, PHP) ont opérateurs de raccourci pour affecter le résultat de la plupart des opérateurs binaires au premier opérande, tel que
a += 3; // for a = a + 3
a *= 3; // for a = a * 3;
a <<= 3; // for a = a << 3;
mais quand je veux basculer une expression booléenne, je me retrouve toujours à écrire quelque chose comme
a = !a;
ce qui devient agaçant quand a
est une expression longue comme.
this.dataSource.trackedObject.currentValue.booleanFlag =
!this.dataSource.trackedObject.currentValue.booleanFlag;
(oui, la loi de Demeter, je sais).
Je me demandais donc s'il existait un langage avec un opérateur de basculement booléen unaire qui me permettrait de raccourcir a = !a
sans répéter l'expression de a
, par exemple
!=a;
// or
a!!;
Supposons que notre langage a un type booléen correct (comme bool
en C++) et que a
est de ce type (donc pas de C-style int a = TRUE
).
Si vous pouvez trouver une source documentée, je serais également intéressé de savoir si, par exemple, les concepteurs C++ ont envisagé d'ajouter un opérateur comme celui-ci lorsque bool
devenait un type intégré et, dans l'affirmative, pourquoi ils l'avaient décidé.
(Remarque: je sais que certaines personnes sont d'avis que l'affectation ne devrait pas utiliser =
et que ++
et +=
ne sont pas des opérateurs utiles mais des défauts de conception; supposons que je suis heureux. avec eux et se concentrer sur la raison pour laquelle ils ne s’étendraient pas aux bools).
Cette question est en effet intéressante d'un point de vue purement théorique. Mettant de côté si un opérateur de basculement booléen en mutation, unaire et mutant serait utile, ou pourquoi de nombreuses langues ont choisi de ne pas en fournir un, je me suis aventuré à la recherche de son existence ou non.
TL; DR apparemment non, mais Swift vous permet d'en implémenter un. Si vous souhaitez seulement voir comment cela se passe, vous pouvez aller au bas de cette réponse.
Après une recherche (rapide) sur les caractéristiques de différentes langues, je me risquerais à dire qu'aucune langue n'a implémenté cet opérateur en tant qu'opération in situ en mutation stricte (corrigez-moi si vous en trouvez une). La prochaine chose à faire serait donc de voir s’il existe des langages qui vous permettent d’en construire un. Cela nécessiterait deux choses:
De nombreuses langues seront immédiatement exclues pour non-prise en charge de l'une ou l'autre de ces exigences. Java pour l'un n'autorise pas la surcharge des opérateurs (ou les opérateurs personnalisés) et, en outre, tous les types primitifs sont passés par valeur. Go ne prend pas en charge la surcharge de l'opérateur (sauf par hacks ). Rust n'autorise la surcharge de l'opérateur que pour les types personnalisés. Vous pouvez presque atteindre ceci en Scala , ce qui vous permet d'utiliser des fonctions nommées de manière très créative et d'omettre les parenthèses, mais malheureusement, il n'y a pas passer par référence. Fortran est très proche dans la mesure où il autorise les opérateurs personnalisés, mais leur interdit en particulier d'avoir des paramètres inout (qui sont autorisés dans fonctions normales et sous-routines).
Il existe cependant au moins une langue qui coche toutes les cases nécessaires: Swift . Bien que certaines personnes se soient associées à la fonction membre . Toggle (), vous pouvez également écrire votre propre opérateur, qui prend en charge les arguments inout. Et voilà:
prefix operator ^
prefix func ^ (b: inout Bool) {
b = !b
}
var foo = true
print(foo)
// true
^foo
print(foo)
// false
... cela me permettrait d'abréger _
a = !a
_ sans répéter l'expression poura
...
Cette approche n’est pas vraiment un pur opérateur de "basculement en mutation", mais elle répond aux critères ci-dessus; le côté droit de l'expression n'implique pas la variable elle-même.
Toute langue avec une affectation booléenne XOR (par exemple _^=
_) permettrait de retourner la valeur actuelle d'une variable, par exemple a
, au moyen de XOR affectation à true
:
_// type of a is bool
a ^= true; // if a was false, it is now true,
// if a was true, it is now false
_
Comme l'a souligné @cmaster dans les commentaires ci-dessous, ce qui précède suppose que a
est de type bool
et non pas, par exemple. un entier ou un pointeur. Si a
est en fait quelque chose d'autre (par exemple quelque chose de non -bool
évaluant une valeur de "vérité" ou "fausseté", avec une représentation de bit qui n'est pas _0b1
_ ou _0b0
_, respectivement), le précédent pas tenir.
Par exemple, Java est un langage où il est bien défini et n'est sujet à aucune conversion en mode silencieux. Citant le commentaire de @ Boann ci-dessous:
En Java, _
^
_ et _^=
_ ont explicitement défini le comportement des booléens et des entiers ( 15.22.2. Opérateurs logiques booléens _&
_, _^
_ et _|
_ ), où l’un ou l’autre des deux côtés de l’opérateur doit être un booléen ou un entier. Il n'y a pas de conversion silencieuse entre ces types. Donc, cela ne fonctionnera pas en silence sia
est déclaré comme un entier, mais plutôt une erreur de compilation. Donc _a ^= true;
_ est sécurisé et bien défini en Java.
toggle()
À partir de Swift4.2, la proposition d'évolution suivante a été acceptée et mise en œuvre:
Ceci ajoute une fonction toggle()
native à la Bool
dans Swift.
toggle()
Bascule la valeur de la variable booléenne.
Déclaration
_mutating func toggle()
_Discussion
Utilisez cette méthode pour basculer une valeur booléenne de
true
àfalse
ou defalse
àtrue
._var bools = [true, false] bools[0].toggle() // bools == [false, false]
_
Ce n'est pas un opérateur en soi, mais permet une approche native pour le basculement booléen.
En C++, il est possible de commettre le péché cardinal de redéfinir le sens des opérateurs. Dans cet esprit, et avec un peu d'ADL, tout ce que nous avons à faire pour libérer le chaos de nos utilisateurs est le suivant:
#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;
}
production attendue:
6
0
1
0
1
Tant que nous incluons le langage d'assemblage ...
INVERT
pour un complément binaire.
0=
pour un complément logique (vrai/faux).
La décrémentation d’un C99 bool
aura l’effet souhaité, de même que l’incrémentation ou la décrémentation des types bit
pris en charge par certains dialectes de microcontrôleur minuscule de sorte que tous les nombres pairs sont tronqués à 0 et tous les nombres impairs à 1). Je ne recommanderais pas particulièrement cet usage, en partie parce que je ne suis pas un grand fan de la sémantique du type bool
[à mon humble avis, le type aurait dû spécifier un bool
auquel toute valeur autre que 0 ou 1 est stocké peut se comporter lorsqu'il est lu comme s'il contenait une valeur entière non spécifiée (pas nécessairement cohérente); si un programme tente de stocker une valeur entière dont la valeur n'est pas connue comme 0 ou 1, il doit d'abord utiliser !!
.
Langage d'assemblage
NOT eax
Voir https://www.tutorialspoint.com/Assembly_programming/Assembly_logical_instructions.htm
Je suppose que vous ne choisirez pas un langage basé uniquement sur ceci :-) Dans tous les cas, vous pouvez le faire en C++ avec quelque chose comme:
inline void makenot(bool &b) { b = !b; }
Voir le programme complet suivant pour exemple:
#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);
}
Cette sortie, comme prévu:
false
true
false
PostScript , étant un concaténatif , orienté pile comme Forth, a une bascule unaire, pas . L'opérateur not bascule la valeur en haut de la pile. Par exemple,
true % Push true onto the stack
not % invert the top of stack
% the top of stack is now false
Voir le Manuel de référence du langage PostScript (pdf) , p. 458.
Visual Basic.Net prend en charge cela via une méthode d'extension.
Définissez la méthode d'extension comme suit:
<Extension>
Public Sub Flip(ByRef someBool As Boolean)
someBool = Not someBool
End Sub
Et puis appelez ça comme ça:
Dim someVariable As Boolean
someVariable = True
someVariable.Flip
Ainsi, votre exemple original ressemblerait à quelque chose comme:
me.DataSource.TrackedObject.CurrentValue.BooleanFlag.Flip
Dans Rust, vous pouvez créer votre propre trait pour étendre les types qui implémentent le trait Not
:
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);
}
Vous pouvez également utiliser ^= true
comme suggéré et, dans le cas de Rust, il n'y a pas de problème possible, car false
n'est pas un entier "déguisé" comme en C ou C++:
fn main() {
let mut b = true;
b ^= true;
assert_eq!(b, false);
let mut b = false;
b ^= true;
assert_eq!(b, true);
}
Python supporte une telle fonctionnalité, si la variable a type bool (ce qui est True ou False) avec l'opérateur exclusive or (^=)
:
a = False
a ^= True
print(a) # --> True
a ^= True
print(a) # --> False
En C # :
boolean.variable.down.here ^= true;
L'opérateur booléen ^ est XOR, et XORing avec true est identique à l'inversion.