4

来自 JavaScript 背景,我已经习惯于使用 JavaScript 的动态范围将值封装在函数中。例如:

function Dog( firstname, lastname ) {

  this.fullname = firstname + lastname

  return {
    say_name: function () {
      return fullname;
    }
  }

}

现在在 Ruby 中,我不太确定这样的事情是否会很好地工作:

class Foo

  attr_accessor :bar, :baz

  def initialize bar, baz
    @bar = bar
    @baz = baz
  end

  def give_me_a_proc
    return Proc.new { @bar + @baz }
  end
end

任何人都可以快速解释范围在 Ruby 中的工作原理吗?如果我调用从返回的 Proc give_me_a_proc,它是否仍然可以访问其定义时间范围?

一旦我定义了proc,这些值是否会变得固定,或者Foo即使在定义proc之后,所做的任何更改也会传递到Proc?

4

4 回答 4

4

是的,它仍然可以访问定义时间范围。请参阅Proc 类的文档

更改将向下传递到 Proc 后定义。irb 的结果与您的班级一起运行。

> foo = Foo.new(1, 2)
 => #<Foo:0x007f9002167080 @bar=1, @baz=2> 
> quux = foo.give_me_a_proc
 => #<Proc:0x007f900214f688@(irb):11> 
> quux.call
 => 3 
> foo.bar = 3
 => 3 
> quux.call
 => 5 
于 2012-07-20T16:35:44.630 回答
3

在 Ruby 中,proc 是一个闭包。在 Javascript 中,函数是一个闭包。闭包的想法是它“关闭”了定义它的环境。在 Ruby 中,proc 中的变量仍然可以更改,即使是在 proc 之外的代码,新值将反映在 proc 中(参见 peakxu 演示)。不确定 Javascript 闭包是否以这种方式工作。

于 2012-07-20T16:42:24.890 回答
2

Ruby procs 和 lambdas 是闭包。当您这样做时,return Proc.new { @bar + @baz }您实际上是在捕获self,这就是查找实例变量的方式。Ruby 块也是闭包。Ruby 确实允许您更改变量,并且更改将传播到调用范围,假设调用范围仍然存在:

@hi = 0
def get_hi()
  lambda {@hi = 42}
end
get_hi.call()
@hi #=> 42

注意:除非你的 proc 有非常宽松的参数要求(它不关心它是否有任何参数,有多少,有点像 C 的int f(void)),使用lambda而不是Proc.new. lambda检查以确保您获得正确数量的参数。

于 2012-07-20T16:55:51.220 回答
1

Ruby 有一种从对象获取闭包的方法,它非常适合您的示例:

class Foo
  # reopen Foo class
  def get_sum
    @bar + @baz
  end
end

m = Foo.new(5,20).method(:get_sum)
m.call #=> 25

是对象,它充当' 实例中的闭包,因此实例变量和 ' 的值m仍然可用。MethodFooself

于 2012-07-20T17:22:36.543 回答