11

我只是在学习 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"
}

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

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

4

4 回答 4

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绑定中的 与块中的本地不同(我不知道,谢谢)。

于 2009-01-07T23:09:00.353 回答
3

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

Ruby 中的块是闭包,这意味着它们在创建时捕获它们周围的范围。创建块时可见的变量就是它看到的变量。如果在代码的顶部有一个fooand ,在任何方法之外,该块在被调用时改变那些。bar

于 2009-01-08T08:34:03.787 回答
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,将生成适当的设置器(foobar方法)。

于 2009-01-08T08:50:16.017 回答
-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,然后块可以使用 |param,another| 访问它 在块的开头。

另外,检查过程。

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"

于 2009-01-07T23:10:17.980 回答