ruby:一个块可以影响方法中的局部变量吗?

时间:2009-01-07 22:56:06

标签: ruby closures

我只是学习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'方法,是否可以访问块中定义的变量?

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

4 个答案:

答案 0 :(得分:11)

首先,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"就会中断,因为foo成为块中的局部变量而在绑定中不存在。

总结:您无法访问块中的局部变量,只能访问定义块的局部变量以及块的返回值。

即使这样也行不通:

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

因为绑定中的foo与块中的本地不同(我不知道这个,谢谢)。

答案 1 :(得分:3)

不,一个块不能影响调用它的地方变量。

Ruby中的块是闭包,这意味着它们在创建时会捕获它们周围的范围。创建块时可见的变量是它看到的变量。如果您的代码顶部有foobar,那么在任何方法之外,会阻止 更改它们。

答案 2 :(得分:2)

你可以通过更加冗长的方式做你想做的事:

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之类的方法来生成适当的setter(foobar方法)。

答案 3 :(得分:-1)

def test(&block)
  foo = yield
  puts "in test, foo is #{foo}"
end

test { "this is foo" }

打印in test, foo is this is foo

yield的值是块的值。

您还可以将参数传递给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 is this is foo"

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

test2 p

打印"foo is this is foo"