ruby - 为什么在 ruby​​ 救援 block 中调用的函数无法修改变量?

标签 ruby loops exception

我有一个与这段代码相当的情况:

i=0
def add_one(i)
  i+=1
  puts "FUNCTION:#{i}"
end

begin
  puts "BEGIN:#{i}"
  raise unless i>5
rescue
  add_one(i)
  puts "RESCUE:#{i}"
  retry
end

当我运行它时,我反复看到这个输出:

BEGIN:0
FUNCTION:1
RESCUE:0

此版本递增i 并完美地完成程序:

i=0
begin
  puts "BEGIN:#{i}"
  raise unless i>5
rescue
  i+=1
  puts "RESCUE:#{i}"
  retry
end

为什么会有差异?我怎样才能在救援 block 中获得一个函数来实际修改一个变量?

最佳答案

这是因为在您的 add_one 函数中,您没有操作与函数外部相同的 i 变量。

让我试着解释一下。在 Ruby 中,您通常处理可变对象(值得注意的异常(exception)是数字、truefalsenil)。变量是指向此类对象的指针。多个变量可以指向同一个对象。

a = 123
b = a

现在 ab 指的是同一个对象。如果您将新对象分配给 ab 之一,它们将再次引用不同的对象,同时仍保留名称。

以上是局部变量。这些仅在范围 内有效,主要是方法的持续时间。如果你创建一个新的局部变量(通过给它赋值),它只会在方法运行期间有效,并且在离开方法后的某个时间会被垃圾回收。

正如我上面所说,Ruby 中的大多数对象都是可变的,这意味着您可以在保留变量指针的同时更改它们。一个例子是向数组添加一个元素:

array = []
array << :foo

现在 array 变量仍将引用它初始化时使用的同一个 Array 对象。但是你已经改变了对象。如果您要为 array 变量分配一个新数组,例如

array = [:foo]

它看起来像是同一个对象,但实际上它们是不同的(您可以验证正在检查每个对象的 object_id 方法。如果相同,则您指的是非常同一个对象)

现在,当您在 add_one 方法中执行 i += 1 时,您实际上是在运行 i = i + 1 这将设置我在本地方法范围内变量为新值。您实际上并没有更改 i 变量,而是在本地方法范围内为其分配了一个新值。这意味着在您的外部范围(即开始/结束 block )上名为 i 的变量将引用与 add_one< 中的 i 变量不同的对象 方法。这是因为虽然它们具有相同的名称,但它们具有不同的范围。内部作用域总是掩盖外部作用域,因此当您在不同作用域中拥有具有相同名称的变量时,它们不会以任何方式干扰(这在处理实例或类变量时会发生变化)

不幸的是,正如我上面所说,数字是不可变的。你无法改变它们。如果您将一个新数字分配给一个变量,它就是一个新对象。因此,您不能像更改值的代码那样更改指向另一个范围内数字的变量的值。

为了避免这种情况,您可以从函数返回一个值,然后像这样显式地将它分配给外部作用域中的 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

关于ruby - 为什么在 ruby​​ 救援 block 中调用的函数无法修改变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11106641/

相关文章:

java - 在应用程序级别处理数据库崩溃

ruby - 创建具有均匀间隔值的数组

c++ - 删除两个 vector C++ 中的相似元素

string - "Borrowed value does not live long enough", 在循环中使用时丢弃

c++ - Vector在C++中的实现

java - 线程 "main"java.lang.NoClassDefFoundError : org/apache/http/conn/scheme/SchemeSocketFactory while Using Crawler4j 中出现异常

ruby-on-rails - 比较 Ruby on Rails 中的时间和日期时间?

arrays - 使用 Redis 存储哈希数组

ruby - 打印变量

c# - 变量在多个循环内不递增