모든 언어에 단항 부울 토글 연산자가 있습니까?


141

따라서 이것은 이론적 인 질문입니다. 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!!;

우리 언어에 적절한 부울 유형 ( boolC ++에서 와 같이 )이 있고 a그 유형 (C 스타일이 없음 ) 이라고 가정 해 봅시다 int a = TRUE.

문서화 된 소스를 찾을 수 있다면 C ++ 디자이너가 bool내장 유형 이되었을 때와 같은 연산자를 추가하는 것을 고려했는지 여부와 그 이유에 대해 결정한 이유를 알고 싶습니다 .


(참고 : 나는 어떤 사람들은 할당은 사용하지 말아야한다는 의견 것을 알고 =것을 ++하고 +=유용한 사업자하지만 설계 결함 있지 않으며,하자 그냥 나는 그들이 bools까지 확장되지 이유에 그들과 초점 행복 해요 가정 것).


31
함수 void Flip(bool& Flag) { Flag=!Flag; }는 어떻습니까? 긴 표현이 짧아집니다.
하퍼

72
this.dataSource.trackedObject.currentValue.booleanFlag ^= 1;
KamilCuk

13
코드를 단순화하기 위해 긴 표현식을 참조에 할당 할 수 있습니다
Quentin 2

6
@KamilCuk이 방법은 효과가 있지만 혼합 유형입니다. 부울에 정수를 할당합니다.
harper

7
@ user463035818이 *= -1있지만 어떤 이유로 든보다 직관적 ^= true입니다.
CompuChip

답변:


15

이 질문은 순전히 이론적 인 관점에서 실제로 흥미 롭습니다. 단항, 가변 부울 토글 연산자 가 유용한 지 여부 또는 많은 언어가 언어를 제공하지 않기로 선택한 이유를 제쳐두고 나는 실제로 존재하는지 여부를 알아보기 위해 탐구했습니다.

TL; DR은 분명히 아니요, 그러나 Swift를 사용하면 구현할 수 있습니다. 완료된 방법 만보고 싶다면이 답변의 맨 아래로 스크롤하면됩니다.


다양한 언어의 기능을 (빠른) 검색 한 후에는이 연산자를 엄격한 변경 위치 내 작업으로 구현 한 언어가 없다고 말하는 것이 안전합니다 (찾을 경우 수정하십시오). 다음은 언어를 만들 수있는 언어가 있는지 확인하는 것입니다. 이것이 필요한 것은 두 가지입니다.

  1. 함수로 (단항) 연산자를 구현할 수있는 것
  2. 상기 함수가 참조에 의한 전달 인수를 갖도록 허용 (그들은 인수를 직접 변경시킬 수 있음)

이러한 요구 사항 중 하나 또는 둘 다를 지원하지 않으면 많은 언어가 즉시 배제됩니다. 하나의 Java 는 연산자 오버로드 (또는 사용자 정의 연산자)를 허용하지 않으며 모든 기본 유형이 값으로 전달됩니다. Go 는 연산자 오버로드 ( 핵을 제외하고 )를 전혀 지원하지 않습니다. 녹은 사용자 정의 유형에 대해서만 연산자 오버로드를 허용합니다. 당신은 수있는 거의 에서이를 스칼라 당신은 매우 창의적이라는 기능도 생략 괄호를 사용하자,하지만 슬프게도 더 통과에 의해 참조가 없습니다. 포트란 은 사용자 정의 사업자 수 있다는 점에서 매우 가까운 얻을 수 있지만, 특별히 필요에서 그들을 금지 입출력 매개 변수 (정상적인 기능 및 서브 루틴에서 허용됨).


그러나 필요한 모든 상자를 선택하는 언어가 하나 이상 있습니다 : Swift . 어떤 사람들은 곧 연결했지만 .toggle () 멤버 함수, 당신은 또한 참으로 지원 자신의 연산자 쓸 수 inout를 인수. 보라.

prefix operator ^

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

var foo = true
print(foo)
// true

^foo

print(foo)
// false

6
언어는 이것을 제공 할 것입니다 : github.com/apple/swift-evolution/blob/master/proposals/…
Cristik

3
예, 그러나 질문 은 멤버 함수가 아닌 연산자를 구체적으로 요청했습니다 .
Lauri Piispanen

4
"
녹도

3
@Boiethios 감사합니다! Rust 용으로 편집-사용자 정의 유형에 대해 오버로드 된 연산자 만 허용하므로 주사위는 없습니다.
Lauri Piispanen

3
@LauriPiispanen이 규칙은 그보다 더 일반적이며, 고아 규칙 으로 인해 FYI
Boiethios

143

부울 비트 토글

...에 대한 표현을 반복하지 않고 약어 a = !a 를 사용할 수 있습니다.a ...

이 접근법은 실제로 순수한 "돌연변이 플립"연산자는 아니지만 위의 기준을 충족시킵니다. 표현식의 오른쪽에는 변수 자체가 포함되지 않습니다.

부울 XOR 할당 (예 :)이있는 모든 언어 ^=는 변수의 현재 값을 예를 들어 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

아래 주석에서 @cmaster가 지적한 것처럼 위 a의 형식 bool은 정수 또는 포인터가 아닌 유형 이라고 가정합니다 . 경우 a(예를 들어, 뭔가 아닌 다른 사실 뭔가에 bool없는 비트 표현과 더불어, "truthy"또는 "falsy"값으로 평가 0b1하거나0b0 각각), 위에서 보유하지 않습니다.

구체적인 예를 들어, Java는 잘 정의 된 언어이며 자동 변환이 적용되지 않는 언어입니다. 아래에서 @Boann의 의견 인용 :

자바에서, ^그리고 ^=명시 적 논리 값과 정수에 대한 동작을 정의 ( 15.22.2. 부울 논리 연산자 &, ^그리고| ) 연산자의 양쪽 중 하나가 논리 값이어야 경우, 또는 양쪽 모두의 정수이어야합니다. 이러한 유형 간에는 자동 변환이 없습니다. 따라서 자동으로 오작동하지 않습니다.a 정수로 선언 않지만 컴파일 오류가 발생합니다. 그래서 a ^= true;안전하고 자바에서 잘 정의이다.


빠른: toggle()

스위프트 4.2에서 다음과 같은 진화 제안이 받아 들여지고 이행되었다 :

이것은 Swift toggle()Bool타입에 네이티브 함수를 추가합니다 .

toggle()

부울 변수 값을 토글합니다.

선언

mutating func toggle()

토론

부울 값을에서 truefalse또는에서 false로 부울 때이 방법을 사용하십시오 true.

var bools = [true, false]

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

이것은 그 자체로는 연산자가 아니지만 부울 토글에 대한 언어 고유 접근 방식을 허용합니다.


3
의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Samuel Liew

44

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

9
“연산자의 의미를 재정의하는 추기경 죄”— 나는 당신이 과장하고 있다는 것을 알고 있지만, 기존의 사업자들에게 일반적으로 새로운 의미를 재 할당하는 것은 진정한 죄가 아닙니다 .
Konrad Rudolph

5
@KonradRudolph 물론 이것은 운영자가 일반적인 산술 또는 논리적 의미와 관련하여 논리적으로 이해해야한다는 것을 의미합니다. 연결이 덧셈 또는 누적과 비슷하기 때문에 2 개의 문자열에 대한 'operator +'는 논리적입니다. operator<<STL의 원래 남용으로 인해 '스트리밍 아웃'을 의미하게되었습니다. 그것은 본질적으로 'RHS에 변환 기능을 적용하십시오'를 의미하지는 않을 것입니다. 이것은 본질적으로 여기서 다시 용도를 바 꾸었습니다.
Richard Hodges

6
좋은 주장이라고 생각하지 않습니다. 죄송합니다. 우선, 문자열 연결은 덧셈과 완전히 다른 의미를 갖 습니다 (정상적인 것도 아닙니다!). 둘째, 논리 <<에 따르면 "스트림 출력"연산자가 아닌 비트 시프트 연산자입니다. 재정의 문제는 기존 연산자의 의미와 일치하지 않는 것이 아니라 단순한 함수 호출보다 가치가 없다는 것입니다.
Konrad Rudolph

8
<<과부하가 심한 것 같습니다. 나는 할 것이다 !invert= x;;)
야크-아담 네 브라우 몬트

12
@ Yakk-AdamNevraumont LOL, 이제 시작했습니다 : godbolt.org/z/KIkesp
Richard Hodges

37

어셈블리 언어를 포함하는 한 ...

앞으로

INVERT 비트 단위 보완.

0= 논리적 (참 / 거짓) 보완의 경우.


4
논쟁의 여지가 있지만, 모든 스택 지향 언어에서 단항 not은 "토글"연산자입니다 : –)
Bergi

2
물론 OP가 전통적인 할당 문이있는 C와 같은 언어에 대해 명확하게 생각하고 있기 때문에 약간의 측면 대답입니다. 독자가 생각하지 못한 프로그래밍 세계의 일부만 보여 주기만한다면 옆에있는 답변은 가치가 있다고 생각합니다. ( "우리의 철학에서 꿈꾸는 것보다 더 많은 프로그래밍 언어가 하늘과 땅에 있습니다.")
Wayne Conrad

31

C99를 줄이면 작은 마이크로 컨트롤러 방언에서 지원되는 유형을 bool늘리거나 줄일 수 있기 때문에 원하는 효과를 얻을 수 bit있습니다 (관찰 한 바에 따르면 비트를 단일 비트 폭 비트 필드로 취급하므로 모든 짝수는 0으로 잘립니다. 홀수는 1). bool형식 의미론 의 큰 팬이 아니기 때문에 특히 그러한 사용법을 권장하지는 않습니다. [IMHO, 형식은 bool0 또는 1 이외의 값이 저장되어있는 것처럼 읽을 때 동작 할 수 있도록 지정해야합니다 불특정 한 정수 값을 가질 수 있습니다. 프로그램이 0 또는 1로 알려지지 않은 정수 값을 저장하려고 !!하면 먼저 사용해야 합니다].


2
C ++는 bool C ++ 17까지 동일한 작업을 수행했습니다 .
Davis Herring

31

2
나는 이것이 x86 어셈블리라고 가정 할 때 이것이 op가 생각한 것이라고 생각하지 않습니다. 예 : en.wikibooks.org/wiki/X86_Assembly/Logic ; here edx would be 0xFFFFFFFE because a bitwise NOT 0x00000001 = 0xFFFFFFFE
BurnsBA

8
대신 모든 비트를 사용할 수 있습니다. NOT 0x00000000 = 0xFFFFFFFF
Adrian

5
글쎄, 그래도 부울 연산자와 비트 연산자를 구별하려고 노력했지만. 이 질문에서 op가 말했듯이 언어에 잘 정의 된 부울 유형이 있다고 가정합니다. "(C 스타일 int a = TRUE가 없음)"
BurnsBA

5
@BurnsBA : 실제로, 어셈블리 언어에는 잘 정의 된 진실 / 거짓 값의 단일 세트가 없습니다. 코드 골프에서 다양한 언어로 부울 문제에 대해 반환 할 수있는 값에 대해 많이 논의되었습니다. asm에는 if(x)구문 이 없으므로 SIMD 비교 ( felixcloutier.com/x86/PCMPEQB:PCMPEQW:PCMPEQD.html ) 와 같이 0 / -1을 false / true로 허용하는 것이 합리적 입니다. 그러나 다른 값으로 사용하면 이것이 깨지는 것이 맞습니다.
Peter Cordes

4
PCMPEQW의 결과가 0 / -1이지만 SETcc의 결과가 0 / + 1 인 경우 단일 부울 표현이 어렵습니다.
Eugene Styer

28

나는 당신이 이것에 기초하여 언어를 선택하지 않을 것이라고 가정하고 있습니다 :-) 어쨌든, 당신은 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

2
당신도 원 return b = !b하십니까? 누군가 읽 foo = makenot(x) || y거나 단순한 것은 foo = makenot(bar)makenot기능이 순수한 기능이라고 가정하고 부작용이 없다고 가정 할 수 있습니다. 더 큰 표현식에 부작용을 묻는 것은 좋지 않은 스타일 일 수 있으며, 무효가 아닌 반환 유형의 유일한 이점은 가능합니다.
Peter Cordes

2
@MatteoItalia는 template<typename T> T& invert(T& t) { t = !t; return t; } 템플릿이 bool아닌 bool객체 에서 사용 되는 경우 템플릿을 변환 합니다. 그것이 좋거나 나쁘면 IDK. 아마 좋습니다.
Peter Cordes


21

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

2
이것은 저자가 찾고있는 속기 기능을 수행하지만, 구현하기 위해 두 연산자를 사용해야합니다 Flip(): =Not. 호출 SomeInstance.SomeBoolean.Flip하는 경우 SomeBoolean속성 인 경우에도 작동 하지만 동등한 C # 코드는 컴파일되지 않습니다.
BACON

14

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의 경우 falseC 또는 C ++에서와 같이 "변장 된"정수가 아니기 때문에 가능한 문제 는 없습니다.

fn main() {
    let mut b = true;
    b ^= true;
    assert_eq!(b, false);

    let mut b = false;
    b ^= true;
    assert_eq!(b, true);
}


3

C #에서 :

boolean.variable.down.here ^= true;

부울 ^ 연산자는 XOR이며 true를 사용한 XORing은 반전과 동일합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.