Почему в этом случае компилятор Ruby считает ожидаемое количество аргументов равным нулю? - PullRequest
2 голосов
/ 30 сентября 2019

Я довольно новичок в Ruby, и у меня возникла проблема с определением объекта класса. Если я правильно понимаю, def initialize должен запрашивать три значения, но код компилируется с ошибкой, которая дается три, но ожидал нулевых значений.

Я думал, что ошибка будет в attr_accessor, но быстротест показал, что это не тот случай.

Версия ruby ​​- 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)

1 Ответ

4 голосов
/ 01 октября 2019

Как вы, возможно, знаете, attr_accessor принимает список символов (или строк) и для каждого из них определены два метода: так называемый установщик (например, health=) и получатель (health). Список (не фактический тип между прочим) - это одно или несколько выражений, разделенных запятой, поэтому в вашем случае Ruby ожидал другое выражение после :armor, потому что после него ставится запятая.

(Если конечная запятаябыло намеренным, скажите нам. Некоторые программисты используют и рекомендуют их в определенных случаях (и в зависимости от языка). Хотя это может усложнить любой ответ, так как Ruby на самом деле им подходит - просто не в этом случае.)

Однако это только одна часть объяснения вашей ошибки. Одна из основных функций Ruby как языка программирования состоит в том, что почти все имеет значение (выражения). Например, попробуйте что-то вроде x = if ... на других языках! Это часто не работает, так как многие языки имеют операторы if, а не выражения if.

В определениях методов Ruby также есть выражения: определение метода с помощью def (def foo; end), используемого для возврата nil. Начиная с определения метода Ruby 2.1 возвращается имя метода (:foo). Теперь возвращаемое значение def-выражений может показаться вам не очень полезным, и это не так во многих случаях, но вы, вероятно, согласны, что имя метода (:foo), безусловно, более полезно, чем nil.

Основным вариантом использования для этого являются такие методы, как private, public, 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, а ваш новый не принимает аргументов, потому что получатели обычно этого не делают.

Переопределения не рассматриваются как ошибки, поскольку они могут быть преднамеренными, однако 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 должен быть оценен перед вызовом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...