【问题标题】:Ruby unary tilde (`~`) methodRuby 一元波浪号 (`~`) 方法
【发布时间】:2013-07-27 10:32:51
【问题描述】:

我在 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 是否本质上是想给我们自己的可定制的一元运算符?

我可以在 ruby​​docs 中找到的唯一参考是在 operator precedence 注释中,它与 !unary + 一起被列为第一高优先级运算符,这对于一元运算符是有意义的。 (有关接下来两个优先级的有趣勘误表,** 然后unary -,请查看this question。)除此之外,没有提及此实用程序。

~=,!~, and~>` 的问题中,我可以通过搜索找到对这个运算符的两个值得注意的引用,它们是thisthis。他们都注意到了它的有用性、怪异和晦涩,却没有深入探讨它的历史。

在我即将注销 ~ 作为为您的对象提供自定义一元运算符行为的一种很酷的方式之后,我找到了它在 ruby​​ 中实际使用的地方--fixnum(整数)。

~2 返回-3~-1 返回1。所以它否定一个整数并减去一个......出于某种原因?

任何人都可以告诉我波浪号运算符在整个 ruby​​ 中的独特和意外行为的目的吗?

【问题讨论】:

    标签: ruby syntax operators literals


    【解决方案1】:

    使用pry检查方法:

    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 中对十进制整数的否定。

    更重要的是,由于 ruby​​ 是一种面向对象的语言,编码~0b1010 行为的正确方法是定义一个对二进制整数对象执行按位求反的方法(我们称之为~)。为了实现这一点,ruby 解析器(这里都是猜想)必须将任何对象的 ~obj 解释为 obj.~,因此您将获得所有对象的一元运算符。

    这只是一种预感,哪位有更权威或解释清楚的答案,请赐教!

    --编辑--

    正如@7stud 指出的那样,Regexpmakes use of it as well 基本上将正则表达式与$_ 匹配,gets 在当前范围内收到的最后一个字符串。

    正如@Daiku 指出的那样,Fixnums 的按位否定是also documented

    我认为我的解析器解释解决了一个更大的问题,即为什么 ruby​​ 允许 ~ 作为调用 Object#~ 的全局一元运算符。

    【讨论】:

      【解决方案2】:

      对于fixnum,它是一个的补码,它以二进制形式将所有的1 和0 翻转为相反的值。这是文档:http://www.ruby-doc.org/core-2.0/Fixnum.html#method-i-7E。要了解它为什么给出示例中的值,您需要了解负数如何以二进制表示。为什么 ruby​​ 提供这个,我不知道。 Two's complement 通常是现代计算机中使用的一种。它的优点是基本数学运算的相同规则适用于正数和负数。

      【讨论】:

        【解决方案3】:

        ~ 是 Ruby 中的二进制补码运算符。一个补码只是翻转一个数字的位,效果是该数字现在在算术上是负数。

        例如,32 位(Fixnum 的大小)二进制中的 2 是 0000 0000 0000 0010,因此 ~2 将等于 1111 1111 1111 1101 的补码。

        但是,正如您已经注意到并且this article 更详细地讨论的那样,Ruby 的反码版本的实现似乎有所不同,因为它不仅使整数为负数,而且还从中减去 1。我不知道为什么会这样,但似乎确实如此。

        【讨论】:

        • 我认为必须修复文档,因为~ 运算符不会使 one's 互补,而是 two's。见Two's complement on Wikipedia
        • 这正是我开始自己弄乱它时的想法!但是,二进制补码不是翻转位并一,而不是减一吗??
        • "我认为文档必须修复..." 什么文档?这里一切看起来都很好。答案中链接的那篇文章是垃圾。 “因此,Ruby 中的 ~ 运算符表示 {-x - 1} 而不是 {~x}。”是完全错误的。 ~x 在 ruby​​ 中与任何语言一样:按位补码。 Ruby 使用二进制补码(与其他所有人一样),因此它可以表示比一个补码多一个数字,这意味着一个数字的按位补码与原始数字具有不同的大小。这里一切都很好。
        • ??‍♂️ 我不记得我指的是什么文档,那是 8 年前的事了。那时,我可能有一个月的 Ruby 经验会很糟糕,所以我可能指的是当时的 ~ 运算符的官方 Ruby 文档。
        • this other answer about the ~ bitwise operator似乎解释得更好:正如我们在这里讨论的那样,按位补码是一个补码,但是当涉及到负数时,由于它们是如何存在的,所以有一个有趣的交互存储:stackoverflow.com/a/791340/1795402
        【解决方案4】:

        在镐 1.8 的几个地方都提到了它,例如字符串类。但是,在 ruby​​ 1.8.7 中,它不适用于所宣传的 String 类。它确实适用于 Regexp 类:

        print "Enter something: "
        input = gets
        pattern = 'hello'
        puts ~ /#{pattern}/
        
        --output:--
        Enter something: 01hello
        2
        

        它应该与 String 类类似。

        【讨论】:

          【解决方案5】:
          • ~(比格努姆)
          • ~(复杂)
          • ~ (Fixnum)
          • ~(正则表达式)
          • ~ (IPAddr)
          • ~(整数)
          • ~(正则表达式)

          这些都记录在文档中。

          此列表来自 Ruby 2.6 的文档

          这个方法的“大体上”行为基本上是你想要的任何东西,正如你在 Object 类上定义一个名为 ~ 的方法所描述的那样。由实现维护者定义的核心类上的行为似乎有很好的文档记录,因此这些对象不应该有意外的行为。

          【讨论】:

          • 不确定为什么在 6 年后投反对票。但是,是的,它可能有点过时了。 ;)
          猜你喜欢
          • 2019-07-07
          • 1970-01-01
          • 2022-11-19
          • 2013-07-25
          • 2019-04-09
          • 2016-12-06
          • 2012-10-10
          • 2011-06-06
          • 2013-10-13
          相关资源
          最近更新 更多