Как вы, возможно, знаете, 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 должен быть оценен перед вызовом.