在Ruby中引用数组索引

时间:2018-11-04 02:49:59

标签: arrays ruby

我对Ruby中的引用等同于C中的指针感到印象深刻,但是我对此感到困惑:

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

first是否指向ar[0]的存储地址,该地址保留值为2?为什么它仍然保持在过期值?

first等于ar[0]当前值的Ruby方法是什么?

2 个答案:

答案 0 :(得分:1)

您将变量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]user1first都更改了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核心对象ID相当恒定,但假装它们是不可预测的。它们旨在使您能够轻松比较两个对象。 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

现在,firstar[0]是相同的字符串值,但是是一个新的字符串实例。您可以安全地对firstar[0]进行突变,而无需更改其他变量。

突变并不总是坏的。这可能会很有帮助。但是,如果您不知道突变的发生,那么意外的突变可能会真的使您感到困惑。尤其是如果您要为函数更改参数,因为原始值是在其他位置定义的,现在已经更改了。

如果存在可变版本,Ruby倾向于以!结尾的方法名称。就像subsub!一样。 sub返回一个新字符串,并应用您的替换,而保留原始字符串。 sub!修改(aka变异)您调用它的字符串。

答案 1 :(得分:0)

  

让Ruby首先等于ar [0]的当前值的Ruby方法是什么?

如果您确实想要,可以将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(方法调用)