Постоянная ошибка назначения в Ruby? - PullRequest
3 голосов
/ 10 июня 2010

Мы поймали какой-то код в Ruby, который кажется странным, и мне было интересно, может кто-нибудь объяснить это:

$ irb
irb(main):001:0> APPLE = 'aaa'
=> "aaa"
irb(main):002:0> banana = APPLE
=> "aaa"
irb(main):003:0> banana << 'bbb'
=> "aaabbb"
irb(main):004:0> banana
=> "aaabbb"
irb(main):005:0> APPLE
=> "aaabbb"

Поймать это?Константа была добавлена ​​одновременно с локальной переменной.

Известное поведение?Ожидаемое

Ответы [ 4 ]

6 голосов
/ 10 июня 2010

Известное поведение. Константы не означают, что вы не можете изменить объект, на который он ссылается, просто вы получите предупреждение (и только предупреждение), если назначите его другому объекту.

Короче говоря, рубиновые константы не являются.

Примечание : Это поведение указано в ответе на вопрос "О каких рубиновых ошибках следует предупредить новичка?" Это стоит прочитать.

4 голосов
/ 10 июня 2010

Поймать это? Константа была добавлена ​​одновременно с локальной переменной.

Нет, он не был добавлен и не являлся локальной переменной.

Объект single , к которому оба относятся константа и локальная переменная, был добавлен, но ни константа, ни локальная переменная не были изменены. Вы не можете изменять или изменять переменную или константу в Ruby (по крайней мере, не так, как подразумевает ваш вопрос), единственное, что вы можете изменить - это объекты.

Единственные две вещи, которые вы можете делать с переменными или константами, - это разыменовывать их и присваивать им.

Константа здесь - красная сельдь, она совершенно не имеет отношения к приведенному примеру. Единственное, что имеет отношение к делу, это то, что во всем примере только один один объект. Этот единственный объект доступен под двумя разными именами. Если объект изменяется, то объект изменяется. Период. Это не таинственно разделить себя на две части. Какое имя вы используете, чтобы посмотреть на измененный объект, не имеет значения. В любом случае, есть только один объект.

Это работает точно так же, как и в любом другом языке программирования: если у вас есть несколько ссылок на изменяемый объект, скажем, в Python, Java, C #, C ++, C, Lisp, Smalltalk, JavaScript, PHP, Perl или любой другой, тогда любое изменение в этом объекте будет видимым независимо от того, какая ссылка используется, даже если некоторые из этих ссылок final или const или как этот конкретный язык называет их.

Это просто то, как работает общее изменяемое состояние, и это лишь одна из многих причин, по которым общее изменяемое состояние является плохим.

В Ruby обычно можно вызывать метод freeze для любого объекта, чтобы сделать его неизменным. Однако, опять же, вы модифицируете объект здесь, так что любой, кто имеет ссылку на этот объект, внезапно обнаружит, что объект стал неизменным. Поэтому, чтобы быть в безопасности, сначала нужно скопировать объект, вызвав dup. Но, конечно, этого тоже недостаточно, если вы думаете о массиве, например: если вы dup массив, вы получите другой массив , но объекты внутри массив все те же, что и в исходном массиве. И если вы freeze массив, то вы больше не можете изменять массив , но объекты в массив вполне могут быть изменяемыми:

ORIG = ['Hello']
CLONE = ORIG.dup.freeze
CLONE[0] << ', World!'
CLONE # => ['Hello, World!']

Это общее изменяемое состояние для вас. Единственный способ избежать этого безумия - это либо отказаться от общего состояния (например, программирование актера: если никто больше не может его видеть, то не имеет значения, как часто или когда оно меняется), либо изменчивого состояния (например, функциональное программирование: если оно никогда не происходит). меняется, тогда не имеет значения, сколько это видят другие).

Тот факт, что одна из двух переменных в исходном примере на самом деле является константой, совершенно не имеет отношения к проблеме. Существует два основных различия между переменной и константой в Ruby: у них разные правила поиска, и константы генерируют предупреждение, если они назначены более одного раза. Но в этом примере правила поиска не имеют значения, и константа назначается только один раз, поэтому в данном случае действительно нет разницы между переменной и константой.

0 голосов
/ 10 июня 2010

Константы в Ruby не являются "константами". Вы могли бы также использовать любое другое имя; размещение их во всех заглавных буквах ничего не изменит в интерпретаторе относительно объекта, если вы не попытаетесь изменить адрес указателя.

Если вы посмотрите на это так, поведение очевидно и необходимо; Apple - это указатель на строковый объект, как и банан. Затем вы редактируете объект, на который указывает банан. Apple указывает на тот же объект, поэтому изменение также отражается там.

0 голосов
/ 10 июня 2010

Вы можете заморозить константы, если хотите, чтобы они были неизменяемыми:

>> APPLE = 'aaa'
=> "aaa"
>> banana = APPLE
=> "aaa"
>> APPLE.freeze
=> "aaa"
>> banana.frozen?
=> true
>> banana << 'bbb'
TypeError: can't modify frozen string
    from (irb):5:in `<<'
    from (irb):5
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...