这更多是一个理论问题。 C ++和直接基于它的语言(Java,C#,PHP)具有shortcut operators,用于将大多数二进制运算符的结果分配给第一个操作数,例如
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;
(是的,我知道得墨meter耳定律)。
所以我想知道,是否有任何一种具有一元布尔切换运算符的语言,该语言可以让我缩写a = !a
而不用重复a
的表达式,例如< / p>
!=a;
// or
a!!;
让我们假设我们的语言具有正确的布尔类型(如C ++中的bool
),并且a
属于该类型(因此没有C风格的int a = TRUE
)。
如果您可以找到记录在案的资料来源,我也想了解一下是否C ++设计人员已考虑在bool
成为内置类型时添加一个像这样的运算符,如果是这样,为什么他们决定反对它。
(注意:我知道有些人认为作业不应使用
=
和++
和+=
不是有用的运算符,而是设计缺陷;让我们假设我对它们感到满意,并专注于为什么它们不会扩展到布尔值。
答案 0 :(得分:143)
......,这使我可以缩写
的表达式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
实际上是其他内容(例如非bool
的值表示为“真”或“虚假”值,且其位表示形式不是0b1
或{{1} }),则上述内容不成立。
举一个具体的例子,Java是一种语言,定义明确且不受任何静默转换的影响。从下面引用@Boann的评论:
在Java中,
0b0
和^
明确定义了布尔值的行为 和整数 (15.22.2. Boolean Logical Operators&
,^
, and|
), 运算符的必填项必须为布尔值,或者双方都必须为整数。 这些类型之间没有静默转换。所以这不会 如果将^=
声明为整数,则会自动发生故障,但是, 给出编译错误。因此a
是安全的,并且在 Java。
a ^= true;
从Swift 4.2开始,以下改进建议已被接受并实施:
这会将原生toggle()
函数添加到Swift中的Bool
类型。
toggle()
切换布尔变量的值。
声明
toggle()
讨论
使用此方法将布尔值从
mutating func toggle()
切换到true
或 从false
到false
。true
这本身不是运算符,但确实允许使用语言本机方法进行布尔切换。
答案 1 :(得分:42)
在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
答案 2 :(得分:37)
只要我们包含汇编语言...
INVERT
用于按位补码。
0=
表示逻辑(对/错)补码。
答案 3 :(得分:31)
汇编语言
NOT eax
请参见https://www.tutorialspoint.com/assembly_programming/assembly_logical_instructions.htm
答案 4 :(得分:30)
递减C99 bool
会达到预期的效果,递增或递减某些微型控制器方言所支持的bit
类型(根据我的观察,将位视为单位)宽位字段,因此所有偶数均被截断为0,所有奇数均截断为1)。我不会特别推荐这种用法,部分原因是我不是bool
类型语义[IMHO的忠实拥护者,该类型应该已指定bool
的值不是0或1读取时可能会表现为似乎具有未指定(不一定一致)的整数值;如果程序试图存储一个未知的0或1的整数,则应首先在其上使用!!
。
答案 5 :(得分: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
答案 6 :(得分:22)
PostScript是一种concatenative,stack-oriented语言,如Forth,具有一元切换, not 。 not 运算符可将值切换到堆栈顶部。例如,
true % push true onto the stack
not % invert the top of stack
% the top of stack is now false
请参见第PostScript Language Reference Manual (pdf)页。 458。
答案 7 :(得分: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
答案 8 :(得分:14)
从纯粹的理论角度来看,这个问题确实很有趣。抛开一元,可变的布尔布尔切换运算符是否有用,或者为什么许多语言选择不提供它,我冒险寻求一个发现是否确实存在。>
TL; DR 显然没有,但是Swift允许您实现一个。如果您只想看看它是如何完成的,则可以滚动到此答案的底部。
(快速)搜索各种语言的功能后,我可以肯定地说没有一种语言将此操作符实现为严格的变异就地操作(如果找到一种,请纠正我)。因此,接下来的事情就是看看是否有可以让您构建语言的语言。这将需要两件事:
许多语言会因为不支持这两种要求之一或全部而被立即排除。 Java 不允许操作符重载(或自定义操作符),此外,所有原始类型均按值传递。 执行完全不支持操作符重载(hacks除外)。 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
答案 9 :(得分:13)
在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 ++那样的“伪装”整数: / p>
fn main() {
let mut b = true;
b ^= true;
assert_eq!(b, false);
let mut b = false;
b ^= true;
assert_eq!(b, true);
}
答案 10 :(得分:5)
如果变量的bool type运算符具有exclusive or (^=)
(为True或False),则Python支持此类功能:
a = False
a ^= True
print(a) # --> True
a ^= True
print(a) # --> False
答案 11 :(得分:2)