【问题标题】:Ruby scope, returning a Proc from a functionRuby 作用域,从函数返回 Proc
【发布时间】:2012-07-19 22:49:01
【问题描述】:

来自 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 中作用域的工作原理?如果我调用从give_me_a_proc 返回的 Proc,它仍然可以访问其定义时间范围吗?

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

【问题讨论】:

    标签: javascript ruby scope proc


    【解决方案1】:

    是的,它仍然可以访问定义时间范围。见documentation for Proc class

    更改会向下传递到 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 
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 谢谢,这正是我想要的解释
      • 欢迎。感谢您的反馈。你把这些东西扔掉,你想知道它是否达到了目标。 :)
      【解决方案3】:

      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.newlambda 检查以确保您获得正确数量的参数。

      【讨论】:

        【解决方案4】:

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

        class Foo
          # reopen Foo class
          def get_sum
            @bar + @baz
          end
        end
        
        m = Foo.new(5,20).method(:get_sum)
        m.call #=> 25
        

        mMethod 对象,它充当Foo 实例中的闭包,因此实例变量和self 的值仍然可用。

        【讨论】:

        • 嗯。这为 Foo 添加了一个新的(公共)方法。可能根本不可取。
        猜你喜欢
        • 1970-01-01
        • 2012-01-01
        • 2012-07-24
        • 1970-01-01
        • 2012-09-10
        • 1970-01-01
        • 1970-01-01
        • 2018-04-06
        • 2017-08-08
        相关资源
        最近更新 更多