2009-01-07 71 views
10

我只是在学习ruby,并试图理解在代码块中执行的代码范围。举例来说,我希望能够创建一个影响其所连接的方法,像这样一个块: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”中访问创建的变量。

回答

10

首先,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
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"

+0

这是误导,你没有访问块中的当地人,因为问题说,只是块的返回值。 – 2009-01-07 23:30:12

3

不,块不能在它被称为地方影响局部变量。

Ruby中的块是关闭,这意味着它们捕获它们创建时的范围。创建块时可见的变量是它所看到的变量。如果代码顶部有foobar,则在任何方法之外,该块会在调用它时更改它们。

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方法,将产生apropriate二传手(在foobar方法)。

相关问题