_G.print("hello lua, version is", _VERSION)
table面向?qū)ο笳Z法糖
lua對table中的函數(shù)調(diào)用做了優(yōu)化押蚤,使用起來像類方法机打,增加了個特殊變量self
褥紫。
local t = {x=1,}
t.Add = function(self)
self.x = self.x + 1
end
t.Add(t)
print(t.x) -- 2
local t = {x=1,}
function t:Add()
self.x = self.x + 1
end
t:Add()
print(t.x) -- 2
兩個例子效果一樣寸潦。第二個算是語法糖,隱含定義了第一個參數(shù)變量self
答憔,方便使用价涝。
閉包 closure
lua里的閉包就是函數(shù)炮赦。特殊之處,在于閉包綁定了一些局部變量向叉,lua實現(xiàn)里稱為upvalue
锥腻。
function get_closure()
-- 這個局部變量被下面的函數(shù)保存了
local x = 1
local add = function() x = x + 1 end
local get = function() return x end
-- add和get使用的是同一個x
return get,add
end
local get,add = get_closure()
print(get()) -- 1
add()
print(get()) -- 2
閉包綁定局部變量時是引用綁定,多個閉包可以共享同一個局部變量母谎。
元表 metatable
lua提供了元表機制瘦黑,可用于擴展lua的功能,如:模擬類奇唤,模擬原型幸斥。
理解上就是用一個table描述另一個table的一些特定情況如何處理。
t = {}
print(t.x) -- nil
mt = {
-- 當(dāng)table不存在某個key值時咬扇,調(diào)用這個函數(shù)
__index = function(_,key)
return key .. " not find"
end,
}
setmetatable(t,mt)
print(t.x) -- x not find
setmetatable(t,{
-- 當(dāng)t中不存在某個key值甲葬,嘗試從這個表中獲取
__index = {x = "x save in metatable"}
})
print(t.x) -- x save in metatable
上面的例子演示了最常見的元表項__index
,還有其他元表項懈贺。
元表key | 描述 |
---|---|
__index | 讀取table[key] 经窖,值為nil,會檢查這個元表項梭灿。是function画侣,就獲取函數(shù)返回值。不是堡妒,就遞歸讀取配乱。 |
__newindex | 寫入table[key] = value ,原值為nil涕蚤,檢查這個元表項宪卿。是function,調(diào)用之万栅。不是佑钾,遞歸寫入。 |
__gc | 垃圾回收時調(diào)用烦粒,常用于userdata類型休溶,相當(dāng)于析構(gòu)函數(shù) |
__call | 使用obj() 時調(diào)用代赁,如果obj不是函數(shù)的話 |
__mode | 值是個字符串。用于定義弱引用兽掰,弱引用不能阻止垃圾回收芭碍。 里面有 k 俊戳,則key是弱引用姐浮;里面有v ,則value是弱引用 |
__concat | 自定義.. 運算符 |
__eq | 自定義== 運算符 |
__add | 自定義+ 運算符 |
__len | 自定義# 運算符 |
__lt | 自定義< 運算符 |
... | 還有些其他的操作 |
注意:
- 在lua里setmetatable只能修改table類型的元表垫言,可以在c里修改其他類型數(shù)據(jù)的元表
- 在lua中存在元表保護模式杉女,如果原來的元表有
__metatable
字段瞻讽,那該元表不能被替換。
協(xié)程 coroutine
lua實現(xiàn)了協(xié)程熏挎。
協(xié)程有些像線程速勇,函數(shù)調(diào)用可以被中斷,然后再從中斷的地方繼續(xù)執(zhí)行坎拐。
協(xié)程與線程不同的地方在于烦磁,同一時刻只有一個協(xié)程處于運行狀態(tài),沒有互斥鎖的使用哼勇。
經(jīng)典的生產(chǎn)者消費者的例子:
producer = coroutine.create(function()
for x = 1,2 do
print("生產(chǎn) --> 第" .. x .. "個產(chǎn)品")
coroutine.yield(x)
end
print("生產(chǎn)者結(jié)束生產(chǎn)")
end)
function consumer()
while true do
local _,x = coroutine.resume(producer)
if x then
print("消費 <-- 第" .. x .. "個產(chǎn)品")
else
print("消費者離場")
break
end
end
end
consumer()
輸出的結(jié)果:
生產(chǎn) --> 第1個產(chǎn)品
消費 <-- 第1個產(chǎn)品
生產(chǎn) --> 第2個產(chǎn)品
消費 <-- 第2個產(chǎn)品
生產(chǎn)者結(jié)束生產(chǎn)
消費者離場
模擬類·綜合應(yīng)用
使用table和閉包模擬類封裝都伪。
function New(name)
local obj = {}
local _name = name or "default name"
obj.PrintName = function ()
print(_name)
end
obj.SetName = function (name)
_name = name
end
return obj
end
local obj = New()
obj.PrintName() -- default name
obj.SetName("new name")
obj.PrintName() -- new name
使用元表模擬類,可以實現(xiàn)繼承猴蹂。下面的例子實現(xiàn)的簡單的單繼承院溺。
-- 單繼承框架
function class(class_name,base_class)
local cls = {__name = class_name}
cls.__index = cls
function cls:new(...)
local obj = setmetatable({}, cls)
cls.ctor(obj,...)
return obj
end
if base_class then
cls.super = base_class
setmetatable(cls,{__index = base_class})
end
return cls
end
-- 使用
BaseClass = class("base")
function BaseClass:ctor()
end
function BaseClass:Say()
print(self.__name .. " say")
end
DerivedClass = class("derived",BaseClass)
local base = BaseClass.new()
local derived = DerivedClass.new()
base:Say() -- base say
derived:Say() -- derived say
c擴展
lua和c交互時使用棧來傳遞參數(shù)。簡單例子演示
static int abs(lua_State *L) // [-0, +1, -]
{
double n = luaL_checknumber(L, 1);
n = (n > 0 ? n : -n);
lua_pushinteger(L, n);
return 1;// 返回值個數(shù):1個
}
static const luaL_Reg libs[] = {
{"abs",abs},
{NULL,NULL}
};
LUALIB_API int luaopen_test(lua_State *L) {
luaL_register(L, "test", libs);// test 是模塊名
return 1;
}
lua使用 print(test.abs(-1))
磅轻,棧的變化
注釋
[-0, +1, -]
簡述了棧的變化珍逸,出棧0個,入棧1個聋溜。-
表示不拋異常谆膳。
lua提供很多操作棧的API用于擴展,這兒講的簡單了撮躁,擴展閱讀漱病。
其他
module模塊【5.1版本有用,之后就廢棄了】
lua提供了module來實現(xiàn)模塊管理把曼。
local print = print -- 注意這行
module("hello")
msg = "hello world"
function printHelloWorld()
print(msg)
end
這兒的msg和printHelloWorld定義時沒有使用local
杨帽,但是他們不是簡單的全局變量。
他們是模塊的全局變量嗤军,外部使用hello.printHelloWorld()
注盈。
例子中第一行的local print=print
,在這兒是一定要加的叙赚,不然module里不是使用print
老客。
有個簡單的方法module("hello",package.seeall)
僚饭,這樣module里可以訪問所有的全局變量。