2009-01-07 76 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方法)。

相關問題