我对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方法是什么?
答案 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]
,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核心对象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
现在,first
与ar[0]
是相同的字符串值,但是是一个新的字符串实例。您可以安全地对first
或ar[0]
进行突变,而无需更改其他变量。
突变并不总是坏的。这可能会很有帮助。但是,如果您不知道突变的发生,那么意外的突变可能会真的使您感到困惑。尤其是如果您要为函数更改参数,因为原始值是在其他位置定义的,现在已经更改了。
如果存在可变版本,Ruby倾向于以!
结尾的方法名称。就像sub
和sub!
一样。 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
(方法调用)