这是因为在您的add_one
函数中,您没有操作与i
函数外部相同的变量。
让我试着解释一下。在 Ruby 中,您处理通常是可变的对象(值得注意的例外是数字、、、true
和false
)nil
。变量是指向此类对象的指针。多个变量可以指向同一个对象。
a = 123
b = a
现在a
并b
引用同一个对象。如果您将新对象分配给其中一个a
或b
,它们将再次引用不同的对象,同时仍保留名称。
你上面有的是局部变量。这些仅在范围内有效,主要是在方法的持续时间内。如果您创建一个新的局部变量(通过为其分配一个值),它将仅在方法执行期间有效,并且将在离开方法后的某个时间被垃圾收集。
现在正如我上面所说,Ruby 中的大多数对象都是可变的,这意味着您可以在保留变量指针的同时更改它们。一个例子是向数组中添加一个元素:
array = []
array << :foo
现在该array
变量仍将引用它初始化时使用的同一个 Array 对象。但是你已经改变了对象。如果您要为array
变量分配一个新数组,例如
array = [:foo]
它看起来像同一个对象,但实际上它们是不同的(您可以验证检查object_id
每个对象的方法。如果相同,则指的是同一个对象)
现在,当您i += 1
在add_one
方法中执行此操作时,您实际上正在运行i = i + 1
,它将 i 变量设置为本地方法范围内的新值。您实际上并没有更改i
变量,而是在本地方法范围内为其分配了一个新值。这意味着在您的外部范围(即开始/结束块)上命名的变量将引用与您方法中i
的变量不同的对象。这是因为虽然它们具有相同的名称,但它们具有不同的范围。内部作用域总是掩盖外部作用域,因此虽然您在不同作用域中具有相同名称的变量,但它们不会以任何方式干扰(这在处理实例或类变量时会发生变化)i
add_one
不幸的是,正如我上面所说,数字是不可变的。你不能改变它们。如果为变量分配一个新数字,它就是一个新对象。因此,您不能将指向另一个范围内的数字的变量的值更改为更改值的代码。
为了避免这种情况,您可以从函数返回一个值并将其显式分配给i
外部范围内的变量,如下所示
i = 0
def add_one(i)
i+=1
puts "FUNCTION:#{i}"
return i
end
i = add_one(i)
或者你可以使用这样的对象的实例变量
class Foo
def initialize
@i = 0
end
def add_one
@i += 1
end
def do_something
begin
puts "BEGIN:#{@i}"
raise unless @i>5
rescue
add_one
puts "RESCUE:#{@i}"
retry
end
end
end
# create a new object and run the instance method
Foo.new.do_something