赋值和参数传递

大多数情况下,Ruby 方法有两个接入点 - 比如进出房间的门。参数列表提供了入口;返回值提供了出口。对输入参数的修改不会影响原始数据,原因很简单,当 Ruby 计算表达式时,该计算结果会创建一个新对象 - 因此对参数所做的任何更改只会影响新对象,而不会影响原始对象数据。但是这个规则有例外,我们稍后会看到这样的示例。

in_out.rb

让我们从最简单的情况开始:一个方法,它将获取一个值,其作为命名参数,并返回另一个值:

  1. def change( x )
  2. x += 1
  3. return x
  4. end

从表面上看,你可能会认为我们正在处理单个对象 x,这里:对象 x 进入 change 方法并返回同一个对象 x。事实上,情况并非如此。一个对象进入(参数),出来的是一个不同的对象(返回值)。你可以轻松验证这一点,使用 object_id 方法以显示程序中每个对象的唯一标识数字:

  1. num = 10
  2. puts( "num.object_id=#{num.object_id}" )
  3. num = change( num )
  4. puts( "num.object_id=#{num.object_id}" )

在调用 change 方法之前和之后,变量 num 的标识符是不同的。这表明,即使变量名保持不变,change 方法返回的 num 对象也不同于发送给它的 num 对象。

method_call.rb

方法调用本身与对象的更改无关。你可以通过运行 method_call.rb 来验证这一点。这只是将 num 对象传递给 change 方法并返回它:

  1. def nochange( x )
  2. return x
  3. end

在这种情况下,返回之后的 num 与发送到方法之前的 numobject_id 相同。换句话说,进入方法的对象与再次出来的对象完全相同。这产生了一个必然的结论,即在 change 方法(x += 1)中有一些关于赋值(assignment)的行为导致创建了新的对象。

但是赋值行为本身并不能解释这个问题。如果只是为自己分配一个变量,则不会创建新对象…

assignment.rb
  1. num = 10
  2. num = num # a new num object is not created

那么,如果你为对象分配的值与已有的值相同怎么办?

  1. num = 10
  2. num = 10 # a new num object is not created

这表明单独的赋值必定不会创建新的对象。现在让我们尝试分配一个新值…

  1. num = 10
  2. num += 1 # this time a new num object is created

通过查看 object_id,我们可以确定当为现有的变量分配新值时,会创建一个新对象。

大多数数据项被视为是唯一的,因此一个字符串,”hello” 被认为与其它另一个字符串 “hello” 不同,以及一个浮点数 10.5 被认为与其它另一个浮点数 10.5 不同。因此,任何字符串(string)或浮点数(float)赋值操作都将创建一个新对象。

但是在处理整数(integer)时,只有当分配的值与前一个值不同时才会创建一个新对象。你可以在赋值运算符的右侧执行各种复杂的操作,但如果生成的值与原始值相同,则不会创建新对象…

  1. num = (((num + 1 - 1) * 100) / 100) # a new object is not created!
object_ids.rb