Так что это скорее теоретический вопрос. C ++ и языки (в), непосредственно основанные на нем (Java, C #, PHP), имеют операторы быстрого доступа для присвоения результата большинства бинарных операторов первому операнду, например
a += 3; // for a = a + 3
a *= 3; // for a = a * 3;
a <<= 3; // for a = a << 3;
но когда я хочу переключить логическое выражение, я всегда пишу что-то вроде
a = !a;
что раздражает, когда a
это длинное выражение вроде.
this.dataSource.trackedObject.currentValue.booleanFlag =
!this.dataSource.trackedObject.currentValue.booleanFlag;
(да, я знаю Закон Деметры).
Поэтому мне было интересно, есть ли какой-нибудь язык с унарным логическим оператором переключения , который позволил бы мне сокращать, a = !a
не повторяя выражение a
, например
!=a;
// or
a!!;
Предположим, что у нашего языка есть правильный логический тип (например, bool
в C ++), и он a
относится к этому типу (поэтому нет стиля C int a = TRUE
).
Если вы можете найти документированный источник, мне также было бы интересно узнать, рассматривали ли, например, разработчики C ++ возможность добавления такого оператора, когда он bool
стал встроенным типом, и если да, то почему они отказались от него.
(Примечание: я знаю, что некоторые люди считают, что присваивание не следует использовать
=
и что операторы не являются полезными , ++
а +=
являются недостатками дизайна; давайте просто предположим, что я доволен ими, и сосредоточимся на том, почему они не распространяются на bools).
источник
void Flip(bool& Flag) { Flag=!Flag; }
The сокращает ваше длинное выражение.this.dataSource.trackedObject.currentValue.booleanFlag ^= 1;
*= -1
но по некоторым причинам я считаю его более интуитивным, чем^= true
.Ответы:
Этот вопрос действительно интересен с чисто теоретической точки зрения. Не говоря уже о том, будет ли полезен унарный, мутирующий логический оператор переключения , или почему многие языки предпочли его не предоставлять, я рискнул проверить, существует ли он на самом деле.
TL; DR, очевидно, нет, но Swift позволяет вам реализовать его. Если вы хотите только увидеть, как это делается, прокрутите этот ответ до конца.
После (быстрого) поиска функций различных языков я с уверенностью могу сказать, что ни один язык не реализовал этот оператор как операцию строгого изменения на месте (исправьте меня, если вы ее найдете). Итак, следующим шагом будет посмотреть, есть ли языки, которые позволят вам создать такой язык. Для этого потребуются две вещи:
Многие языки будут немедленно исключены за то, что не поддерживают одно или оба этих требования. Например, Java не допускает перегрузки операторов (или пользовательских операторов), и, кроме того, все примитивные типы передаются по значению. Go вообще не поддерживает перегрузку оператора (кроме как путем взлома ). Rust допускает перегрузку операторов только для пользовательских типов. Вы могли бы почти достичь этого в Scala , который позволяет вам использовать очень креативно названные функции, а также опускать скобки, но, к сожалению, здесь нет передачи по ссылке. Фортран очень близок в том, что он позволяет использовать пользовательские операторы, но специально запрещает им иметь inout параметры (которые разрешены в обычных функциях и подпрограммах).
Однако есть по крайней мере один язык, на котором отмечены все необходимые поля: Swift . Хотя некоторые люди связались с предстоящей функцией- членом .toggle () , вы также можете написать свой собственный оператор, который действительно поддерживает inout- аргументы. И вот:
источник
Переключение логического бита
Этот подход на самом деле не является чистым оператором «изменяющегося переворота», но он удовлетворяет указанным выше критериям; правая часть выражения не включает саму переменную.
Любой язык с логическим назначением XOR (например,
^=
) позволит перевернуть текущее значение переменной, скажемa
, с помощью назначения XORtrue
:Как указано @cmaster в комментариях ниже, выше предполагается, что
a
это типbool
, а не, например, целое число или указатель. Еслиa
на самом деле это что-то еще (например, что-то, неbool
оцениваемое до «правдивого» или «ложного» значения, с битовым представлением, которое не является0b1
или0b0
, соответственно), вышеуказанное не выполняется.В качестве конкретного примера, Java - это язык, в котором он четко определен и не подлежит никаким преобразованиям без вывода сообщений. Цитата из комментария @Boann снизу:
Swift:
toggle()
Начиная с Swift 4.2, следующее предложение по развитию было принято и реализовано:
Это добавляет встроенную
toggle()
функцию кBool
типу в Swift.Сам по себе это не оператор, но он позволяет использовать родной язык для логического переключения.
источник
В C ++ можно совершить кардинальный грех переопределения значений операторов. Имея это в виду и немного ADL, все, что нам нужно сделать, чтобы развязать хаос в нашей пользовательской базе, это следующее:
#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; }
ожидаемый результат:
источник
operator<<
стало означать «потоковое» из-за его изначального злоупотребления в STL. Естественно, это не будет означать «применить функцию преобразования к правой части экрана», для чего я, по сути, изменил ее здесь.<<
это оператор битового сдвига, а не оператор «вывода потока». Проблема с вашим переопределением не в том, что оно несовместимо с существующими значениями оператора, а в том, что оно не добавляет значения по сравнению с простым вызовом функции.<<
похоже на плохую перегрузку. Я бы сделал!invert= x;
;)Пока мы включаем ассемблер ...
Четвертый
INVERT
для побитового дополнения.0=
для логического (истина / ложь) дополнения.источник
not
оператор является оператором переключения :-)Уменьшение C99
bool
будет иметь желаемый эффект, так же как будет увеличивать или уменьшатьbit
типы, поддерживаемые в некоторых диалектах крошечных микроконтроллеров (которые из того, что я наблюдал, рассматривают биты как однобитовые битовые поля, поэтому все четные числа усекаются до 0 и все нечетные числа до 1). Я бы не особо рекомендовал такое использование, отчасти потому, что я не большой поклонникbool
семантики типа [IMHO, тип должен был указывать, что a,bool
в котором хранится любое значение, отличное от 0 или 1, может вести себя при чтении как если бы он содержит неопределенное (не обязательно непротиворечивое) целочисленное значение; если программа пытается сохранить целочисленное значение, которое, как известно, не равно 0 или 1, она должна!!
сначала использовать его].источник
bool
до C ++ 17 .язык ассемблера
См. Https://www.tutorialspoint.com/assembly_programming/assembly_logical_instructions.htm
источник
; here edx would be 0xFFFFFFFE because a bitwise NOT 0x00000001 = 0xFFFFFFFE
if(x)
конструкции , вполне разумно разрешить 0 / -1 как false / true, как для сравнения SIMD ( felixcloutier.com/x86/PCMPEQB:PCMPEQW:PCMPEQD.html ). Но вы правы, что это не работает, если вы используете его для любого другого значения.Я предполагаю, что вы не собираетесь выбирать язык, основываясь исключительно на этом :-) В любом случае вы можете сделать это на C ++ примерно так:
См., Например, следующую полную программу:
Это выводит, как и ожидалось:
источник
return b = !b
тоже? Кто-то, читающийfoo = makenot(x) || y
или простой,foo = makenot(bar)
может предположить, чтоmakenot
это чистая функция, и предположить, что не было никаких побочных эффектов. Скрывать побочный эффект внутри более крупного выражения, вероятно, является плохим стилем, и единственное преимущество непустого возвращаемого типа - это включение этого.template<typename T> T& invert(T& t) { t = !t; return t; }
, что шаблон будет преобразован в / из,bool
если используется на не-bool
объекте. IDK, хорошо это или плохо. Предположительно хорошо.PostScript , будучи конкатенативным , стек-ориентированный язык , как Forth, имеет одноместную тумблер, не . Оператор not переключает значение на вершине стека. Например,
См. Справочное руководство по языку PostScript (pdf) , стр. 458.
источник
Visual Basic.Net поддерживает это с помощью метода расширения.
Определите метод расширения следующим образом:
А потом назовите это так:
Итак, ваш исходный пример будет выглядеть примерно так:
источник
Flip()
:=
иNot
. Интересно узнать, что в случае вызоваSomeInstance.SomeBoolean.Flip
он работает, даже еслиSomeBoolean
является свойством, тогда как эквивалентный код C # не компилируется.В Rust вы можете создать свой собственный трейт для расширения типов, реализующих
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); }
Вы также можете использовать,
^= true
как было предложено, и в случае Rust нет никаких проблем с этим, потому чтоfalse
это не "замаскированное" целое число, как в C или C ++:fn main() { let mut b = true; b ^= true; assert_eq!(b, false); let mut b = false; b ^= true; assert_eq!(b, true); }
источник
В Python
Python поддерживает такую функциональность, если переменная имеет тип bool (True или False) с
exclusive or (^=)
оператором:источник
В C # :
Логический оператор ^ - это XOR, а операция XOR с истиной аналогична инвертированию.
источник