2011-05-25 24 views
29

我完全困惑Lua的變量作用域和函數參數傳遞(值或引用)。函數/變量作用域(傳遞值或引用?)

請參見下面的代碼:

local a = 9  -- since it's define local, should not have func scope 
local t = {4,6} -- since it's define local, should not have func scope 

function moda(a) 
    a = 10   -- creates a global var? 
end 
function modt(t) 
    t[1] = 7   -- create a global var? 
    t[2] = 8 
end 

moda(a) 
modt(t) 
print(a) -- print 9 (function does not modify the parent variable) 
print(t[1]..t[2]) -- print 78 (some how modt is modifying the parent t var) 

因此,這種行爲完全混淆了我。

  • 這是否意味着通過 引用傳遞給函數 ,而不是看重的表變量?

  • 如何創建全局變量 與已定義的 局部變量衝突?

    • 爲什麼modt能夠 修改表尚未moda不能 修改一個變量?

回答

37

你猜對了,表變量是通過引用傳遞。引用Lua 5.1 Reference Manual

在Lua中有八種基本類型:nil,boolean,number,string,function,userdata,thread和table。 ....

表,函數,線程和(完整)用戶數據值是對象:變量實際上不包含這些值,只包含對它們的引用。賦值,參數傳遞和函數返回總是處理對這些值的引用;這些操作並不意味着任何形式的副本。

因此零,布爾值,數字和字符串都是按值傳遞的。這正好解釋了你觀察到的行爲。

+4

這與通過引用略有不同。 (看我的答案)。特別是'function(x)x = {} end'的行爲是不同的。 – 2011-12-02 08:32:03

+1

所有東西都是按值傳遞的,通過某些類型(表,函數,線程和(完整)用戶數據值)是引用。這些引用是按值傳遞的。 – Ethan 2015-10-30 01:10:16

18

lua的functiontableuserdatathread(協程)類型通過引用傳遞。其他類型通過值傳遞。或者像有些人喜歡說的那樣;所有類型都按值傳遞,但function,table,userdatathread是引用類型。

string也是一種引用類型,但是它是不可變的,被執行的並且在寫時複製 - 它的行爲類似於值類型,但性能更好。

這裏發生的事情:

local a = 9 
local t = {4,6} 

function moda(a) 
    a = 10 -- sets 'a', which is a local introduced in the parameter list 
end 

function modt(t) 
    t[1] = 7 -- modifies the table referred to by the local 't' introduced in the parameter list 
    t[2] = 8 
end 

或許這將正確看待這個事情,爲什麼事情會是這樣的:

local a = 9 
local t = {4,6} 

function moda() 
    a = 10 -- modifies the upvalue 'a' 
end 

function modt() 
    t[1] = 7 -- modifies the table referred to by the upvalue 't' 
    t[2] = 8 
end 

-- 'moda' and 'modt' are closures already containing 'a' and 't', 
-- so we don't have to pass any parameters to modify those variables 
moda() 
modt() 
print(a) -- now print 10 
print(t[1]..t[2]) -- still print 78 
7

我不會重複已經在巴斯Bossink和jA_cOp的關於引用類型的回答說,但是:

- 因爲它定義的地方,不應該有FUNC範圍

這是不正確的。 Lua中的變量爲lexically scoped,這意味着它們是在一段代碼及其所有嵌套塊中定義的。
local所做的是創建一個新變量,該變量僅限於語句所在的塊,塊可以是函數的主體,「縮進級別」或文件。

這意味着只要您對變量進行引用,Lua就會「向上掃描」,直到找到一個代碼塊,其中該變量被聲明爲本地代碼塊,如果沒有此類聲明,則默認爲全局代碼塊。

在這種情況下,at被聲明的局部,但聲明在全球範圍內,所以at是全球性的;或者至多,它們是當前文件的本地文件。

他們然後不被重新聲明local裏面的功能,他們聲明爲參數,它具有相同的效果。如果它們不是函數參數,函數體內的任何引用仍然會引用外部變量。

lua-users.org上有一個Scope Tutorial,其中一些例子可能比我的解釋更有幫助。 Programming in Lua's section on the subject也是一個很好的閱讀。

1

這是否意味着表變量通過引用傳遞給函數而不是值?

是的。

全局變量創建如何與已定義的局部變量衝突?

不是。這可能會出現這種情況,因爲你有一個叫做t的全局變量,並將它傳遞給一個名爲t的參數,但這兩個t是不同的。如果您將參數重命名爲其他東西,e,g,q,則輸出將完全相同。 modt(t)只能因爲通過引用傳遞它而能夠修改全局變量t。例如,如果您撥打modt({}),全球t將不受影響。

爲什麼模式能夠修改表格但moda無法修改變量?

因爲參數是本地的。命名參數a類似於聲明一個局部變量local a,除非顯然該參數接收傳入的值,而常規的局部變量則不會。如果您的論點被稱爲z(或根本不存在),那麼moda確實會修改全球a

16

jA_cOp是正確的,當他說「所有類型都通過值傳遞,但函數,表,用戶數據和線程都是引用類型」。

這和「表格通過引用傳遞」之間的區別很重要。

在這種情況下它沒有什麼區別,

function modt_1(x) 
    x.foo = "bar" 
end 

結果:兩個「按引用傳遞表」和「按值傳遞表,但表是引用類型」將這樣做:X現在有它的foo字段設置爲「bar」。

但這個功能它使差異

function modt_2(x) 
    x = {} 
end 

的世界。在這種情況下,通過引用傳遞將導致參數得到改變爲空表。但是,在「按值傳遞,但是它是一個引用類型」的情況下,新表將局部綁定到x,並且參數將保持不變。如果你在lua中試試這個,你會發現它是第二個(值是引用)。

+0

這真的很有幫助,謝謝 – 2014-12-14 17:07:54

+0

我發現一個很好的方式來思考這一切,只是一切都是按價值傳遞的。然而,恰巧有些類型只是參考。引用本身是通過值傳遞的,這就是爲什麼你的例子不會改變't'。很好的解釋 :) – Ethan 2015-10-30 01:09:18