代码之家  ›  专栏  ›  技术社区  ›  Bryan Oakley

Ruby:一个块能影响一个方法中的局部变量吗?

  •  11
  • Bryan Oakley  · 技术社区  · 17 年前

    我只是在学习Ruby,并试图理解代码在块中执行的范围。例如,我希望能够创建一个影响它所附加到的方法的块,如下所示:

    def test(&block)
      block.call() if block_given?
      puts "in test, foo is #{foo}"
      puts "in test, bar is #{bar}"
    end
    
    test() {
      foo="this is foo"
      bar="this is bar"
    }
    

    在这种情况下,我根本不想修改块——我希望能够使用简单的变量引用而不使用参数来编写它。 仅通过更改上述示例中的“测试”方法 ,是否可以访问块中定义的变量?

    同样,目标是不修改块,但在块执行后能够从“test”中访问创建的变量。

    4 回复  |  直到 13 年前
        1
  •  11
  •   wberry    13 年前

    首先, block.call() 完成了 yield 你不需要 &block 这样参数。

    通常情况下,您不能执行所需的操作,块在创建时是绑定的,在块内您可以看到此时定义的局部变量;执行所需操作的最简单方法(而不是通常使用块的方法)是:

    def test()
      foo = yield if block_given?
      puts "in test, foo is #{foo}"
    end
    
    test() {
      foo="this is foo"
    }
    

    但这只是一个副作用,因为 foo 被块“返回”。如果您改为这样做:

    def test()
      foo = yield if block_given?
      puts "in test, foo is #{foo}"
    end
    
    test() {
      foo="this is foo"
      "ha ha, no foo for you"
    }
    

    你会注意到它有不同的作用。

    更神奇的是:

    def test(&block)
       foo = eval "foo", block.binding
       puts foo
       block.call
       foo = eval "foo", block.binding
       puts foo
    end
    
    foo = "before test"
    test() {
      foo = "after test"
      "ha ha, no foo for you"
    }
    

    这是一种工作,但如果你把它移开,它就会断裂。 foo = "before test" 因为 成为块中的局部变量,并且不存在于绑定中。

    摘要:不能从块访问局部变量,只能访问定义块的局部变量和块的返回值。

    即使这样也不行:

    def test(&block)
       eval "foo = 'go fish'", block.binding
       block.call
       bar = eval "foo", block.binding
       puts bar
    end
    

    因为 在绑定上不同于本地块(我不知道,谢谢)。

        2
  •  3
  •   Chuck    17 年前

    不,块不能影响调用它的地方的局部变量。

    红宝石中的块是 闭包 这意味着它们在创建时捕获周围的作用域。创建块时可见的变量就是它看到的变量。如果有 foo bar 在代码的顶部,在任何方法之外,该块 打电话的时候换一下。

        3
  •  2
  •   rkj    17 年前

    你可以做你想做的事情,只要稍微详细一点:

    class Test
      def foo(t)
        @foo = t
      end
      def bar(t)
        @bar = t
      end
      def test(&block)
        self.instance_eval &block if block_given?
        puts "in test, foo is #{@foo}"
        puts "in test, bar is #{@bar}"
      end
    end
    
    Test.new.test() {
      foo "this is foo"
      bar "this is bar"
    }
    

    您可以创建如下方法 attr_accessor 将生成一个优先级设置器 foo bar 方法)。

        4
  •  -1
  •   dylanfm    17 年前
    def test(&block)
      foo = yield
      puts "in test, foo is #{foo}"
    end
    
    test { "this is foo" }
    

    印刷品 in test, foo is this is foo

    收益率值是块的值。

    您还可以将参数传递给yield,然后块可以使用param(块开头的另一个)访问该参数。

    另外,检查程序。

    foo = "this is foo"
    p = Proc.new { "foo is #{foo}" }
    p.call
    

    印刷品 "foo is this is foo"

    def test(p) 
      p.call
    end
    
    test p
    

    印刷品 “foo是foo吗?”

    def test2(p)
      foo = "monkey"
      p.call
    end
    
    test2 p
    

    印刷品 “foo是foo吗?”

    推荐文章