天天看點

lua語言:類型,基本文法,函數

類型和變量

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

  1. table中不能有nil

    table.sort是排序函數,它要求要排序的目标table的必須是從1到n連續的,即中間不能有nil。

  2. 重寫的比較函數,兩個值相等時不能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 完成同樣的功

能但有兩點不同:

  1. require 會搜尋目錄加載檔案
  2. 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)