Herhangi bir dilde tek yönlü bir boole değiştirme operatörü var mı?


141

Bu daha çok teorik bir soru. C ++ ve diller (in) doğrudan buna dayalı olarak (Java, C #, PHP) çoğu ikili işleçin sonucunu ilk işlenene atamak için kısayol işleçlerine sahiptir .

a += 3;   // for a = a + 3
a *= 3;   // for a = a * 3;
a <<= 3;  // for a = a << 3;

ama boole ifadesini değiştirmek istediğimde kendimi hep böyle bir şey yazarken bulurum

a = !a;

Bu auzun bir ifade gibi olduğunda sinir bozucu olur .

this.dataSource.trackedObject.currentValue.booleanFlag =
    !this.dataSource.trackedObject.currentValue.booleanFlag;

(evet, Demeter Yasası, biliyorum).

Merak ediyordum, tekdüze bir boole geçiş operatörüne sahip , örneğin a = !aifadeyi tekrar etmeden kısaltmamı sağlayacak herhangi bir dil var mı?a

!=a;  
// or
a!!;

Diyelim ki dilimizin uygun bir boole türü var ( boolC ++ gibi ) ve abu türden (yani C stili yok int a = TRUE).

Belgelendirilmiş bir kaynak bulabilirseniz, örneğin C ++ tasarımcılarının boolyerleşik bir tür haline geldiğinde böyle bir operatör eklemeyi düşünüp düşünmediğini ve neden öyleyse buna karşı karar verdiklerini öğrenmek isterim .


(Not: Bazı insanlar atama kullanmaması gerektiğini görüşünde olduğunu biliyoruz =ve o ++ve +=kullanışlı operatörler ama dizayn hataları değildir; let sadece onlar bool değerlerden oluşan kapsamaz neden onlarla ve odak mutluyum varsayalım ediyor).


31
Bir işlev hakkında void Flip(bool& Flag) { Flag=!Flag; }Uzun ifadenizi kısaltır.
harper

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

13
kodu basitleştirmek için referanslara uzun ifadeler atanabilir
Quentin 2

6
@KamilCuk Bu işe yarayabilir, ancak türleri karıştırırsınız. Bir boole tamsayı atarsınız.
harper

7
@ user463035818 *= -1olsa da, bir nedenle daha sezgisel buluyorum ^= true.
CompuChip

Yanıtlar:


15

Bu soru gerçekten de tamamen teorik açıdan ilginç. Tek , mutasyona uğrayan bir boole geçiş operatörünün yararlı olup olmayacağını veya birçok dilin neden bir dil sağlamayı seçmediğini bir kenara bırakarak , gerçekten var olup olmadığını görmek için bir arayışa girdim.

TL; DR görünüşe göre hayır, ama Swift bir tane uygulamanızı sağlar. Sadece nasıl yapıldığını görmek istiyorsanız, bu cevabın en altına gidebilirsiniz.


Çeşitli dillerin özelliklerine (hızlı) bir arama yaptıktan sonra, hiçbir dilin bu operatörü katı bir mutasyona uğrayan yerinde işlem olarak uygulamadığını söylemek güvenli olur (bulursanız beni düzeltin). Bir sonraki şey, bir tane oluşturmanıza izin veren diller olup olmadığını görmek olacaktır. Bunun için iki şey gerekir:

  1. işleve sahip (tekli) operatörleri uygulayabilme
  2. adı geçen işlevlerin referansla bağımsız değişkenlere sahip olmasına izin vermek (böylece bağımsız değişkenlerini doğrudan değiştirebilmeleri için)

Birçok dil, bu gereksinimlerin herhangi birini veya her ikisini de desteklemediği için derhal reddedilecektir. Birincisi için Java , operatörün aşırı yüklenmesine (veya özel işleçlere) izin vermez ve ek olarak, tüm ilkel türler değere göre geçirilir. Go'nun operatör aşırı yüklemesi (bilgisayar korsanları hariç ) için desteği yoktur. Pas yalnızca özel tipler için operatörün aşırı yüklenmesine izin verir. Bunu , çok yaratıcı bir şekilde adlandırılmış işlevleri kullanmanıza ve parantezleri atlamanıza izin veren Scala'da neredeyse başarabilirsiniz , ancak ne yazık ki doğrudan referans yok. Fortran o özel operatörler için izin verdiğini çok yakın alır, ama özellikle sahip olmaktan onları yasaklar inout parametreleri (normal fonksiyonlarda ve altyordamlarda izin verilir).


Ancak, gerekli tüm kutuları işaretleyen en az bir dil var: Swift . Bazı insanlar yaklaşan bağlı olsa da .toggle () üye fonksiyonu, aynı zamanda gerçekten destekleyen kendi operatörü, yazabilir inout argümanlar. Bakın ve görün:

prefix operator ^

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

var foo = true
print(foo)
// true

^foo

print(foo)
// false


3
Evet, ancak soru üye işlevi yerine özel olarak bir operatör istedi .
Lauri Piispanen

4
"Ne Rust?" => Evet öyle
Boiethios

3
@Boiethios teşekkürler! Rust için düzenlendi - ancak özel türler için yalnızca aşırı yüklenmiş işleçlere izin verir, bu nedenle zar yoktur.
Lauri Piispanen

3
@LauriPiispanen Kural bundan daha genel ve yetim kuralı nedeniyle , FYI
Boiethios

143

Boole bitini değiştirme

... bu a = !a ifadeyi tekrar etmedena kısaltmamı sağlayacaktı ...

Bu yaklaşım gerçekten saf bir "mutasyona uğrayan flip" operatörü değildir, ancak yukarıdaki kriterlerinizi karşılar; ifadenin sağ tarafı değişkenin kendisini içermez.

Bir boolean XOR atama (örn sahip herhangi bir dil ^=) bir değişken, diyelim mevcut değerini saygısız sağlayacak aXOR ataması vasıtasıyla, true:

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

Aşağıdaki yorumlarda @cmaster tarafından belirtildiği gibi, yukarıdaki varsayım atürdür boolve örneğin bir tamsayı veya bir işaretçi değildir. Eğer agerçekte (non örn şey başka bir şeydir booldeğil biraz gösterimi ile bir "truthy" veya "falsy" değerine değerlendiren, 0b1ya 0b0sırasıyla), yukarıda tutmaz.

Somut bir örnek olarak, Java bunun iyi tanımlandığı ve sessiz dönüşümlere tabi olmadığı bir dildir. Aşağıdan @ Boann'ın yorumundan alıntı:

Java'da, ^ve ^=açıkça boolelerde için ve tamsayılar için davranışını tanımladık ( 15.22.2. Boolean Mantıksal Operatörleri &, ^ve| ), operatörün her iki taraf ya mantıksal ifadenin olması gereken ya da her iki taraf tamsayı olmalıdır. Bu türler arasında sessiz bir dönüşüm yoktur. Bu nedenle a, bir tamsayı olarak bildirilirse sessizce arızalanmayacak , daha ziyade bir derleme hatası verecektir. Yani a ^= true;güvenli ve Java iyi tanımlanmış olduğunu.


Swift: toggle()

Swift 4.2 itibariyle, aşağıdaki evrim teklifi kabul edilmiş ve uygulanmıştır:

Bu , Swift'teki türe yerel bir toggle()işlev ekler Bool.

toggle()

Boolean değişkeninin değerini değiştirir.

deklarasyon

mutating func toggle()

Tartışma

Bir Boolean değeri geçiş yapmak için bu yöntemi kullanın trueiçin falseya gelen falseetmek true.

var bools = [true, false]

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

Bu, kendi başına bir operatör değildir, ancak boole geçişi için yerel bir dil yaklaşımına izin verir.


3
Yorumlar uzun tartışmalar için değildir; bu görüşme sohbete taşındı .
Samuel Liew

44

C ++ 'da, Kardinal Günah'ın operatörlerin anlamını yeniden tanımlamasını taahhüt etmek mümkündür. Bunu göz önünde bulundurarak ve biraz ADL ile, kullanıcı tabanımızda kargaşayı serbest bırakmak için tek yapmamız gereken şey şudur:

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

beklenen çıktı:

6
0
1
0
1

9
“Operatörlerin anlamını yeniden tanımlama Kardinal Günahı” - Abartıldığınızı anlıyorum, ancak genel olarak mevcut operatörlere yeni anlamlar atamak gerçekten kardinal bir günah değil .
Konrad Rudolph

5
@KonradRudolph Bununla kastettiğim, elbette bir operatörün her zamanki aritmetik veya mantıksal anlamıyla mantıklı bir anlam ifade etmesi gerektiğidir. 2 dizge için 'operatör +' mantıklıdır, çünkü birleştirme (zihnimizin gözünde) toplama veya birikmeye benzerdir. operator<<STL'deki orijinal kötüye kullanımı nedeniyle 'akıtıldı' anlamına geliyor. Doğal olarak 'dönüşüm işlevini RHS'ye uygulamak' anlamına gelmez, bu da aslında onu burada yeniden amaçladığım şeydir.
Richard Hodges

6
Bunun iyi bir tartışma olduğunu sanmıyorum, üzgünüm. İlk olarak, dize birleştirmenin toplamadan tamamen farklı bir anlambilimi vardır (değişmeli bile değildir!). İkincisi, mantığınız tarafından <<“dışarı akış” operatörü değil, biraz kaydırma operatörüdür. Yeniden tanımlamanızdaki sorun, operatörün mevcut anlamları ile tutarsız olması değil, basit bir işlev çağrısına değer katmamasıdır.
Konrad Rudolph

8
<<kötü bir aşırı yük gibi görünüyor. Yaparım !invert= x;;)
Yakk - Adam Nevraumont

12
@ Yakk-AdamNevraumont LOL, şimdi beni başlattın: godbolt.org/z/KIkesp
Richard Hodges

37

Montaj dilini içerdiğimiz sürece ...

ORTAYA

INVERT bitsel bir tamamlayıcı için.

0= mantıklı (doğru / yanlış) bir tamamlayıcı için.


4
Tartışmalı, her yığın odaklı dilde bir unary notbir "geçiş" operatörüdür :-)
Bergi

2
Elbette, OP geleneksel bir atama ifadesine sahip C benzeri diller hakkında açıkça düşündüğü için bu biraz yanal bir cevap. Bence, programlama dünyasının sadece okuyucunun düşünmemiş olabileceği bir kısmını göstermek için, bunun gibi yan cevapların bir değeri var. ("Cennette ve yeryüzünde Horatio'da felsefemizden çok daha fazla programlama dili vardır.")
Wayne Conrad

31

Bir C99 eksiltim boolarttırılması veya eksiltim irade olarak, istenilen etkiye sahip olacaktır bit(bazı küçük-mikrodenetleyici lehçelerde desteklenen türlerini hangi ı gözlenen tedavi bitlerini ettik neyi gibi tek bit çift numaralar 0 ve tüm kesilmiş olsun, böylece geniş bitfields 1'e tek sayılar). Bu tür bir kullanımı özellikle tavsiye etmem, çünkü booltip semantiğinin büyük bir hayranı değilim [IMHO, tip bool0 veya 1 dışında herhangi bir değerin saklandığı bir a'nın sanki okunduğunda davranabileceğini belirtmiş olmalı belirtilmemiş (zorunlu olarak tutarlı olmayan) bir tamsayı değeri taşır; bir program 0 veya 1 olarak bilinmeyen bir tamsayı değerini saklamaya çalışıyorsa, önce onu kullanmalıdır !!].


2
C ++, bool C ++ 17'ye kadar aynı şeyi yaptı .
Davis Herring

31

2
Bunun x86 meclisi olduğunu varsayarak, op'un aklında olan şey olduğunu sanmıyorum. ör. en.wikibooks.org/wiki/X86_Assembly/Logic ; here edx would be 0xFFFFFFFE because a bitwise NOT 0x00000001 = 0xFFFFFFFE
BurnsBA

8
Bunun yerine tüm bitleri kullanabilirsiniz: NOT 0x00000000 = 0xFFFFFFFF
Adrian

5
Evet, boolean ve bitwise operatörler arasındaki farkı çizmeye çalışıyordum. Op'un soruda dediği gibi, dilin iyi tanımlanmış bir boole tipine sahip olduğunu varsayın: "(C stili int a = TRUE yok)."
BurnsBA

5
@BurnsBA: gerçekten de, montaj dillerinin tek bir iyi tanımlanmış doğruluk / falsy değeri kümesi yoktur. Code-golf üzerinde, çeşitli dillerde boole sorunları için hangi değerleri döndürmenize izin verildiği hakkında çok şey tartışıldı. ASM'nin bir if(x)yapısı olmadığından, SIMD karşılaştırmaları gibi 0 / -1 değerinin yanlış / doğru olmasına izin vermek tamamen mantıklıdır ( felixcloutier.com/x86/PCMPEQB:PCMPEQW:PCMPEQD.html ). Ancak, başka bir değerde kullanırsanız, bunun kırılması konusunda haklısınız.
Peter Cordes

4
PCMPEQW'nin 0 / -1 ile sonuçlandığı, ancak SETcc'nin 0 / + 1 ile sonuçlandığı göz önüne alındığında, tek bir Boolean temsiline sahip olmak zordur.
Eugene Styer

28

Ben sadece bu dayalı bir dil seçmeyeceğim varsayıyorum :-) Her durumda, böyle bir şey ile C ++ yapabilirsiniz:

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

Örneğin aşağıdaki tam programa bakın:

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

Bu çıktı, beklendiği gibi:

false
true
false

2
Siz return b = !bde ister misiniz ? Birisi okuma foo = makenot(x) || yya da sadece basit bir foo = makenot(bar)farz olabileceğini makenotsaf fonksiyon olduğunu ve hiçbir yan etkisi yoktu varsayalım. Daha büyük bir ifadenin içine bir yan etki gömmek muhtemelen kötü bir stildir ve geçersiz bir dönüş türünün tek yararı bunu mümkün kılmaktır.
Peter Cordes

2
@MatteoItalia, template<typename T> T& invert(T& t) { t = !t; return t; } şablondan hangisinin nesne boololmayan bir boolnesnede kullanıldığında dönüştürülüp dönüştürüleceğini önerir . IDK bu iyi ya da kötü ise. Muhtemelen iyi.
Peter Cordes


21

Visual Basic.Net bunu bir uzantı yöntemi ile destekler.

Uzantı yöntemini şu şekilde tanımlayın:

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

Ve sonra şöyle deyin:

Dim someVariable As Boolean
someVariable = True
someVariable.Flip

Yani, orijinal örneğiniz şöyle görünecektir:

me.DataSource.TrackedObject.CurrentValue.BooleanFlag.Flip

2
Bu, yazarın aradığı stenografi işlevi görürken, elbette, uygulamak için iki operatör kullanmalısınız Flip(): =ve Not. Çağırma durumunda bir özellik SomeInstance.SomeBoolean.Flipolsa bile çalıştığını öğrenmek ilginçtir SomeBoolean, oysa eşdeğer C # kodu derlenmeyecektir.
BACON

14

Rust'ta, özelliği uygulayan türleri genişletmek için kendi özelliğinizi oluşturabilirsiniz 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);
}

Ayrıca ^= trueönerildiği gibi kullanabilirsiniz ve Rust durumunda, falseC veya C ++ gibi "gizlenmiş" bir tamsayı olmadığı için bunu yapmak mümkün değildir:

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

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


3

C # dilinde :

boolean.variable.down.here ^= true;

Boolean ^ işleci XOR'dir ve true ile XORing, ters çevirme ile aynıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.