任何语言都有一元布尔切换运算符吗?

所以这更多的是一个理论问题。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!!;

让我们假设我们的语言有一个合适的布尔类型(如 C + + 中的 bool) ,并且 a也是这种类型(所以没有 C 风格的 int a = TRUE)。

如果你能找到一个文档化的源代码,我也很想知道 C + + 的设计者是否考虑过在 bool成为内置类型时添加一个类似的操作符,如果是的话,为什么他们决定不这么做。


(注意: 我知道有些人认为作业不应该使用 =+++=不是有用的操作符,而是设计缺陷; 让我们假设我对它们很满意,并关注为什么它们不能扩展到 bools)。

11292 次浏览

我假设您不会仅仅基于以下内容来选择一种语言: ——无论如何,您可以在 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

切换布尔位

... 这样我就可以缩写 a = !a < strong > 而不用重复 a的表达式 ...

这种方法实际上不是一个纯粹的“变异翻转”操作符,但是满足了上面的条件; 表达式的右边不涉及变量本身。

任何带有布尔异或赋值的语言(例如 ^=)都允许通过将异或赋值给 true来反转变量(例如 a)的当前值:

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

正如@cmaster 在下面的注释中指出的,上面假设 abool类型,而不是例如一个整数或指针。如果 a实际上是别的什么东西(例如,某个非 bool值为“真实”或“虚假”值的东西,其位表示分别不是 0b10b0) ,上面的内容就不成立。

举个具体的例子,Java 是一种定义良好的语言,不受任何静默转换的约束。引用@Boann 在下面的评论:

在 Java 中,^^=已经为布尔值显式定义了行为 以及整数 (< a href = “ https://docs.oracle.com/javase/specs/jls/se10/html/jls-15.html # jls-15.22.2”rel = “ noReferrer”> 15.22.2。 布尔逻辑运算符 &^| ) ,其中任一边 运算符的边长必须是布尔值,或者两边都必须是整数。 这些类型之间没有无声的转换,所以不会 如果将 a声明为整数,则会发生无声故障,但相反, 给出一个编译错误。因此 a ^= true;是安全的,在 爪哇咖啡。


toggle()

截至 Swift 4.2,以下演进提案已被接受并实现:

这将为 Swift 中的 Bool类型添加一个本机 toggle()函数。

toggle()

切换布尔变量的值。

声明

mutating func toggle()

讨论

使用此方法将布尔值从 true切换到 false或 从 falsetrue

var bools = [true, false]


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

这本身不是一个操作符,但是允许使用语言本机方法进行布尔切换。

在 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

减少一个 C99 bool将会达到预期的效果,增加或减少一些微控制器方言中支持的 bit类型也会达到预期效果(根据我的观察,这种方言将位视为单位宽的位字段,所以所有偶数都被截断为0,所有奇数都被截断为1)。我不会特别推荐这种用法,部分原因是我不是 bool类型语义的忠实粉丝[ IMHO,该类型应该指定一个 bool,其中除了0或1以外的任何值都存储在其中,当读取时,它可能表现得好像存储了一个未指定(不一定一致)的整数值; 如果一个程序试图存储一个不知道为0或1的整数值,它应该首先在它上面使用 !!]。

VisualBasic.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

只要我们包括汇编语言..。

福斯

位补数的 INVERT

逻辑(真/假)补语的 0=

PostScript 是一种类似 Forth 的 连接的面向堆栈的语言,它有一个一元切换 没有没有运算符切换堆栈顶部的值。比如说,

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

参见 PostScript 语言参考手册(pdf),第458页。

从纯理论的角度来看,这个问题的确很有趣。撇开 一元变异布尔切换运算符是否有用,或者为什么许多语言选择不提供 一元变异布尔切换运算符不谈,我冒险去探索它是否真的存在。

DR 显然没有,但是 Swift 允许您实现一个。如果你只想看看它是如何完成的,你可以滚动到这个答案的底部。


在(快速)搜索了各种语言的特性之后,我可以放心地说,没有任何语言将这个操作符实现为一个严格的就地变异操作(如果您找到了一个,请纠正我)。所以接下来要做的就是看看是否有语言可以让你构建一个。这需要两件事:

  1. 能够用函数实现(一元)运算符
  2. 允许所述函数具有通过引用传递的参数(以便它们可以直接改变参数)

许多语言将立即被排除在外,因为它们不支持这两个需求中的任何一个或两个。而且,所有的基元类型都是通过值传递的,因此不允许使用运算符重载(或自定义运算符)。去吧不支持任何运算符重载(除了 黑客)。生锈了只允许自定义类型的运算符重载。您可以在 斯卡拉中使用 差不多来实现这一点,斯卡拉允许您使用非常有创意的命名函数,并且省略括号,但遗憾的是没有引用传递。Fortran非常接近,因为它允许自定义操作符,但明确禁止它们拥有 收线参数(这在普通函数和子例程中是允许的)。


然而,至少有一种语言符合所有必要的条件: 斯威夫特。虽然有些人已经链接到即将出现的 . toggle ()成员函数,但是您也可以编写自己的操作符,它确实支持 收线参数。你瞧:

prefix operator ^


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


var foo = true
print(foo)
// true


^foo


print(foo)
// false

在 C # :

boolean.variable.down.here ^= true;

布尔 ^ 运算符是 XOR,使用 true 进行 XORing 与进行反转相同。

在 Rust 中,您可以创建自己的 trait 来扩展实现 Not trait 的类型:

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 支持这样的功能,如果变量具有 布尔型的(它是 True 或 False)和 exclusive or (^=)操作符:

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