【问题标题】:What does the + method in Ruby do, exactly?Ruby 中的 + 方法到底是做什么的?
【发布时间】:2015-07-04 04:35:06
【问题描述】:

假设我想编写自己的数学运算符,例如“+”

简单的版本是:

def plus(a,b)
    return a+b
end

但这不是真正的“+”所做的。

我想要3.add(4) # =>7 但是我如何告诉 ruby​​ 获取我使用我的方法的对象呢?

我试过了

def add(c)
    return self+c
end

但我收到错误消息:

:in <main>': private methodadd' 调用 3:Fixnum (NoMethodError)

【问题讨论】:

  • 不是 Ruby 人,但我怀疑您的答案是一个特殊变量,称为 thisself。这就是大多数面向对象语言的做法。
  • 是的,但我在第二个代码示例中使用了“self”,但它不起作用。
  • 嗯。您的错误消息特别提到该方法是私有的。您需要做些什么来公开该方法吗?
  • 可能是这样。不幸的是,我不知道该怎么做/这意味着什么,因为我是新手,还没有读过关于公共和私人的内容。
  • 我认为您发布的错误消息不正确。该消息显示add,但您定义的方法称为plus。所以我怀疑你没有复制/粘贴你的确切代码和错误消息。

标签: ruby math operators addition


【解决方案1】:

ruby 中的加号 (+) 可以像任何其他方法一样被覆盖(您可以寻找运算符重载):

class MyOperator
  attr_accessor :text
  def initialize(text)
    @text = text
  end
  def +(operand)
     "#{self.text} #{operand.text}"
  end

  def to_s
    self.text
  end
end


a = MyOperator.new "Hello"
b = MyOperator.new "World"


puts (a+b)

所以它没有太多的魔力。但是,如果重载运算符在您的上下文中有意义,您必须小心。

【讨论】:

    【解决方案2】:

    问题

    你定义了方法:

    def add(c)
      return self + c
    end
    

    并试图这样使用它:

    3.add(4) #=> NoMethodError: private method `add' called for 3:Fixnum
    

    了解此错误消息

    此错误消息会准确告诉您问题所在。我认为您的问题只是您不了解 Ruby 如何调用对象上的方法。

    当 Ruby 看到3.add(4) 时,它首先查看接收器3,然后确定:

    3.class #=> Fixnum
    

    这告诉它add 方法的定义位置:在Fixnum 类中或Fixnum 的祖先类或模块之一中。

    所以它在那里寻找它,没有找到它,并发出一条错误消息。我们可以确认它不存在:

    Fixnum.instance_methods.include?(:add)
      #=> false
    

    那么add 是在哪里定义的?

    不过,您确实定义了它,那么它在哪里?让我们找出答案:

    method(:add).owner
      #=> Object 
    
    Object.instance_methods.include?(:add)
      #=> false
    

    Object.instance_methods 返回在ObjectObject 的祖先上定义的所有public 实例方法的数组。 add 不在其中,因此我们得出结论 add 是一个受保护的私有方法:

    Object.protected_instance_methods.include?(:add)
      #=> false
    
    Object.private_instance_methods.include?(:add)
      #=> true
    

    让我们尝试在Object 的实例上调用该方法:

    Object.new.add(4)
      #=> NoMethodError: 
      #   private method `add' called for #<Object:0x007fdb6a27fa68>
    

    这是有道理的,考虑到Object#add 是私有的。但是,我们可以使用 Object#send 调用私有方法:

    Object.new.send(:add,4)
      #NoMethodError: undefined method `+' for #<Object:0x007fdb6a28e068>
    

    作为练习,请确保您了解 Ruby 采取的导致她引发此异常的步骤(实例方法 + 没有在 Object 上定义,或者等效地,Object 的实例没有有一个方法+)。

    顺便问一下,add 是在哪里定义的?我的意思是,当您定义 self 时,它的值是多少?让我们看看:

    self       #=> main
    self.class #=> Object
    

    我们看到add 必须在其接收者是实例的类上定义。 (一口,是的,但这很重要,所以请确保您理解这一点)。

    为什么Object#add 是私有的而不是公共的?

    考虑:

    def greet
      puts 'hi'
    end
    
    class A
    end
    
    A.private_instance_methods.include?(:add)
      #=> true 
    A.new.send(:greet)
      #=> 'hi'
    

    这是因为AObject继承greet

    A.ancestors.include?(Object) #=> true
    

    如果Object#greet 是公共的,那么每个内置类和您定义的每个类都会有一个公共实例方法greet。那会带来很大的痛苦。 (假设你有一个方法great 并且输入错误greet!)即使是私有的greet 也可能会造成麻烦。)

    应该在哪里定义add

    由于add.class =&gt; Fixnum,我们这样定义它:

    class Fixnum
      def add(other)
        self + other
      end
    end
    
    Fixnum.instance_methods.include?(:add) #=> true
    3.add(4)                               #=> 7
    

    如果我在class Fixnum 之后包含puts "self#{self}" 行,它将打印“Fixnum”。用显示self 值的puts 语句对代码进行加盐通常有助于理解发生了什么。

    最后一件事:

    method(:add).owner
      #=> NameError: undefined method `add' for class `Object'
    

    为什么这没有返回Fixnum?由于method 没有明确的接收者(即没有xx.method),Ruby 假定接收者是self,这里是:

    self #=> main 
    

    所以她在self.class =&gt; Object 中寻找方法method,你知道她找到了什么(或者,我应该说,没有找到)。相反,我们需要写:

    Fixnum.instance_method(:add).owner #=> Fixnum
    

    3.method(:add).owner #=> Fixnum
    

    这里的3 当然可以替换为Fixnum 的任何实例。

    注意我已经稍微简化了这个解释。在搜索方法时,Ruby 还会查看接收者的单例类。但是,对于直接对象(数字、符号、truefalsenil)来说,这不是问题,因为它们没有单例类:

    3.singleton_class     #=> TypeError: can't define singleton
    

    相比之下,例如:

    [1,2].singleton_class #=> #<Class:#<Array:0x007fbcf18c01a8>> 
    

    【讨论】:

      猜你喜欢
      • 2023-04-11
      • 2013-11-29
      • 2014-10-16
      • 2015-01-23
      • 1970-01-01
      • 2015-08-06
      • 2013-09-02
      • 2014-01-02
      相关资源
      最近更新 更多