我在 pry REPL 中闲逛,发现了一些非常有趣的行为:波形符方法。
Ruby 语法似乎有一个内置的文字一元运算符,
~
,只是闲置。
这意味着
~Object.new
将消息 ~
发送到 Object
的实例:
class Object
def ~
puts 'what are you doing, ruby?'
end
end
~Object.new #=> what are you doing, ruby?
这看起来真的很酷,但也很神秘。 Matz 本质上是想为我们提供我们自己的可定制一元运算符吗?
我在 rubydocs 中能找到的唯一参考是在 operatorprincence 注释中,它被列为第一最高优先级运算符,与
!
和一元 +
并列,这对于一元运算符来说是有意义的。 (有关接下来的两个优先级的有趣勘误表,**
然后一元-
,请查看这个问题。)除此之外,没有提及此实用程序。
通过搜索,我可以在
~=
、!~
和 ~>
问题中找到对该运算符的两个值得注意的引用,即 this 和 this。他们都注意到了它的有用性、奇怪性和默默无闻,但没有深入探讨它的历史。
在我准备将
~
作为一种为对象提供自定义一元运算符行为的酷方法之后,我发现了它在 ruby 中实际使用的地方——fixnum(整数)。
~2
返回 -3
。 ~-1
返回 1
。所以它会因为某种原因否定一个整数并减去一个......?
任何人都可以告诉我波浪号运算符在整个红宝石中独特且意想不到的行为的目的吗?
使用撬探方法:
show-method 1.~
From: numeric.c (C Method):
Owner: Fixnum
Visibility: public
Number of lines: 5
static VALUE
fix_rev(VALUE num)
{
return ~num | FIXNUM_FLAG;
}
虽然这对我来说难以理解,但它促使我寻找 C 一元
~
运算符。存在一个:它是按位 NOT 运算符,它翻转二进制整数的位 (~1010
=> 0101
)。由于某种原因,这会比 Ruby 中的十进制整数的负数小 1。
更重要的是,由于 ruby 是一种面向对象的语言,因此对
~0b1010
的行为进行编码的正确方法是定义一个对二进制整数对象执行按位求反的方法(我们称之为 ~
)。为了实现这一点,Ruby 解析器(这都是推测)必须将任何对象的 ~obj
解释为 obj.~
,这样你就可以得到所有对象的一元运算符。
这只是预感,谁有更权威或更清楚的答案,请赐教!
正如 @7stud 指出的那样,
Regexp
类也使用了它,本质上将正则表达式与$_
(gets
在当前范围内接收到的最后一个字符串)进行匹配。
正如 @Daiku 指出的那样,
Fixnum
s 的按位否定也有记录。
我认为我的解析器解释解决了一个更大的问题,即为什么 ruby 允许
~
作为调用 Object#~
的全局一元运算符。
对于
fixnum
,它是 1 的补码,以二进制形式将所有 1 和 0 翻转为相反的值。 这是文档:http://www.ruby-doc.org/core-2.0/Fixnum.html#method-i-7E。 要理解为什么它给出示例中的值,您需要了解负数如何以二进制表示。 为什么 ruby 提供这个,我不知道。 二进制补码通常是现代计算机中使用的补码。 它的优点是基本数学运算的相同规则适用于正数和负数。
~
是 Ruby 中的二进制补码运算符。补码只是翻转数字的位,结果是该数字现在在算术上是负数。
例如,32 位(Fixnum 的大小)二进制中的 2 为 0000 0000 0000 0010,因此 ~2 等于 1111 1111 1111 1101 的补码。
但是,正如您所注意到的以及这篇文章 更详细地讨论的那样,Ruby 版本的补码的实现方式似乎有所不同,因为它不仅使整数变为负数,而且还从中减去 1。我不知道为什么会这样,但似乎确实是这样。
镐 1.8 中的多个地方都提到了这一点,例如字符串类。 然而,在 ruby 1.8.7 中,它不适用于宣传的 String 类。 它确实适用于 Regexp 类:
print "Enter something: "
input = gets
pattern = 'hello'
puts ~ /#{pattern}/
--output:--
Enter something: 01hello
2
对于 String 类来说,它的工作原理应该类似。