Вы присвоили переменную first
любому первому элементу в то время. Поскольку ar[0]
был 1 в то время, это было точно так же, как просто сказать first = 1
.
Это не то же самое, что вызов ar.first
, который всегда указывает на первый элемент (синоним ar[0]
).
Ruby использует так называемый object_id
для отслеживания каждого экземпляра объекта. Когда вы говорите first = ar[0]
, Ruby назначает first
тому же объекту, что и в ar[0]
. Эти два объекта останутся синхронизированными, и любые выполненные с ними мутации останутся синхронизированными, поскольку это точно один и тот же объект в памяти. Если вы переназначите любую переменную (ar[0]
или first
) на другой объект, вы потеряете синхронизацию.
irb(main):001:0> ar = ['a','b','c']
=> ["a", "b", "c"]
irb(main):002:0> first = ar[0]
=> "a"
irb(main):003:0> ar[0] << ' testing!'
=> "a testing!"
irb(main):004:0> first
=> "a testing!"
irb(main):005:0> ar[0].replace('this is a test')
=> "this is a test"
irb(main):006:0> ar
=> ["this is a test", "b", "c"]
irb(main):007:0> first
=> "this is a test"
irb(main):008:0> ar[0].object_id
=> 70159033810680
irb(main):009:0> first.object_id
=> 70159033810680
irb(main):010:0> ar[0] = 'different string instance'
=> "different string instance"
irb(main):011:0> ar
=> ["different string instance", "b", "c"]
irb(main):012:0> first
=> "this is a test"
irb(main):013:0> ar[0].object_id
=> 70159033712320
Метод <<
и методы replace
изменяют строковый объект. Если вы используете Rails и знакомы с ActiveRecord, это будет похоже на:
user1 = User.new(first_name: 'Bob')
user2 = User.new(first_name: 'Joe')
user3 = User.new(first_name: 'Dave')
ar = [user1, user2, user3]
first = ar[0]
В конце концов, все, что вы сделали, было first = user1
Если вы сделаете user1.first_name = 'Jim'
, вы увидите, что ar[0]
, user1
и first
все изменили first_name
, потому что вы мутировали объект ActiveRecord.
Числа неизменны. Вы не можете изменить объект, который находится в памяти. Вы не можете изменить 1
на 5
. Все, что вы можете сделать, это обновить переменную, чтобы использовать другой экземпляр объекта.
irb(main):014:0> testing = 1 + 5
=> 6
irb(main):015:0> testing.object_id
=> 13
irb(main):016:0> 6.object_id
=> 13
6
в основном константа. Вы не можете изменить 1, вы не можете изменить 5, но вы можете сложить их вместе, чтобы получить 6.
Краткое примечание о object_id
. Некоторые из идентификаторов объектов ядра Ruby довольно постоянны, но просто притворяются, что они не предсказуемы. Они предназначены для того, чтобы вы могли легко сравнивать два объекта. Некоторые функции в Ruby используют object_id для определения равенства, потому что если object_id двух разных переменных одинаковы, то они определенно равны, поскольку они указывают на одну и ту же ячейку памяти.
Каждый раз, когда вы создаете строковый литерал записи, вы создаете новый объект в памяти.
irb(main):017:0> a = 'testing'
=> "testing"
irb(main):018:0> b = 'testing'
=> "testing"
irb(main):019:0> a.object_id
=> 70159025508120
irb(main):020:0> b.object_id
=> 70159029161840
Если вы изменили a
, b
не изменится, потому что это разные объекты. Если вы передаете строковую переменную, важно знать, является ли вызываемое вами действие мутативным или нет, и хотите ли вы на самом деле мутацию.
Например, вы можете в какой-то момент жизни создать массив.
ar = ['a','b','c']
=> ["a", "b", "c"]
А затем решите, что вы хотите использовать этот первый элемент массива в аргумент функции или сохранить его в переменной.
first = ar[0]
=> "a"
Это хорошо, если вы не мутируете. Ну, может быть, вы хотите изменить переменную first
по какой-то причине. Возможно, вы создаете список классов HTML.
first << ' some cool text'
=> "a some cool text"
first
=> "a some cool text"
Все вроде нормально, верно?
Но посмотрите, что это сделало с вашим массивом.
ar
=> ["a some cool text", "b", "c"]
Если вы видите время, когда вы хотите изменить передаваемую переменную, вы можете назначить копию этой переменной.
first = ar[0].dup
Теперь first
- это то же строковое значение, что и ar[0]
, но новый экземпляр строки. Вы можете безопасно изменять first
или ar[0]
, не меняя другой.
Мутация не всегда плохая. Это может быть очень полезно. Но неожиданные мутации могут быть действительно запутанными, если вы не знаете, что они происходят. Особенно, если вы изменяете аргумент функции, потому что исходное значение было определено в другом месте, а теперь оно изменилось.
Ruby имеет тенденцию оканчивать имена методов на !
, если есть изменяемая версия. Как sub
против sub!
в строке. sub
возвращает новую строку с примененными вами подстановками, оставляя исходную строку в покое. sub!
изменяет (он же видоизменяет) строку, в которой вы ее вызываете.