Ruby 2.3.3 The Well Grounded Rubyist — неопределенный метод «баланс» для 25:Fixnum

В настоящее время я просматриваю «The Well Grounded Rubyist 2nd Edition». Я нахожусь на странице 196 и получил следующий код.

class Account
  attr_accessor :balance

  def initialize(amount=0)
    self.balance = amount
  end

  def +(x)
    self.balance += x
  end

  def -(x)
    self.balance -= x
  end

  def to_s
    balance.to_s
  end

end

Я использовал это в сеансе irb, например

2.3.3 :001 > require './account.rb'
 => true
2.3.3 :002 > acc = Account.new(20)
 => #<Account:0x007fccb1834ef8 @balance=20>
2.3.3 :003 > balance
NameError: undefined local variable or method `balance' for main:Object
    from (irb):3
    from /Users/BartJudge/.rvm/rubies/ruby-2.3.3/bin/irb:11:in `<main>'
2.3.3 :004 > acc.balance
 => 20
2.3.3 :005 > acc+=5
 => 25
2.3.3 :006 > acc.balance
NoMethodError: undefined method `balance' for 25:Fixnum
    from (irb):6
    from /Users/BartJudge/.rvm/rubies/ruby-2.3.3/bin/irb:11:in `<main>'
2.3.3 :007 > acc -= 5
 => 20
2.3.3 :008 > acc.balance
NoMethodError: undefined method `balance' for 20:Fixnum
    from (irb):8
    from /Users/BartJudge/.rvm/rubies/ruby-2.3.3/bin/irb:11:in `<main>'
2.3.3 :009 >

Строка 4 работает так, как я ожидал acc.balance Однако, когда я снова использую ее в строке 8, я получаю следующую ошибку undefined method `balance' for 20:Fixnum

Когда я делаю следующее, он работает последовательно, как я ожидаю.

 => true
2.3.3 :002 > acc = Account.new(20)
 => #<Account:0x007f82d1834f18 @balance=20>
2.3.3 :003 > acc.balance
 => 20
2.3.3 :004 > acc.balance
 => 20
2.3.3 :005 > acc.+ (5)
 => 25
2.3.3 :006 > acc.balance
 => 25
2.3.3 :007 > acc.-(10)
 => 15
2.3.3 :008 > acc.balance
 => 15
2.3.3 :009 >

Я предполагаю, что это как-то связано с тем, как вызываются методы, но я не могу найти ничего, чтобы объяснить это. Кто-нибудь может пролить свет на несоответствие результатов и почему FIXNUM участвует. Я думал, что @balance будет INTEGER.

ТИА.


person Bart_Judge    schedule 08.12.2019    source источник
comment
acc + 5 это сокращение от acc.+(5)   -  person steenslag    schedule 08.12.2019


Ответы (2)


Операторы присваивания += и -= фактически переназначают переменную. acc += 1 на самом деле просто сокращение от acc = acc + 1.

И почему FIXNUM вмешивается. Я думал, что @balance будет INTEGER.

До Ruby 2.4 было два класса — Fixnum и Bignum, которые представляют целые числа разных размеров.

Ruby 2.4 заменил их на унифицированный целочисленный класс.

person max    schedule 08.12.2019
comment
С технической точки зрения, в Ruby всегда был только класс Integer, при условии, что реализациям разрешалось иметь подклассы, специфичные для реализации. Однако программисту не разрешалось полагаться на их наличие. - person Jörg W Mittag; 09.12.2019
comment
Fixnum / Bignum существуют (s/ed) в MRI, YARV, Rubinius, JRuby и IronRuby, я полагаю, но, например. Если я правильно помню, у MacRuby вместо этого было NSInteger, а MagLev использовал базовое числовое представление Smalltalk VM. Лично я всегда считал ошибкой разоблачение этой оптимизации производительности. Сравните с flonums в YARV или оптимизацией небольших строк и небольших объектов в различных реализациях, которые полностью прозрачны для программиста. - person Jörg W Mittag; 09.12.2019
comment
Например, некоторые реализации оптимизируют объекты с небольшим количеством переменных экземпляра, сохраняя переменные экземпляра внутри заголовка объекта с фиксированными смещениями, а не в таблице переменных экземпляра. Однако было бы действительно странно видеть подклассы SmallObject и BigObject класса Object и объекты, волшебным образом перемещающиеся между ними... но это именно так делают целые числа в старых версиях YARV. - person Jörg W Mittag; 09.12.2019
comment
YARV по-прежнему выполняет ту же самую оптимизацию, что и раньше, просто больше не раскрывает ее. Эта оптимизация хорошо известна и десятилетиями использовалась Smalltalks и Lisps, MRI отличалась тем, что раскрывала ее, и, естественно, почти все другие реализации следовали ей, поскольку MRI была спецификацией. - person Jörg W Mittag; 09.12.2019
comment
Глубина ваших знаний здесь довольно удивительна. Я едва вышла за пределы МРТ. Надеюсь, мы поняли это прямо сейчас, хотя. - person max; 09.12.2019

Вызов только balance вызовет NameError, потому что он пытается вызвать объект на main, который не был определен. Вы получите ту же ошибку, если просто вызовете случайные слова, такие как foo или bar. Методы должны вызываться для объекта.

Ответ Макса объясняет часть проблемы. Однако я бы посоветовал вам определить свой класс таким образом, чтобы избежать ошибки.

class Account
  attr_accessor :balance

  def initialize(amount=0)
    @balance = amount
  end

  def add(x)
    @balance += x
  end

  def subtract(x)
    @balance -= x
  end

  def to_s
    @balance.to_s
  end

end
person lacostenycoder    schedule 08.12.2019
comment
Я согласен, но я подозреваю, что цель примера заключалась в том, чтобы объяснить переопределение операторов. - person max; 08.12.2019
comment
Хорошо спасибо. Я просто запутался, почему вызов метода с точкой не приводит к неопределенному методу `баланса' ''', но когда я вызываю метод без точки, это происходит. Я использую '''acc.balance''', поэтому я не уверен, что вы имеете в виду, когда говорите, что я вызываю метод на main. Я всегда вызываю методы экземпляра класса Account. - person Bart_Judge; 19.12.2019
comment
@Bart_Judge Ruby, чтобы упростить это, запустите консоль irb irb, а затем просто введите self и введите. вы получите =>main. Ruby проверяет, определен ли вызываемый объект. Он пытается найти его в текущей области. Ошибка говорит вам, что balance не определено в этой области main. Когда вы позвоните obj.method, он попытается позвонить method на obj. Но если нет, он будет искать следующую возможную область применения. - person lacostenycoder; 19.12.2019
comment
@lacostenycoder Итак, когда я делаю acc+=5, Ruby думает, что я пытаюсь вызвать его на main? Но тогда как он может прибавить 5 к 20, чтобы получить 25? 2.3.3 :004 > acc.balance => 20 2.3.3 :005 > acc+=5 => 25 2.3.3 :006 > acc.balance NoMethodError: undefined method `balance' for 25:Fixnum - person Bart_Judge; 19.12.2019