簡述
Lua是一個很小的編程語言,很多人將其與Python高級語言進行比較。
Lua有以下的特點:
(1)純C語言實現,源碼小,可以很好地與C/C++融合。可自行編譯,生成靜態庫。
(2)語法簡單,靈活,易學。
我也同時學習了Python,相比之下,Lua精簡,功能簡單,可用的庫少,但是語言的實現寫得如此精簡也很不錯了!與C/C++程序結合就能顯示它的強大能力,適用於要求可配置性很高的C/C++程序中,比如遊戲。
如果想寫純腳本程序,那就選Python吧,它是強大,庫多,喜歡單幹。
好,我們學習Lua吧!
基礎
數據類型
Lua的常用數據類型有:
(1)Nil (空) <==> C語言中的NULL
(2)number 所有數據,包含整數小數
(3)string 字串類型
(4)boolean 布爾類型 true, false
(5)function 函數類型
(6)table 表類型
其實,Lua中的數據類型遠不止上面的這幾個,比如file.
table在Lua中是最重要的數據類型,Lua的很多思想都是基於表來實現的,比如模塊、對象。
boolean 除Nil與false爲假以爲,其它都爲真。
string
\ 轉義字符
\[
\]
\ddd 3個十進制的數據表示一個字符,高位填0
[[ ... ]] 表示多行字符串,其中字串無轉義
a = [[ \t \n [[ ]]
print(a)
\t \n [[
當字符串被參於數值運算時,Lua會試圖將字串轉成數值
print("10"+ 1) --> 11
print("123"* "12")--> 1476
當數值參於字串運算時,Lua也會嘗試將其轉換成字符串
print(123 .. 12)-->12312
print(10 =="10") --> 永遠都是false
-- 除非
print(10 == ("10"+ 0)) --> true
print("10"== (10 .. ""))--> true
總之:操作符決定了兩邊操作數的轉換類型。
表達式
(1)算術運算符 + - * / ^ (加減乘除冪)
(2)關係運算符 < > <= >= == ~= (不等於)
nil只與自己相等,table, usedate, function, 只比對象不比內容的值,相當於C語言中的指針比較
(3)邏輯運算符 and or not
and, or 的運算結果不是true與false
(4)優先級
^ 與 .. 是右連接
表結構
days = {"sun","Mon", "Tue","Wed", "Thu","Fri", "Sat"}
print(days[0])--> nil
print(days[1])--> Sun
Polyline = { color = "blue", thickness = 2, npoints = 4,
{x = 0, y = 0},
{x = -10, y = 0},
{x = -10, y = 10},
{x = 0, y = 10}
}
Polyline["color"]--> "blue"
Polyline.color --> "blue"
Polyline[1] --> {x = 0, y = 0}
Polyline[1].x --> 0
Person = {["name"] ="Peter", ["age"] = 28}-- 等價於
Person = {name = "Peter", age = 28}
{x=0, y=0} --> {["x"]=0, ["y"]=0}
{"red","green", "blue"}--> {[1]="red", [2]="green", [3]="blue"}
基本語法
(1)多元素賦值
a, b = 10, 'Hi'--> 等價於
a = 10; b = 'Hi'
(2)代碼塊chunk。
使用local創建的一個局部變量,可以避免命名衝突,而比全局變量高效。
(3)控制結構
<pre name="code" class="cpp">----------------------------------
if condition1 then
...
else if condition2 then
...
else
...
end
----------------------------------
while condition do
...
end
----------------------------------
repeat
...
until condition
----------------------------------
for var=exp1, exp2, exp3 do
...
end
---> exp1: 初始值
---> exp2: 終止值
---> exp3: 步進
for k in pairs(days) do
print(k .. ' = ' .. days[key])
end
for i, val in ipairs(days) do
print(i .. ' = ' .. val)
end
(4)函數
定義格式:
function func_name (arguments_list)
...
end
dofile "text.lua"
table_print {a=12, b=30}
函數的返回值可以是多個:
function foo(a, b)
return a+b, a-b
end
aa, bb = foo(32, 11)
print(aa, bb) --> 43, 21
(5)閉包
Python與Lua都有閉包的特性,我記得好像Java也有,但是C與C++是沒有的。
爲了便於理解,我寫一段C++的僞代碼:
class CounterFunc
{
public:
CounterFunc(int i*)
{
<span style="white-space:pre"> </span>m_pCnt = i;
}
int operator()()
{
<span style="white-space:pre"> </span>++i;
<span style="white-space:pre"> </span>return i;
}
private:
int *m_pCnt;
}
CounterFunc* newCounter()
{
int *i = new int;
return new CounterFunc(i);
}
int main()
{
CounterFunc &c1 = *newCounter()
cout << c1() << endl;
cout << c1() << endl;
}
(6)非全局函數
表中的函數實現方式:
Lib = {}
Lib.foo = function(x, y) return x+y end
----------------------------------------------
Lib = {
foo = function(x, y) return x+y end
}
----------------------------------------------
Lib = {}
function Lib.foo(x, y) return x+y end
local foo = function(x, y)
return x+y
end
------------------------------------------------
local function foo (x, y)
return x+y
end
(7)尾調函數
當函數的最後返回結果是調用另一個函數,稱之爲尾調函數。Lua的在調用尾調函數時,先是彈出當前函數的棧空間,然後再調用尾調函數,從而降低了函數層層調動過程中的棧消耗,非常適用於函數遞歸調用。
加載文件與運行
當我們需要加載與運行已有文件中的Lua代碼時,可以用以下幾種方式:
(1)loadstring( str ) --- 加載字串
f = loadstring "a = 12; print('a=' .. a)"
f() --> a=12
f = loadstring "function() a = 12; print('a='..a) end"
(2)loadfile( file_name ) --- 加載文件
這個函數相當於從文件裏讀出string,然後再調用loadstring(file_text)實現加載功能。
(3)dofile( file_name ) --- 加載並執行文件
相當於loadfile()之後,返回一個函數,再調用這個函數。
function dofile( file_name )
local f = assert(loadfile(file_name))
f()
end
(4)require( file_name )
這個函數是通過調用dofile()來實現的。不同的是,每次加載執行一個文件時,require()都會記錄,避免重複加載。另外,如果給定的路徑找不到文件,require()會到指定的路徑下去找輸到加載的文件。文件名稱可以省去.lua後綴。
協同函數
關於這個功能,我的理解是:是非搶佔式任務,由任務主動放棄執行權來達到任務切換的目的。
function foo (x, y)
while ture do
<span style="white-space:pre"> </span>local a = x + y
<span style="white-space:pre"> </span>local b = x - y
<span style="white-space:pre"> </span>x, y = coroutine.yield(a, b)
end
end
------------------------------------------
co = coroutine.create(foo)
print(coroutine.resume(co, 1, 2)) --> true 3 -1
print(coroutine.resume(co, 7, 1)) --> true 8 6
(1)co = coroutine.create(foo),創建一個協同任務,返回co,此時co的狀態爲suspend。
(2)coroutine.resume(co, 1, 2),程序轉而執行foo(x, y),x,y的傳入的值正是resume()中的(1, 2)。
(3)foo(1, 2)執行到coroutine.yield(a, b),(a, b)分別爲(3,-1)。程序執行yield(3, -1)切換到主任務,resume()函數返回第一個值爲bool,後面的正是yield()中傳入的參數。
(4)coroutine.resume(co, 7, 1),程序跳到foo()中的coroutine.yield()行繼續執行。其中resume()函數中帶的參數(7, 1),在foo()函數中,x, y = coroutine.yield() 通過返回值的形式賦給了(x, y)。
(5)跳到(3),如此重複。
可見,Lua並沒有實現多任務並行執行,而是任務之間通過resume()與yield()函數進行切換。我想,其主要的作用還是用於將功能以任務的形式進行隔離,便於維護。
Metatables and Metamethods
元表與元方法,這是一個很重要的概念。可以爲table設置或指定metatable,用於指定某些運算符對應的操作方法。
* setmetatable(table, meta_table) 設置元表
* getmetatable(table) 獲取元表
這個元素,可以是個函數。
tb = {
----> <metatable> = {
__add = function : xxx 加法操作
__sub = function : xxx 減法操作
__mul = function : xxx 乘法操作
...
}
[1] = 20
[2] = 40
...
}
(1)算術運算
+ --> __add(a, b)
- --> __sub(a, b)
* --> __mul(a, b)
/ --> __div(a, b)
-負 --> __unm(a)
^ --> __pow(a, b)
(2)關係運算
== --> __eq(a, b)
< --> __lt(a, b)
<= --> __le(a, b)
(3)庫定義
__tostring = function(tb)
(4)表相關
__index = function(tb, key)
__index = tb
__newindex = function(tb, key)
這兩個東西,在類繼承裏用到。
環境變量
Lua環境放在全局變量 _G 中。可以用以下語句打印全局變量。
for n in pairs(_G) do print(n) end
</pre></div></div></div><div id="highlighter_474421" class="syntaxhighlighter lua"><div class="bar"><div class="toolbar"><pre code_snippet_id="358723" snippet_file_name="blog_20140522_27_6186527" name="code" class="cpp">a = 5
print(_G["a"]) --> 5
在非全局域,防止訪問到全局域
do
a = 'Hi'
setfenv(1, {_G = _G})
_G.print(_G.a) --> Hi
end