1. 程式人生 > >lua 學習筆記 二

lua 學習筆記 二

11.0
lua中的習慣一般是以1作為陣列的起始索引,lua庫和長度操作符都遵循這個約定。
對於稀疏矩陣,存在大量的“空洞(nil值)”,遍歷矩陣的效率很低,通常使用pairs且只遍歷非nil元素。訪問順序是無序的。P118
字串緩衝問題可能導致對字串的操作消耗大量的時間,通過使用table配合concat函式來解決。
當字串中包含特殊字元,有可能導致lua程式無效,可以用%q來使用string.format函式來解決。


13.0 - 13.4
通過元表可以修改一個值的行為,使其在面對一個非預定義的操作時執行一個指定的操作。
__add元方法 可以定義相加行為。
使用setmetatable來設定或修改table元表。
任何table都可以作為任何值的元表,而一組相關的table也可以共享一個通用的元表,此元表描述了它們共同的行為。一個table也可以是自己
的元表,用於描述其特有行為。
lua程式碼中,只能設定table的元表,若要設定其他型別的值的元表,則需要通過C程式碼來完成,(防止過度使用某些特定於型別的元表,導致程式碼
無法複用)


算術類:__add,__mul,__sub,__div,__unm(相反數),__mod,__pow(乘冪)。__concat欄位。


Lua查詢元表的步驟:如果第一個值有元表,則用這個欄位的元方法,若第一個值沒有元表,則用第二個值的元表,若都沒有元方法,則lua引發一個錯誤。
關係類:__eq(等於),__lt(小於),__le(小於等於)。其他3個關係操作符沒有單獨的元方法,lua會進行轉換。
關係轉換對“部分有序”(partial order)可能發生錯誤。如對NaN(not a number);
標準規定任何涉及NaN的比較都應返回false,這意味著NaN <= x 永遠為假,但x <= NaN也為假。導致轉換出現不合法。
關係類的元方法不能應用於混合的型別。對不同型別進行順序比較,lua會引發一個錯誤,而等於比較永遠不會引發錯誤,如果兩個物件有不同的元方法,元方法都不呼叫,直接返回false。而是以普通行為處理。只有當兩個比較物件共享一個元方法時,Lua才呼叫這個等於比較的元方法。
要使使用者既不能看也不能修改集合的元表。需要用到欄位__metatable.getmetatable返回該欄位值,而setmetatable引發一個錯誤。P135
__index元方法修改查詢table的行為。
__newindex元方法用於修改table的修改行為
呼叫rawset(t,k,v)就可以不涉及任何元方法而直接設定table t中與key k相關聯的 value v。
跟蹤table訪問
#!/usr/local/bin/lua
do
t = {}
local _t = t
t = {}
local mt = {
__index = function (t,k)
print("access to element" .. tostring(k))
return _t[k]
-- body
end,
__newindex = function (t,k,v)
print("update of element" .. tostring(k) .. "to" .. tostring(v))
_t[k] = v
-- body
end
}
setmetatable(t,mt)


end
-- 存在無法通過pairs遍歷table的問題。(可以重封裝pairs函式)


將原table作為代理table的一個特殊欄位。從而讓同時需要監視的table公用一個元表。
#!/usr/local/bin/lua
do
local _t = {}
local mt = {
__index = function (t,k)
print("access to element" .. tostring(k))
return t[_t][k]
-- body
end,
__newindex = function (t,k,v)
print("update of element" .. tostring(k) .. "to" .. tostring(v))
t[_t][k] = v
-- body
end
}
function track(t)
local proxy = { }
proxy[_t] = t
setmetatable(proxy,mt)
return proxy
-- body
end
t = {"hello","world","heihei","hehe"}
s = {"lua","is","a","simple","language"}
t = track(t)
s = track(s)
end


14.0 - 14.3
lua將其所有的全域性變數儲存在一個常規的table中,這個table稱為環境(environment)。
一,無需再為全域性變數創造新的資料結構,簡化了lua內部實現,二,環境和其他table一樣操作。
lua將環境table自身儲存在一個全域性變數_G中。
可以通過訪問_G中得全域性變數來訪問執行時動態生成的全域性變數值,提高效率
value = _G[varname]
getfield()函式可以獲取巢狀表中指定key的value。P144下劃線寫成了連字元。
過多的全域性變數容易導致命名衝突和訪問錯誤變數名,可以通過修改全域性變數的元表來實現訪問錯誤變數時的行為。
通過rawset可以繞過元表來完成新變數的宣告


可以通過函式setfenv來改變一個函式的環境。函式的引數是一個函式或一個數字(指定棧層)和一個新的環境。(5.3該函式已經取消,改為_ENV,http://blog.codingnow.com/2015/01/lua_52_53.html) 
#!/usr/local/bin/lua
do
a = 1
_ENV = {g = _G}
g.print(a)
g.print(g.type(g))
g.print(g.a)
end


每個函式及某些closure都有一個繼承的環境。


15.0 - 15.5
require函式載入模組。對已經載入的模組,函式不會再次載入,只會返回相應的值。
loadfile載入lua程式碼,loadlib載入C庫;
package.loaded中儲存了載入器的返回值。
在require中,如果一個模組名中包含了連字元,require就會用連字元後的內容來建立luaopen_*函式名。從而方便在測試中對各不同版本的模組進行載入而無需修改程式碼。
packag.seeall 等價於 setmetatable (M,{__index = _G})(繼承方式)


lua支援具有層級性的模組名,可以用一個點來分隔名稱中的層級。
require 子模組a.b並不會自動載入a。


16.0 - 
lua中的table就是一種物件,table與物件一樣可以擁有狀態,其次,table也與物件一樣擁有一個獨立於其值的標識(一個self),最後,table與物件一樣具有獨立於建立者和建立地的生命週期。
冒號的作用是在一個方法定義中新增一個額外的隱藏引數,以及在一個方法呼叫中新增一個額外的實參。
setmetatable(a,(__index = b)), a會在b中查詢所有它沒有的操作。可以將b視為a的基類。


lua中多重繼承的實現,通過將__index欄位設為一個函式,通過函式對多繼承的基類物件進行搜尋。(多重繼承的效能不如單一繼承)
雙table實現私密性


function newAccount (initialBalance)
local self = {balance = initialBalance}
local withdraw = function(v)
self.balance = self.balance - v
end
local desposit = function (v)
self.balance = self.balance + v
end
local getBalance = function() return self.balance end
return {
withdraw =withdraw,
deposit = deposit,
getBalance = getBalance
}
end
當一個物件只有一個方法時,可以不用建立介面table,但需要將這個單獨的方法作為物件表示來返回。即採用單一方法(single - method).採用了closure,雖然無法實現繼承,但擁有完全的私密性。


弱引用table是一種用來告訴Lua一個引用不應該阻礙一個物件的回收的機制。是一種會被垃圾收集器忽視的物件引用。
一個table的弱引用型別通過其元表中的__mode欄位來決定,k對應key,v對應value。完全若引用“kv”
#!/usr/local/bin/lua
do
a = {}
b = {__mode = "k"}
setmetatable(a,b)
key1 = {}
a[key] = 1
key2 = {}
a[key] = 2
print(a[key])
collectgarbage()
for k,v in pairs(a) do print(k,v) end
end
key1 和key2的值是不同的,為二個不同的空table得地址。
在lua中,字串就是值,而非物件,因此,字串和數字布林型別一樣,不會從若引用table中刪除。