首页 > 解决方案 > 为什么 Ruby 编译器在这种情况下将预期的参数数量计算为零?

问题描述

我对 Ruby 很陌生,在定义类的对象时遇到了问题。如果我理解正确, def initialize 应该要求三个计算,但是代码编译时会出现错误,给出三个,但它期望零值。

我以为 attr_accessor 会出现错误,但快速测试表明情况并非如此。

红宝石版本是 2.6.4

test.rb:16:in `new':参数数量错误(给定 3,预期为 0)(ArgumentError)

class Battler
  attr_accessor :health, :damage, :armor,
  def initialize(health, damage, armor)
    @health = health
    @damage = damage
    @armor = armor
  end

  def able?
    return @health > 0
  end

end

knight = Battler.new(1000, 10, 3)
goblin = Battler.new(20, 6, 1)

问题开始于

knight = Battler.new(1000, 10, 3)

标签: ruby

解决方案


您可能知道attr_accessor接受符号列表(或字符串),并且为每一个定义了两个方法:所谓的 setter(例如health=)和 getter(health)。列表(不是实际的类型顺便说一句。)是一个或多个用逗号分隔的表达式,因此在您的情况下,Ruby 期望后面有另一个表达式,:armor因为您在后面加上了一个逗号。

(如果尾随逗号是故意的,请告诉我们。一些程序员在某些情况下(并且取决于语言)使用并推荐它们。尽管这会使任何答案变得更复杂,因为 Ruby 实际上对它们很好 - 只是不在这个案子。)

然而,这只是你的错误解释的一部分。作为一种编程语言,Ruby 最基本的特性之一是几乎所有东西都有一个值(表达式)。例如,尝试x = if ...使用其他语言!它通常不起作用,因为许多语言都有 if 语句,而不是 if 表达式。

在 Ruby 中,方法定义也是表达式:通过def( def foo; end) 定义用于返回的方法nil。由于 Ruby 2.1 方法定义返回方法的名称 ( :foo)。现在,def-expressions 的返回值对您来说似乎不是很有用,而且在很多情况下也不是很有用,但您可能同意方法名称 ( :foo) 肯定比nil.

其主要用例是 , , 等方法privatepublic它们method_function不是关键字,而是像一个关键字一样使用(它们的定义特征是省略括号的调用):

private def foo
end
# before 2.1
private  # from now on every method in this class context will be private
def foo
end
# or
def foo
end
private :foo  # only foo will be private but we have to repeat the name

我们的朋友attr_accessor也是这样一个类似关键字的方法,但def表达式的值在这里更重要。请记住,它是作为符号的方法名称。您写道(简化):

attr_accessor :health, :damage, :armor,
def initialize(health, damage, armor)
end

# which after the method was defined becomes
attr_accessor :health, :damage, :armor, :initialize

为了让它更清楚一点 - 没有attr_accessor你的代码可能看起来像这样:

class Battler
  def initialize(health, damage, armor)  # takes 3 required arguments
  end
  def health
    @health
  end
  def health=(v)
    @health = v
  end
  # damage, armor
  def initialize  # takes no arguments
    @initialize
  end
  def initialize=(v)
    @initialize = v
  end
end

你看到你正在重新定义Battler#initialize,而你的新定义不接受争论,因为 getter 通常不接受。

重新定义不会被视为错误,因为它们可能是故意的,但是 Ruby 可以为您发出警告。ruby -w test.rb在命令行上运行,它会输出:

test.rb:15: warning: assigned but unused variable - knight
test.rb:16: warning: assigned but unused variable - goblin
test.rb:2: warning: method redefined; discarding old initialize
test.rb:3: warning: previous definition of initialize was here
Traceback (most recent call last):
         1: from test.rb:15:in `<main>'
test.rb:15:in `new': wrong number of arguments (given 3, expected 0) (ArgumentError)

在这里您可以看到在第 2 行initialize中,最初在第 3 行中定义的方法被重新定义。如果您对这里为什么第 3 行在第 2 行之前感到困惑,那么请记住,方法定义是传递给的列表的一部分,attr_accessor因此必须在调用之前评估 def-expr。


推荐阅读