我刚刚了解了 Crystal 中的存在&-=运算符。它有什么作用?
这是Mutex#try_lock的示例:
private def try_lock
i = 1000
while @state.swap(1) != 0
while @state.get != 0
Intrinsics.pause
i &-= 1
return false if i == 0
end
end
true
end
Run Code Online (Sandbox Code Playgroud)
在尝试时,我看不出与熟悉的-=操作员有任何区别。例如,这两个片段产生相同的输出:
i = 1000
while i != 0
puts i
i -= 1
end
Run Code Online (Sandbox Code Playgroud)
i = 1000
while i != 0
puts i
i &-= 1
end
Run Code Online (Sandbox Code Playgroud)
难题的第一部分是要意识到这a &-= b只是a = a &- b. 或者更普遍的a op= b是a = a op b. 语言参考在Operators 的“组合赋值”下对此进行了详细说明。
现在我们需要问什么&-是什么以及它与什么不同-?不幸的是,API 文档在这方面写得很安静。语言参考也不是很详细,但在与上面相同的 Operators 页面上,我们可以找到:
-减法
&-包装减法
那么什么是包装减法呢?好吧,Crystal 有固定大小的数字类型。因此,在这种情况下,它们可能会溢出或下溢。这意味着什么?让我们举个例子:
# We have something to sell! Let's keep track of how many!
# It doesn't really make sense to have negative something left,
# so an unsigned integer ought to this.
items_left = 2u32
# Just made the first sell! Let's remember
items_left -= 1
# People seem to actually like this
items_left -= 1
# I could do this all day!
items_left -= 1 # => Unhandled exception: Arithmetic overflow (OverflowError)
# Oh no what happend?
Run Code Online (Sandbox Code Playgroud)
所以程序试图去下面0,UInt32类型不能代表。它下溢了。如果水晶不会做此检查时,CPU会高兴地缠绕在整数类型,我们会得到4294967295的items_left(UInt32::MAX)。
但有时,在低级代码中,这种行为正是我们想要的。例如,如果我们正在计算一些统计数据,比如发送的数据包,如果该计数器溢出或下溢,我们不希望程序在这种情况下失败,回绕是可以的。或者我们有一些对性能敏感的代码,并且确信它的行为正确并且永远不会溢出,所以我们不想在检查操作是否刚刚溢出时支付额外的 CPU 周期。
对于这些情况,有带&前缀的数学运算符。它们只是执行操作而不进行任何溢出检查。如果我们在上面的例子中使用&-而不是-,我们现在就可以4294967295了items_left。
一般来说,您知道是否需要包装运算符或从包装运算符中受益。如有疑问,请假装它们不存在。