1. Lua 基礎(chǔ)知識

2018-02-24 16:10 更新

(1) 變量

賦值

賦值是改變一個變量的值和改變表域的最基本的方法。Lua 中的變量沒有類型,只管賦值即可。比如在 Lua 命令行下輸入:

end_of_world = "death"
print(end_of_world)
end_of_world = 2012
print(end_of_world)

上面這四行代碼 Lua 不會報錯,而會輸出:

death
2012

局部變量

使用 local 創(chuàng)建一個局部變量,與全局變量不同,局部變量只在被聲明的那個代碼塊內(nèi)有效

x = 10
local i = 1              -- 局部變量

while i<=x do
    local x = i*2        -- while 中的局部變量
    print(x)             --> 2, 4, 6, 8, ...
    i = i + 1
end

應(yīng)該盡可能的使用局部變量,有兩個好處:

  1. 避免命名沖突
  2. 訪問局部變量的速度比全局變量更快

代碼塊(block)

代碼塊指一個控制結(jié)構(gòu)內(nèi),一個函數(shù)體,或者一個chunk(變量被聲明的那個文件或者文本串)。

我們給block劃定一個明確的界限:do..end內(nèi)的部分。當(dāng)你想更好的控制局部變量的作用范圍的時候這是很有用的。

do
    local a2 = 2*a
    local d = sqrt(b^2 - 4*a*c)
    x1 = (-b + d)/a2
    x2 = (-b - d)/a2
end            -- scope of 'a2' and 'd' ends here
print(x1, x2)

(2) 類型

雖說變量沒有類型,但并不是說數(shù)據(jù)不分類型。Lua 基本數(shù)據(jù)類型共有八個:nil、boolean、number、string、function、userdata、thread、table。

  • Nil Lua中特殊的類型,他只有一個值:nil;一個全局變量沒有被賦值以前默認(rèn)值為nil;給全局變量負(fù)nil可以刪除該變量。
  • Booleans 兩個取值false和true。但要注意Lua中所有的值都可以作為條件。在控制結(jié)構(gòu)的條件中除了false和nil為假,其他值都為真。所以Lua認(rèn)為0和空串都是真。
  • Numbers 即實數(shù),Lua 中的所有數(shù)都用雙精度浮點數(shù)表示。
  • Strings 字符串類型,指字符的序列,Lua中字符串是不可以修改的,你可以創(chuàng)建一個新的變量存放你要的字符串。
  • Table 是很強(qiáng)大的數(shù)據(jù)結(jié)構(gòu),也是 Lua 中唯一的數(shù)據(jù)結(jié)構(gòu)??梢钥醋魇菙?shù)組或者字典。
  • Function 函數(shù)是第一類值(和其他變量相同),意味著函數(shù)可以存儲在變量中,可以作為函數(shù)的參數(shù),也可以作為函數(shù)的返回值。
  • Userdata userdata可以將C數(shù)據(jù)存放在Lua變量中,userdata在Lua中除了賦值和相等比較外沒有預(yù)定義的操作。userdata用來描述應(yīng)用程序或者使用C實現(xiàn)的庫創(chuàng)建的新類型。例如:用標(biāo)準(zhǔn)I/O庫來描述文件。
  • Thread 線程會在其它章節(jié)來介紹。

可以用?type 函數(shù)取得表達(dá)式的數(shù)據(jù)類型:

print(type(undefined_var))
print(type(true))
print(type(3.14))
print(type('Hello World'))
print(type(type))
print(type({}))

(3) 表達(dá)式

操作符

  1. 算術(shù)運算符:+ - * / ^ (加減乘除冪)
  2. 關(guān)系運算符: = == ~=
  3. 邏輯運算符:and or not
  4. 連接運算符:..

有幾個操作符跟C語言不一樣的:

  • a ~= b 即 a 不等于 b
  • a ^ b 即 a 的 b 次方
  • a .. b 將 a 和 b 作為字符串連接

優(yōu)先級:

  1. ^
  2. not -(負(fù)號)
    • /
    • -
  3. ..
  4. = ~= ==
  5. and
  6. or

表的構(gòu)造:

最簡單的構(gòu)造函數(shù)是{},用來創(chuàng)建一個空表??梢灾苯映跏蓟瘮?shù)組:

days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}

Lua將"Sunday"初始化days[1](第一個元素索引為1),不推薦數(shù)組下標(biāo)以0開始,否則很多標(biāo)準(zhǔn)庫不能使用。

在同一個構(gòu)造函數(shù)中可以數(shù)組風(fēng)格和字典風(fēng)格進(jìn)行初始化:

polyline = {color="blue", thickness=2, npoints=4,
              {x=0,   y=0},
              {x=-10, y=0},
              {x=-10, y=1},
              {x=0,   y=1}
}

多重賦值和多返回值

另外 Lua 還支持多重賦值(還支持函數(shù)返回多個值)。也就是說:等號右邊的值依次賦值給等號左邊的變量。比如:

year, month, day = 2011, 3, 12
print(year, month, day)
reutrn year, month, day -- 多返回值
a, b = f()

于是,交換兩個變量值的操作也變得非常簡單:

a, b = b, a

(4) 控制流

if

name = "peach"
if name == "apple" then
    -- body
elseif name == "banana" then
    -- body
else
    -- body
end

for

-- 初始值, 終止值, 步長
for i=1, 10, 2 do
    print i
end

-- 數(shù)組
for k, v in ipairs(table) do
    print(k, v)
end

-- 字典
for k, v in pairs(table) do
    print(k, v)
end

反向表構(gòu)造實例:

revDays = {} 
for i,v in ipairs(days) do
    revDays[v] = i 
end 

while

while i<10 do
    print i
    i = i + 1
end

repeat-until

repeat
    print i
    i = i + 1
until i < 10

break 和 return

break 語句可用來退出當(dāng)前循環(huán)(for, repeat, while),循環(huán)外部不可以使用。

return 用來從函數(shù)返回結(jié)果,當(dāng)一個函數(shù)自然結(jié)束,結(jié)尾會有一個默認(rèn)的return。

Lua語法要求break和return只能出現(xiàn)在block的結(jié)尾一句(也就是說:作為chunk的最后一句,或者在end之前,或者else前,或者until前):

local i = 1
while a[i] do
    if a[i] == v then break end
    i = i + 1
end

(5) C/C++ 中的 Lua

首先是最簡單的 Lua 為 C/C++ 程序變量賦值,類似史前的 INI 配置文件。

width = 640
height = 480

這樣的賦值即設(shè)置全局變量,本質(zhì)上就是在全局表中添加字段。

在 C/C++ 中,Lua 其實并不是直接去改變變量的值,而是宿主程序通過「讀取腳本中設(shè)置的全局變量到棧、類型檢查、從棧上取值」幾步去主動查詢。

int w, h;
if (luaL_loadfile(L, fname) || // 讀取文件,將內(nèi)容作為一個函數(shù)壓棧
    lua_pcall(L, 0, 0, 0))     // 執(zhí)行棧頂函數(shù),0個參數(shù)、0個返回值、無出錯處理函數(shù)(出錯時直接把錯誤信息壓棧)
    error();

lua_getglobal(L, "width");     // 將全局變量 width 壓棧
lua_getglobal(L, "height");    // 將全局變量 height 壓棧
if (!lua_isnumber(L, -2))      // 自頂向下第二個元素是否為數(shù)字
    error();
if (!lua_isnumber(L, -1))      // 自頂向下第一個元素是否為數(shù)字
    error();
w = lua_tointeger(L, -2);      // 自頂向下第二個元素轉(zhuǎn)為整型返回
h = lua_tointeger(L, -1);      // 自頂向下第一個元素轉(zhuǎn)為整型返回

讀取表的字段的操作也是類似,只不過細(xì)節(jié)上比較麻煩,有點讓我想起在匯編里調(diào)戲各種寄存器:

score = { chinese=80, english=85 }

int chinese, english;
if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0, 0))
    error();

lua_getglobal(L, "score");       // 全局變量 score 壓棧

lua_pushstring(L, "chinese");    // 字符串 math 壓棧
lua_gettable(L, -2);             // 以自頂向下第二個元素為表、第一個元素為索引取值,彈棧,將該值壓棧
if (!lua_isnumber(L, -1))        // 棧頂元素是否為數(shù)字
    error();
chinese = lua_tointeger(L, -2);
lua_pop(L, 1);                   // 彈出一個元素 (此時棧頂為 score 變量)

lua_getfield(L, -1, "english");  // Lua5.1開始提供該函數(shù)簡化七八兩行
if (!lua_isnumber(L, -1))
    error();
english = lua_tointeger(L, -2);
lua_pop(L, 1);                   // 如果就此結(jié)束,這一行彈不彈都無所謂了

前面說過,設(shè)置全局變量本質(zhì)就是在全局表中添加字段,所以 lua_getglobal 函數(shù)本質(zhì)是從全局表中讀取字段。沒錯,lua_getglobal 本身就是一個宏:

#define lua_getglobal(L,s)  lua_getfield(L, LUA_GLOBALSINDEX, s)

宏 LUA_GLOBALSINDEX 指明的就是全局表的索引。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號