Ссылка на индекс массива в Ruby - PullRequest
0 голосов
/ 04 ноября 2018

У меня сложилось впечатление, что ссылки в Ruby эквивалентны указателям на C. Но я смущен этим:

ar = [1, 2, 3, 4]
first = ar[0] # => 1
ar.shift # => 1
ar # => [2, 3, 4]
first # => 1

Разве first не должен указывать на адрес памяти ar[0], который содержит значение 2? Почему до сих пор сохраняется истекшее значение?

Что такое способ Ruby, позволяющий first быть равным текущему значению ar[0]?

Ответы [ 2 ]

0 голосов
/ 05 ноября 2018

Что такое способ Ruby, позволяющий сначала равняться текущему значению ar [0]?

Если вы действительно хотите, вы можете сделать first методом, а не локальной переменной, такой как

ar = [1, 2, 3, 4]
define_method(:first, &ar.method(:first))
first #=> 1
ar.shift # => 1
ar # => [2, 3, 4]
first # => 2

Примечание: Хотя это отвечает на ваш вопрос, я не одобряю это как жизнеспособную концепцию, так как последующее определение первого, например, (first = 12) приведет к first #=> 12 (локальная переменная) и first() #=> 2 (вызов метода)

0 голосов
/ 04 ноября 2018

Вы присвоили переменную 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! изменяет (он же видоизменяет) строку, в которой вы ее вызываете.

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