2011-06-12 101 views
0

我很困惑塊內變量的範圍。這工作:範圍令人困惑

f = 'new' 
[1,2,3].each do |n| puts f * n end 
#=> new newnew newnewnew 

但這並不:

[1,2,3].each do |n| 
    a ||=[] 
    a << n 
end 
a 
#=>a does not exsit! 

這是爲什麼?請爲我提供一些關於這個話題的資源。

+1

@sawa感謝您再次糾正我的問題 – mko 2011-06-14 09:19:05

回答

7

什麼讓人困惑?

在第一片斷f被創建並隨後each塊被執行,它可以看到外面本身的東西(稱爲封閉的範圍)。所以它可以看到f

在第二個片段中,您在該塊內部創建了a,因此其範圍就是該塊。在塊之外,a不存在。

當你引用一個名字時(例如a),ruby將從當前作用域向外移動,查看所有封閉作用域的名稱。如果它在其中一個封閉範圍內找到它,它將使用與該名稱關聯的值。如果不是,則返回大多數本地範圍並在那裏創建名稱。隨後的名稱查找將產生與該名稱關聯的值。

塊結束時,該範圍內的名稱將丟失(不會丟失,只是名稱;當垃圾回收器發現沒有更多名稱(或任何內容)引用時,值會丟失該值,並且gc收集該值以重用其內存)。


如果可視化是你的事情,我覺得把範圍想象成樓梯是有幫助的,在程序開始時,你站在最前面的步驟。每輸入一個塊,你就下一步。您可以在當前步驟中看到所有內容,並且可以看到所有步驟都在您正在使用的步驟之上,但下面的步驟中沒有任何內容。當你引用一個變量名時,你可以看看你正在尋找它的步驟。當你看到它時,你使用該值。如果你沒有看到它,你會看到你正在上面的下一個步驟。如果你看到它,你使用該值。你一直這樣做,直到你看到了最高的一步,但沒有看到那個名字。如果發生這種情況,您可以在您所站的步驟上創建名稱(如果您正在查看它的作業,請給它一個值)。下一次你尋找那個名字時,你會在你站在的那一步看到它,並在那裏使用它。

當一個模塊結束時,您將踏上一個臺階。由於您在下面的步驟中看不到任何名稱,因此您之前步驟中的所有名稱都會丟失。

如果這可以幫助你,那麼想一想。如果沒有,不要。

其實你在第二個步驟,因爲你在全球範圍內不是,但是從全球範圍內使用的名稱,你必須在名稱的開頭使用$。因此,在樓梯示例中,如果您要查找的名稱在開頭處具有$,則可直接查看最上面的步驟。如果沒有,你看起來不那麼遠。然而,這是錯誤的,因爲程序中的所有樓梯都會共享相同的頂級步驟,這是很奇怪的想法。

+1

+1。這取決於範圍'a'是***第一***使用。 – 2011-06-12 17:32:42

+0

如果我使用比喻,我會提到一輛帶煙燻玻璃的汽車;你可以從內部看到外部,但不是相反。 – sawa 2011-06-12 17:57:15

+0

@sawa是的,但你有嵌套的範圍,這將像汽車內的汽車。然後事情開始變得混亂。 – 2011-06-12 17:58:00

1

很簡單,一個塊中定義的變量是不可見的外(如果發生這種情況,我們會說變量已經泄露,並作爲顧名思義,這將是壞的):

>> lambda { x = 1 }.call 
=> 1 
>> x 
NameError: undefined local variable or method `x' for main:Object 
2

地圖的效果要好得多:

a = [1,2,3].map do |n| 
    n 
end 

不需要聲明a外塊。

+0

你是對的!但我只想舉例說明我不能在範圍之外使用的變量 – mko 2011-06-14 09:45:28