Apakah ada bahasa yang memiliki operator toggle boolean unary?


141

Jadi ini lebih merupakan pertanyaan teoretis. C ++ dan bahasa (dalam) langsung berdasarkan itu (Java, C #, PHP) memiliki operator pintas untuk menetapkan hasil dari sebagian besar operator biner ke operan pertama, seperti

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

tetapi ketika saya ingin beralih ekspresi boolean saya selalu menemukan diri saya menulis sesuatu seperti

a = !a;

yang menjadi menjengkelkan ketika aekspresi panjang seperti.

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

(Ya, Hukum Demeter, saya tahu).

Jadi saya bertanya-tanya, apakah ada bahasa dengan operator toggle boolean unary yang akan memungkinkan saya untuk menyingkat a = !atanpa mengulangi ungkapan untuk a, misalnya

!=a;  
// or
a!!;

Mari kita asumsikan bahwa bahasa kita memiliki tipe boolean yang tepat (seperti booldalam C ++) dan itu aadalah tipe itu (jadi tidak ada gaya-C int a = TRUE).

Jika Anda dapat menemukan sumber yang terdokumentasi, saya juga akan tertarik untuk mengetahui apakah mis. Perancang C ++ telah mempertimbangkan untuk menambahkan operator seperti itu ketika boolmenjadi tipe bawaan dan jika demikian, mengapa mereka memutuskan untuk tidak melakukannya.


(Catatan: Saya tahu bahwa beberapa orang berpendapat bahwa penugasan tidak boleh digunakan =dan bahwa ++dan +=bukan operator yang berguna tetapi kelemahan desain; mari kita asumsikan saya senang dengan mereka dan fokus pada mengapa mereka tidak meluas ke bools).


31
Bagaimana dengan fungsi void Flip(bool& Flag) { Flag=!Flag; }, mempersingkat ekspresi panjang Anda.
pemain

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

13
ekspresi panjang dapat diberikan ke referensi untuk menyederhanakan kode
Quentin 2

6
@ KamilCuk Ini mungkin berhasil, tetapi Anda mencampur jenis. Anda menetapkan bilangan bulat ke bool.
pemain

7
@ user463035818 ada *= -1meskipun, yang karena alasan tertentu saya menemukan lebih intuitif daripada ^= true.
CompuChip

Jawaban:


15

Pertanyaan ini memang menarik dari sudut pandang teori belaka. Mengesampingkan apakah operator beralih boolean unary, bermutasi akan berguna, atau mengapa banyak bahasa memilih untuk tidak menyediakan satu, saya memberanikan diri pada pencarian untuk melihat apakah itu benar-benar ada atau tidak.

TL; DR rupanya tidak, tetapi Swift memungkinkan Anda menerapkannya. Jika Anda hanya ingin melihat bagaimana ini dilakukan, Anda dapat menggulir ke bagian bawah jawaban ini.


Setelah pencarian (cepat) ke fitur dari berbagai bahasa, saya merasa aman untuk mengatakan bahwa tidak ada bahasa yang mengimplementasikan operator ini sebagai operasi in-place yang benar-benar bermutasi (perbaiki saya jika Anda menemukannya). Jadi, hal selanjutnya adalah melihat apakah ada bahasa yang memungkinkan Anda membangunnya. Apa yang dibutuhkan adalah dua hal:

  1. mampu menerapkan operator (tidak disadari) dengan fungsi
  2. memungkinkan fungsi-fungsi tersebut memiliki argumen pass-by-reference (sehingga mereka dapat mengubah argumen mereka secara langsung)

Banyak bahasa akan segera dikesampingkan karena tidak mendukung salah satu atau kedua persyaratan ini. Java for one tidak memungkinkan overloading operator (atau operator kustom) dan di samping itu, semua tipe primitif diberikan nilai. Go tidak memiliki dukungan untuk overloading operator (kecuali dengan peretasan ) apa pun. Karat hanya memungkinkan overloading operator untuk jenis khusus. Anda hampir dapat mencapai ini di Scala , yang memungkinkan Anda menggunakan fungsi yang sangat kreatif dan juga menghilangkan tanda kurung, tetapi sayangnya tidak ada pass-by-reference. Fortran mendapat sangat dekat dalam yang memungkinkan untuk operator kustom, namun secara khusus melarang mereka dari memiliki inout parameter (yang diizinkan dalam fungsi dan subrutin normal).


Namun ada setidaknya satu bahasa yang menandai semua kotak yang diperlukan: Swift . Sementara beberapa orang telah menautkan ke fungsi anggota .toggle () yang akan datang , Anda juga dapat menulis operator Anda sendiri, yang memang mendukung argumen inout . Lihatlah:

prefix operator ^

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

var foo = true
print(foo)
// true

^foo

print(foo)
// false


3
Ya, tetapi pertanyaannya secara spesifik menanyakan operator , bukan fungsi anggota.
Lauri Piispanen

4
"Begitupula dengan Rust" => Ya, benar
Boiethios

3
@Boiethios terima kasih! Diedit untuk Rust - hanya memungkinkan operator kelebihan beban untuk jenis khusus, jadi, tidak ada dadu.
Lauri Piispanen

3
@LauriPiispanen Aturannya lebih umum dari itu, dan karena aturan yatim , FYI
Boiethios

143

Beralih sedikit boolean

... yang akan memungkinkan saya untuk menyingkat a = !a tanpa mengulangi ungkapan untuka ...

Pendekatan ini sebenarnya bukan operator "mutating flip" yang murni, tetapi memenuhi kriteria Anda di atas; sisi kanan ekspresi tidak melibatkan variabel itu sendiri.

Bahasa apa pun dengan penugasan XOR boolean (mis. ^=) Akan memungkinkan membalik nilai saat ini dari suatu variabel, katakanlah a, dengan menggunakan penugasan XOR ke true:

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

Seperti yang ditunjukkan oleh @cmaster dalam komentar di bawah ini, asumsi di atas aadalah tipe bool, dan bukan eg integer atau pointer. Jika asebenarnya sesuatu yang lain (misalnya sesuatu yang tidak boolmengevaluasi nilai "kebenaran" atau "palsu", dengan sedikit representasi yang tidak 0b1atau 0b0, masing-masing), hal di atas tidak berlaku.

Sebagai contoh konkret, Java adalah bahasa di mana ini didefinisikan dengan baik dan tidak tunduk pada konversi diam. Mengutip komentar Boann dari bawah:

Di Jawa, ^dan ^=telah secara eksplisit didefinisikan perilaku untuk boolean dan untuk bilangan bulat ( 15.22.2. Logical Operator Boolean &, ^dan| ), di mana baik kedua sisi operator harus boolean, atau kedua belah pihak harus bilangan bulat. Tidak ada konversi diam di antara tipe-tipe itu. Jadi tidak akan ada kerusakan secara diam-diam jika adideklarasikan sebagai integer, melainkan memberikan kesalahan kompilasi. Jadi a ^= true;aman dan terdefinisi dengan baik di Jawa.


Cepat: toggle()

Pada Swift 4.2, proposal evolusi berikut telah diterima dan diterapkan:

Ini menambahkan toggle()fungsi asli ke Booltipe di Swift.

toggle()

Mengaktifkan / menonaktifkan nilai variabel Boolean.

Pernyataan

mutating func toggle()

Diskusi

Gunakan metode ini untuk beralih nilai Boolean dari trueke falseatau dari falseke true.

var bools = [true, false]

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

Ini bukan operator, per se, tetapi memungkinkan pendekatan bahasa asli untuk beralih boolean.


3
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Samuel Liew

44

Dalam C ++ dimungkinkan untuk melakukan Dosa Kardinal mendefinisikan kembali makna operator. Dengan mengingat hal ini, dan sedikit ADL, yang perlu kita lakukan untuk melepaskan kekacauan pada basis pengguna kami adalah ini:

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

output yang diharapkan:

6
0
1
0
1

9
"Dosa Kardinal mendefinisikan kembali makna operator" - Saya mengerti bahwa Anda melebih-lebihkan, tetapi sebenarnya bukan dosa kardinal untuk menetapkan kembali makna baru bagi operator yang ada secara umum.
Konrad Rudolph

5
@KonradRudolph Apa yang saya maksud dengan ini tentu saja bahwa operator harus masuk akal logis sehubungan dengan aritmatika atau makna logis yang biasa. 'operator + `untuk 2 string adalah logis, karena rangkaiannya mirip (dalam mata pikiran kita) dengan penambahan atau akumulasi. operator<<telah berarti 'mengalir keluar' karena penyalahgunaan aslinya di STL. Ini tidak akan secara alami berarti 'menerapkan fungsi transformasi pada RHS', yang pada dasarnya adalah apa yang saya maksudkan di sini.
Richard Hodges

6
Saya pikir itu bukan argumen yang bagus, maaf. Pertama, rangkaian string memiliki semantik yang sama sekali berbeda dari penambahan (bahkan tidak komutatif!). Kedua, menurut logika Anda <<operator bit shift, bukan operator "stream out". Masalah dengan redefinisi Anda bukan karena tidak konsisten dengan makna operator yang ada, tetapi lebih karena tidak menambah nilai dibandingkan pemanggilan fungsi yang sederhana.
Konrad Rudolph

8
<<Sepertinya kelebihan yang buruk. Saya akan lakukan !invert= x;;)
Yakk - Adam Nevraumont

12
@ Yakk-AdamNevraumont LOL, sekarang Anda membuat saya mulai: godbolt.org/z/KIkesp
Richard Hodges

37

Selama kami menyertakan bahasa rakitan ...

FORTH

INVERT untuk komplemen bitwise.

0= untuk pelengkap yang logis (benar / salah).


4
Dapat diperdebatkan, dalam setiap bahasa berorientasi stack, unary notadalah operator "toggle" :-)
Bergi

2
Tentu, ini adalah jawaban yang agak menyamping karena OP jelas-jelas memikirkan bahasa mirip C yang memiliki pernyataan penugasan tradisional. Saya pikir jawaban menyamping seperti ini memiliki nilai, jika hanya untuk menunjukkan bagian dari dunia pemrograman yang mungkin tidak terpikirkan oleh pembaca. ("Ada lebih banyak bahasa pemrograman di surga dan bumi, Horatio, daripada di filosofi kami.")
Wayne Conrad

31

Mengurangi C99 boolakan memiliki efek yang diinginkan, karena akan menambah atau mengurangi bitjenis yang didukung dalam beberapa dialek mikrokontroler kecil (yang dari apa yang saya amati memperlakukan bit sebagai bitfield bit lebar tunggal, sehingga semua angka genap dipotong ke 0 dan semua angka ganjil ke 1). Saya tidak akan merekomendasikan penggunaan semacam itu, sebagian karena saya bukan penggemar boolsemantik tipe [IMHO, tipe tersebut seharusnya menentukan bahwa boolnilai yang selain 0 atau 1 disimpan mungkin berperilaku ketika dibaca seolah-olah ia memiliki nilai integer yang tidak ditentukan (tidak harus konsisten); jika suatu program mencoba untuk menyimpan nilai integer yang tidak dikenal sebagai 0 atau 1, itu harus digunakan !!terlebih dahulu].


2
C ++ melakukan hal yang sama bool hingga C ++ 17 .
Davis Herring

31

2
Saya tidak berpikir ini adalah apa yang ada dalam pikirannya, dengan asumsi ini adalah x86 assembly. mis. en.wikibooks.org/wiki/X86_Assembly/Logic ; here edx would be 0xFFFFFFFE because a bitwise NOT 0x00000001 = 0xFFFFFFFE
BurnsBA

8
Anda dapat menggunakan semua bit: BUKAN 0x00000000 = 0xFFFFFFFF
Adrian

5
Ya, meskipun saya mencoba menggambar perbedaan antara operator boolean dan bitwise. Seperti kata op dalam pertanyaan, anggap bahasanya memiliki tipe boolean yang terdefinisi dengan baik: "(jadi tidak ada C-style int a = TRUE)."
BurnsBA

5
@BurnsBA: memang, bahasa assembly tidak memiliki satu set nilai kebenaran / kepalsuan yang didefinisikan dengan baik. Lebih dari pada kode-golf, banyak yang telah dibahas tentang nilai-nilai mana yang Anda boleh kembalikan untuk masalah boolean dalam berbagai bahasa. Karena asm tidak memiliki if(x)konstruk , sangat masuk akal untuk mengizinkan 0 / -1 sebagai false / true, seperti untuk perbandingan SIMD ( felixcloutier.com/x86/PCMPEQB:PCMPEQW:PCMPEQD.html ). Tapi Anda benar bahwa ini rusak jika Anda menggunakannya pada nilai lain.
Peter Cordes

4
Sulit untuk memiliki representasi Boolean tunggal mengingat PCMPEQW menghasilkan 0 / -1, tetapi SETcc menghasilkan 0 / + 1.
Eugene Styer

28

Saya berasumsi Anda tidak akan memilih bahasa hanya berdasarkan ini :-) Bagaimanapun, Anda dapat melakukan ini di C ++ dengan sesuatu seperti:

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

Lihat program lengkap berikut ini misalnya:

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

Output ini, seperti yang diharapkan:

false
true
false

2
Apakah Anda mau return b = !bjuga? Seseorang yang membaca foo = makenot(x) || yatau sekadar sederhana foo = makenot(bar)mungkin menganggap itu makenotadalah fungsi murni, dan menganggap tidak ada efek samping. Mengubur efek samping di dalam ekspresi yang lebih besar mungkin gaya yang buruk, dan satu-satunya manfaat dari tipe pengembalian non-void adalah memungkinkannya.
Peter Cordes

2
@MatteoItalia menyarankan template<typename T> T& invert(T& t) { t = !t; return t; } , yang sedang templated akan dikonversi ke / dari booljika digunakan pada non- boolobjek. IDK apakah itu baik atau buruk. Agaknya bagus.
Peter Cordes


21

Visual Basic.Net mendukung ini melalui metode ekstensi.

Tentukan metode ekstensi seperti ini:

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

Dan menyebutnya seperti ini:

Dim someVariable As Boolean
someVariable = True
someVariable.Flip

Jadi, contoh asli Anda akan terlihat seperti:

me.DataSource.TrackedObject.CurrentValue.BooleanFlag.Flip

2
Meskipun ini berfungsi sebagai singkatan yang dicari penulis, tentu saja, Anda harus menggunakan dua operator untuk menerapkan Flip(): =dan Not. Sangat menarik untuk mengetahui bahwa dalam kasus memanggil SomeInstance.SomeBoolean.Flipitu berfungsi bahkan jika SomeBooleanmerupakan properti, sedangkan kode C # yang setara tidak akan dikompilasi.
BACON

14

Di Rust, Anda bisa membuat sifat Anda sendiri untuk memperluas jenis yang menerapkan Notsifat itu:

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

Anda juga dapat menggunakan ^= trueseperti yang disarankan, dan dalam kasus Rust, tidak ada masalah untuk melakukan hal ini karena falsebukan bilangan bulat "disamarkan" seperti di C atau C ++:

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

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


3

Dalam C # :

boolean.variable.down.here ^= true;

Operator boolean ^ adalah XOR, dan XORing dengan true sama dengan pembalik.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.