類型和變量
Booleans
兩個取值 false 和 true。但要注意 Lua 中所有的值都可以作為條件。在控制結構的條
件中除了 false 和 nil 為假,其他值都為真。是以 Lua 認為 0 和空串都是真。
Numbers
表示實數,Lua 中沒有整數。一般有個錯誤的看法 CPU 運算浮點數比整數慢。事實
不是如此,用實數代替整數不會有什麼誤差(除非數字大于 100,000,000,000,000)。Lua
的 numbers 可以處理任何長整數不用擔心誤差。你也可以在編譯 Lua 的時候使用長整型
或者單精度浮點型代替 numbers,在一些平台硬體不支援浮點數的情況下這個特性是非
常有用的,具體的情況請參考 Lua 釋出版所附的詳細說明。和其他語言類似,數字常量
的小數部分和指數部分都是可選的,數字常量的例子:
4 0.4 4.57e-3 0.3e12 5e+20
Strings
指字元的序列。lua 是 8 位位元組,是以字元串可以包含任何數值字元,包括嵌入的 0。
這意味着你可以存儲任意的二進制資料在一個字元串裡。Lua 中字元串是不可以修改的,
你可以建立一個新的變量存放你要的字元串,如下:
a = "one string"
b = string.gsub(a, "one", "another") -- change string parts
print(a)
--> one string
print(b)
--> another string
string 和其他對象一樣,Lua 自動進行記憶體配置設定和釋放,一個 string 可以隻包含一個
字母也可以包含一本書,Lua 可以高效的處理長字元串,1M 的 string 在 Lua 中是很常見
的。可以使用單引号或者雙引号表示字元串
a = “a line”
b = ‘another line’
運作時,Lua 會自動在 string 和 numbers 之間自動進行類型轉換,當一個字元串使
用算術操作符時,string 就會被轉成數字。
print("10" + 1)
--> 11
print("10 + 1")
--> 10 + 1
print("-5.3e - 10" * "2")
--> -1.06e-09
print("hello" + 1)
-- ERROR (cannot convert "hello")
反過來,當 Lua 期望一個 string 而碰到數字時,會将數字轉成 string。
print(10 … 20) --> 1020
…在 Lua 中是字元串連接配接符,當在一個數字後面寫…時,必須加上空格以防止被解釋
錯
盡管字元串和數字可以自動轉換,但兩者是不同的,像 10 == "10"這樣的比較永遠
都是錯的。如果需要顯式将 string 轉成數字可以使用函數 tonumber(),如果 string 不是正
确的數字該函數将傳回 nil。
line = io.read()
-- read a line
n = tonumber(line)
-- try to convert it to a number
if n == nil then
error(line .. " is not a valid number")
else
print(n*2)
end
反之,可以調用 tostring()将數字轉成字元串,這種轉換一直有效:
print(tostring(10) == "10")
--> true
print(10 .. "" == "10")
--> true
想要輸出正确數字後面要接空格,再接…
表達式
關系運算
< > <= >= == ~=
不等~=
邏輯運算符
and or not
邏輯運算符認為 false 和 nil 是假(false),其他為真,0 也是 true.
and 和 or 的運算結果不是 true 和 false,而是和它的兩個操作數相關。
a and b
– 如果 a 為 false,則傳回 a,否則傳回 b
a or b
– 如果 a 為 true,則傳回 a,否則傳回 b
–例如:
print(4 and 5)
--> 5
print(nil and 13)
--> nil
print(false and 13)
--> false
print(4 or 5)
--> 4
print(false or 5)
--> 5
一個很實用的技巧:如果 x 為 false 或者 nil 則給 x 賦初始值 v
x = x or v
C 語言中的三元運算符
a ? b : c
在 Lua 中可以這樣實作:
(a and b) or c
基本文法
指派語句
遇到指派語句 Lua 會先計算右邊所有的值然後再執行指派操作,是以我們可以這樣
進行交換變量的值:
x, y = y, x
– swap ‘x’ for ‘y’
a[i], a[j] = a[j], a[i]
– swap ‘a[i]’ for ‘a[i]’
當變量個數和值的個數不一緻時,Lua 會一直以變量個數為基礎采取以下政策:
a. 變量個數 > 值的個數
按變量個數補足 nil
b. 變量個數 < 值的個數
多餘的值會被忽略
控制結構語句
控制結構的條件表達式結果可以是任何值,Lua 認為 false 和 nil 為假,其他值為真。
if 語句,有三種形式:
if conditions then
then-part
end;
if conditions then
then-part
else
else-part
end;
if conditions then
then-part
elseif conditions then
elseif-part
…
—>多個 elseif
else
else-part
end;
while 語句:
while condition do
statements;
end;
repeat-until 語句
repeat
statements;
until conditions;
for 語句有兩大類:
第一,數值 for 循環:
for var=exp1,exp2,exp3 do
loop-part
end
for 将用 exp3 作為 step 從 exp1(初始值)到 exp2(終止值),執行 loop-part。其中
exp3 可以省略,預設 step=1
如果想颠倒來
for i=10,1,-1 do
print(i)
end
-- print all values of array 'a'
for i,v in ipairs(a) do print(v) end
範型 for 周遊疊代子函數傳回的每一個值。
再看一個周遊表 key 的例子:
– print all keys of table ‘t’
for k in pairs(t) do print(k) end
函數
多傳回值
函數多值傳回的特殊函數 unpack,接受一個數組作為輸入參數,傳回數組的所有元
素。unpack 被用來實作範型調用機制,在 C 語言中可以使用函數指針調用可變的函數,
可以聲明參數可變的函數,但不能兩者同時可變。在 Lua 中如果你想調用可變參數的可
變函數隻需要這樣:
f(unpack(a))
unpack 傳回 a 所有的元素作為 f()的參數
f = string.find
a = {"hello", "ll"}
print(f(unpack(a)))
--> 3 4
string.find 預設情況下傳回兩個值, 即查找到的子串的 起下标标和止下标
預定義的 unpack 函數是用 C 語言實作的,我們也可以用 Lua 來完成:
function unpack(t, i)
i = i or 1
if t[i] then
return t[i], unpack(t, i + 1)
end
end
相當于把表的每一個item都作為參數依次傳入
可變參數
Lua 函數可以接受可變數目的參數,和 C 語言類似在函數參數清單中使用三點(…)
表示函數有可變的參數。Lua 将函數的參數放在一個叫 arg 的表中,除了參數以外,arg
表中還有一個域 n 表示參數的個數。
例如,我們可以重寫 print 函數:
printResult = ""
function print(...)
for i,v in ipairs(arg) do
printResult = printResult .. tostring(v) .. "\t"
end
printResult = printResult .. "\n"
end
如果是傳入…,在函數内使用arg表
function g (a, b, ...) end
g(3) a=3, b=nil, arg={n=0}
g(3, 4) a=3, b=4, arg={n=0}
g(3, 4, 5, 8) a=3, b=4, arg={5, 8; n=2}
域 n 表示參數的個數
舉個具體的例子,如果我們隻想要 string.find 傳回的第二個值。一個典型的方法是
使用啞元(dummy variable,下劃線):
local _, x = string.find(s, p)
– now use `x’
再論函數
a = {p = print}
a.p(“Hello World”)
–> Hello World
print = math.sin –
print' now refers to the sine function a.p(print(1)) --> 0.841470 sin = a.p --
sin’ now refers to the print function
sin(10, 20)
–> 10 20
table.sort
内部是快速排序法實作
table 标準庫提供一個排序函數,接受一個表作為輸入參數并且排序表中的元素。這
個函數必須能夠對不同類型的值(字元串或者數值)按升序或者降序進行排序。Lua 不
是盡可能多地提供參數來滿足這些情況的需要,而是接受一個排序函數作為參數(類似
C++的函數對象),排序函數接受兩個排序元素作為輸入參數,并且傳回兩者的大小關系,
例如:
network = {
{name = "grauna", IP = "210.26.30.34"},
{name = "arraial", IP = "210.26.30.23"},
{name = "lua", IP = "210.26.23.12"},
{name = "derain", IP = "210.26.23.20"},
}
for i,v in ipairs(network) do print(v.name) end
table.sort(network, function (a,b)
return (a.name > b.name)
end)
for i,v in ipairs(network) do print(v.name) end
輸出
grauna
arraial
lua
derain
lua
grauna
derain
arraial
-
table中不能有nil
table.sort是排序函數,它要求要排序的目标table的必須是從1到n連續的,即中間不能有nil。
-
重寫的比較函數,兩個值相等時不能return true
此外,當比較函數沒有寫的時候,table.sort預設按照lua裡面的排序規則升序排序;
當額外寫了比較函數時,相當于用你額外寫的比較函數重載了lua中自帶的“<”操作符。
這就有一個特别要注意的問題,當兩個數相等的時候,比較函數一定要傳回false!
如果兩個值相等都,
排序函數傳回true時則會報錯 invalid order function for sorting
table.sort(tmpQueue, function(a, b)
if (a == nil or b == nil) then
return (a.endTime < b.endTime) --此處千萬不能用小于等于,不然順序錯亂
end)
network = {
{name = "grauna", IP = "210.26.30.34"},
{name = "arraial", IP = "210.26.30.23"},
{name = "lua", IP = "210.26.23.12"},
{name = "grauna", IP = "210.26.23.20"},
}
for i,v in ipairs(network) do print(v.name) end
table.sort(network, function (a,b)
return (a.name >= b.name)
end)
for i,v in ipairs(network) do print(v.name) end
輸出
grauna
arraial
lua
grauna
lua
grauna
arraial
grauna
閉包
函數内部有函數
在匿名函數内部 grades 不是全局變量也不是局部變量,我們稱作外部的局部變
量(external local variable)或者 upvalue。
function newCounter()
local i = 0
return function() -- anonymous function
i = i + 1
return i
end
end
c1 = newCounter()
print(c1()) --> 1
print(c1()) --> 2
c2 = newCounter()
print(c2()) --> 1
print(c1()) --> 3
print(c2()) --> 2
匿名函數使用 upvalue i 儲存他的計數,當我們調用匿名函數的時候 i 已經超出了作
用範圍,因為建立 i 的函數 newCounter 已經傳回了。然而 Lua 用閉包的思想正确處理了
這種情況。簡單的說,閉包是一個函數以及它的 upvalues。如果我們再次調用 newCounter,
将建立一個新的局部變量 i,是以我們得到了一個作用在新的變量 i 上的新閉包。
閉包在完全不同的上下文中也是很有用途的。因為函數被存儲在普通的變量内我們
可以很友善的重定義或者預定義函數。通常當你需要原始函數有一個新的實作時可以重
定義函數。例如你可以重定義 sin 使其接受一個度數而不是弧度作為參數:
do
local oldSin = math.sin
local k = math.pi/180
math.sin = function (x)
return oldSin(x*k)
end
end
利用同樣的特征我們可以建立一個安全的環境(也稱作沙箱,和 java 裡的沙箱一樣),
當我們運作一段不信任的代碼(比如我們運作網絡伺服器上擷取的代碼)時安全的環境
是需要的,比如我們可以使用閉包重定義 io 庫的 open 函數來限制程式打開的檔案。
do
local oldOpen = io.open
io.open = function (filename, mode)
if access_OK(filename, mode) then
return oldOpen(filename, mode)
else
return nil, "access denied"
end
end
end
非全局函數
遞歸函數先聲明
上面這種方式導緻 Lua 編譯時遇到 fact(n-1)并不知道他是局部函數 fact,Lua 會去查
找是否有這樣的全局函數 fact。為了解決這個問題我們必須在定義函數以前先聲明:
local fact
fact = function (n)
if n == 0 then
return 1
else
return n*fact(n-1)
end
end
疊代器與泛型for
疊代器與閉包
疊代器是一種支援指針類型的結構,它可以周遊集合的每一個元素
舉一個簡單的例子,我們為一個 list 寫一個簡單的疊代器,與 ipairs()不同的是我們
實作的這個疊代器傳回元素的值而不是索引下标:
function list_iter (t)
local i = 0
local n = table.getn(t)
return function ()
i = i + 1
if i <= n then return t[i] end
end
end
t = {10, 20, 30}
for element in list_iter(t) do
print(element)
end
可以自己寫些周遊的條件,例如取table裡滿足條件的item
範性for
ipairs與pairs差別
ipairs 僅僅周遊值,按照索引升序周遊,索引中斷停止周遊。即不能傳回 nil,隻能傳回數字 0,如果遇到 nil 則退出。它隻能周遊到集合中出現的第一個不是整數的 key。必須是連續的,從1開始,隻要中間為nil,即斷開
pairs 能周遊集合的所有元素。即 pairs 可以周遊集合中所有的 key,并且除了疊代器本身以及周遊表本身還可以傳回 nil。
local tab= { [1] = "a", [3] = "b", [4] = "c" } for i,v in pairs(tab) do -- 輸出 "a" ,"b", "c" ,
print( tab[i] ) end for i,v in ipairs(tab) do -- 輸出 "a" ,k=2時斷開 print( tab[i] ) end
編譯,運作,錯誤資訊
assert斷言
local f = assert(loadstring("return " … l))
require 函數
Lua 提供進階的 require 函數來加載運作庫。粗略的說 require 和 dofile 完成同樣的功
能但有兩點不同:
- require 會搜尋目錄加載檔案
-
require 會判斷是否檔案已經加載避免重複加載同一檔案。由于上述特征,require
在 Lua 中是加載庫的更好的函數。
require 的另一個功能是避免重複加載同一個檔案兩次。Lua 保留一張所有已經加載
的檔案的清單(使用 table 儲存)。如果一個加載的檔案在表中存在 require 簡單的傳回;
表中保留加載的檔案的虛名,而不是實檔案名。是以如果你使用不同的虛檔案名 require
同一個檔案兩次,将會加載兩次該檔案。比如 require "foo"和 require “foo.lua”,路徑為
"?;?.lua"将會加載 foo.lua 兩次。
C Package
local path = “/usr/local/lua/lib/libluasocket.so”
– or path = “C:\windows\luasocket.dll”
local f = assert(loadlib(path, “luaopen_socket”))
f() – actually open the library
異常與錯誤處理
lua實作try catch
當我們的Lua程式遇到有需要保護的代碼或者方法時(即使程式異常,也隻是抛出異常資訊,而不是讓程式崩潰),Lua為我們提供了兩種解決的辦法,這兩種方法可以讓我們捕獲異常,是以封裝自己的tryCatch函數。
1.pcall調用
2.xpcall調用
相同點:
當程式正常時,傳回true,被執行函數的傳回值
不同點:
1.參數不同
pcall(fun) ,參數隻有一個被調用函數
xpcall(fun,errHandleFun),參數是被調用函數,錯誤函數處理
2.執行結果
pcall:傳回錯誤資訊時,已經釋放了儲存錯誤發生情況的棧資訊。
xpcall:會在棧資訊釋放之前調用錯誤處理程式(可以使用debug庫收集錯誤資訊)
3.傳回結果
pcall 傳回 nil , 錯誤資訊
xpcall傳回nil , 無錯誤資訊
local fun=function ( b)
local a=1;
print(a+b);
return a+b;
end
tryCatch=function(fun)
local ret,errMessage=pcall(fun);
print("ret:" .. (ret and "true" or "false" ) .. " \nerrMessage:" .. (errMessage or "null"));
end
xTryCatchGetErrorInfo=function()
print(debug.traceback());
end
xTryCatch=function(fun)
local ret,errMessage=xpcall(fun,xTryCatchGetErrorInfo);
print("ret:" .. (ret and "true" or "false" ) .. " \nerrMessage:" .. (errMessage or "null"));
end
print("\n------A------\n")
tryCatch(fun("1"));
print("\n------B------\n")
xTryCatch(fun("1"));
print("\n------C------\n")
輸出
------A------
2
ret:false
errMessage:attempt to call a number value
------B------
2
stack traceback:
PInLua.lua:13: in function PInLua.lua:12
[C]: in function ‘xpcall’
PInLua.lua:16: in function ‘xTryCatch’
PInLua.lua:25: in main chunk
[C]: ?
ret:false
errMessage:null
------C------
協同程式
local func = function(a, b)
for i= 1, 3 do
print(i, a, b)
end
end
local func1 = function(a, b)
for i = 1, 3 do
print(i, a, b)
coroutine.yield()
end
end
co = coroutine.create(func)
coroutine.resume(co, 1, 2)
--此時會輸出 1 ,1, 2/ 2,1,2/ 3, 1,2
co1 = coroutine.create(func1)
coroutine.resume(co1, 1, 2)
--此時會輸出 1, 1,2 然後挂起
coroutine.resume(co1, 3, 4)
--此時将上次挂起的協程恢複執行一次,輸出: 2, 1, 2 是以新傳入的參數3,4是無效的
coroutine.resume(co1, 3, 4)
coroutine.resume(co1, 3, 4)
coroutine.resume(co1, 3, 4)
coroutine.resume(co1, 3, 4)