Имеет ли какой-либо язык унарный логический оператор переключения?

Так что это скорее теоретический вопрос. C++ и языки (in), основанные на нем (Java, С#, 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, например

!=a;  
// or
a!!;

Предположим, что наш язык имеет правильный логический тип (например, bool в C++) и что a имеет такой тип (поэтому нет C-стиля int a = TRUE).

Если вы можете найти документальный источник, мне также будет интересно узнать, рассмотрели ли, например, дизайнеры C++ добавление такого оператора, когда bool стал встроенным типом, и если да, то почему они решили против него.


(Примечание: я знаю, что некоторые люди считают, что назначение не должно использовать = и что ++ и += не являются полезными операторами, но создают недостатки, давайте просто предположим, что я доволен ими и сосредоточен на том, почему они не будут распространяться к bools).

Ответ 1

Этот вопрос действительно интересен с чисто теоретической точки зрения. Если оставить в стороне, полезен ли унарный, мутирующий логический оператор переключения, или почему многие языки отказались предоставить его, я отважился на поиски, чтобы узнать, действительно ли он существует.

TL; DR, по- видимому, нет, но Swift позволяет реализовать его. Если вы хотите только посмотреть, как это сделать, вы можете прокрутить нижнюю часть этого ответа.


После (быстрого) поиска функций на разных языках я с уверенностью могу сказать, что ни один язык не реализовал этот оператор как строгую мутирующую операцию на месте (исправьте меня, если вы ее найдете). Итак, следующая вещь должна была бы увидеть, есть ли языки, которые позволят вам построить один. Для этого потребуется две вещи:

  1. возможность реализовать (унарные) операторы с функциями
  2. позволяя упомянутым функциям иметь аргументы pass-by-reference (чтобы они могли напрямую изменять свои аргументы)

Многие языки немедленно исключаются из-за того, что они не поддерживают ни один из этих требований, ни оба эти требования. Java для одного не позволяет перегрузку оператора (или пользовательских операторов), и, кроме того, все примитивные типы передаются по значению. Go не поддерживает перегрузку оператора (за исключением хаков). Rust допускает только перегрузку оператора для пользовательских типов. Вы можете почти достичь этого в Scala, что позволяет использовать очень творчески названные функции, а также пропускать круглые скобки, но, к сожалению, здесь нет пропущенных ссылок. Fortran очень близок к тому, что он позволяет создавать пользовательские операторы, но в частности запрещает им иметь inout параметры (которые разрешены в обычных функциях и подпрограммах).


Однако есть хотя бы один язык, который выделяет все необходимые поля: Swift. Хотя некоторые люди связаны с предстоящей функцией.toggle(), вы также можете написать собственный оператор, который действительно поддерживает inout аргументы. И вот:

prefix operator ^

prefix func ^ (b: inout Bool) {
    b = !b
}

var foo = true
print(foo)
// true

^foo

print(foo)
// false

Ответ 2

Переключение булевского бита

... что позволило бы мне сокращайте a = !a, Не повторяя выражение для... a

Этот подход не является действительно чистым оператором "мутирующего флип", но выполняет ваши критерии выше; правая часть выражения не включает в себя сама переменная.

Любой язык с логическим назначением XOR (например, ^=) позволит перевернуть текущее значение переменной, например, a, посредством присвоения XOR в true:

// type of a is bool
a ^= true;  // if a was false, it is now true,
            // if a was true, it is now false

Как указано в комментариях @cmaster в приведенных ниже комментариях, вышесказанное предполагает, что a имеет тип bool, а не, например, целое число или указатель. Если a на самом деле что-то другое (например, что-то non- bool оценивается как "правдивое" или "ложное" значение, с битовым представлением, которое не является 0b1 или 0b0, соответственно), вышеуказанное не выполняется.

Для конкретного примера Java - это язык, где это четко определено и не подвержено никаким бесшумным преобразованиям. Цитируя комментарий @Boann:

В Java, ^ и ^= имеют явно определенное поведение для булевых и целых чисел (15.22.2. Логические логические операторы &, ^ и |), где либо обе стороны оператора должны быть булевыми, либо обеим сторонам должны быть целые числа. Там нет молчаливого преобразования между этими типами. Таким образом, он не будет молчаливо работать, если a объявлен как целое число, а скорее даст ошибку компиляции. Итак, a ^= true; безопасен и четко определен на Java.


Swift: toggle()

Начиная с Swift 4.2, было принято и реализовано следующее эволюционное предложение:

Это добавляет собственную функцию toggle() к типу Bool в Swift.

toggle()

Переключает значение Boolean variables.

декларация

mutating func toggle()

обсуждение

Используйте этот метод для переключения логического значения с true на false или с false на true.

var bools = [true, false]

bools[0].toggle() // bools == [false, false]

Это не оператор, как таковой, но он позволяет использовать собственный язык для булевых переходов.

Ответ 3

В 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;
}

ожидаемый результат:

6
0
1
0
1

Ответ 4

Пока мы включаем язык ассемблера...

ВПЕРЕД

INVERT для поразрядного дополнения.

0= для логического (истинного/ложного) дополнения.

Ответ 5

Decrement of C99 bool будет иметь желаемый эффект, так как будет увеличивать или уменьшать типы bit поддерживаемые в некоторых крошечных микроконтроллерных диалектах (что из того, что я наблюдал, рассматривал биты как однобитовые битовые поля, поэтому все четные числа усекаются до 0 и все нечетные числа до 1). Я бы особо не рекомендовал такого использования, отчасти потому, что я не большой поклонник семантики типа bool [IMHO, тип должен был указать, что bool в котором хранится любое значение, отличное от 0 или 1, может вести себя при чтении как будто он содержит Unspecified (не обязательно последовательное) целочисленное значение; если программа пытается сохранить целое значение, которое не известно, 0 или 1, оно должно использовать !! сначала на нем].

Ответ 7

Я предполагаю, что вы не будете выбирать язык, основанный исключительно на этом :-) В любом случае вы можете сделать это в C++ с чем-то вроде:

inline void makenot(bool &b) { b = !b; }

См. Следующую полную программу, например:

#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);
}

Это выводит, как и ожидалось:

false
true
false

Ответ 8

PostScript, являющийся конкатенативным, ориентированным на стек языком языком, таким как Forth, имеет унарный переключатель, а не. Оператор not переключает значение поверх стека. Например,

true    % push true onto the stack
not     % invert the top of stack
        % the top of stack is now false

См. Справочное руководство по языку PostScript (pdf), стр. 458.

Ответ 9

Visual Basic.Net поддерживает это с помощью метода расширения.

Определите метод расширения следующим образом:

<Extension>
Public Sub Flip(ByRef someBool As Boolean)
    someBool = Not someBool
End Sub

И затем назовите это так:

Dim someVariable As Boolean
someVariable = True
someVariable.Flip

Итак, ваш оригинальный пример будет выглядеть примерно так:

me.DataSource.TrackedObject.CurrentValue.BooleanFlag.Flip

Ответ 10

В 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);
}

Ответ 11

В Python

Python поддерживает такую функциональность, если переменная имеет тип bool (который является True или False) с exclusive or (^=) оператором:

a = False
a ^= True
print(a)  # --> True
a ^= True
print(a)  # --> False

Ответ 12

В С#:

boolean.variable.down.here ^= true;

Оператор boolean ^ - это XOR, а XORing с истинным - это то же самое, что инвертировать.