函數(shù)
24、函數(shù)
格式:
function fun_name(params)
--body
end
調用方式:o.fun(x) 或 o:fun(x),其中冒號調用方式會隱含將o作為函數(shù)第一個參數(shù)傳入;
參數(shù):形參和實參數(shù)量可以不同,規(guī)則和多重賦值一致;
多重返回值:只需在return時這樣寫 -- return a, b, c
print可以接受不同數(shù)量的實參,多重返回值的函數(shù)可以直接作為其它函數(shù)的參數(shù),也可以作為table的構造式 --> a = {f()};
25、table.unpack:返回數(shù)組中所有值
print(table.unpack({10, 20, 30}))
local a = {10, 20, 30}
print(table.unpack(a))
local a = {}
a[1] = 10;
a[2] = 20;
a[3] = 30;
print(table.unpack(a))
26、變長參數(shù):lua的函數(shù)可以接受不同數(shù)量的實參
function add(...)
-- body
local s = 0
for i,v in ipairs{...} do --注意這里是大括號
s = s + v
end
return s
end
print(add(3,4, 10, 25, 12)) --54
參數(shù)表中三個點(...)表示該函數(shù)可接受不同數(shù)量的實參。
表達式(...)的行為類似一個具有多重返回值的函數(shù)。
把第一個和第二個參數(shù)賦給a和b :local a, b = ...
可以像訪問數(shù)組一樣去訪問可變參數(shù)。
select(p1, p2):p1為數(shù)字n,那么select返回它第n個可變實參;p1為‘#’select返回變長參數(shù)的總數(shù)。
function fun( ... )
-- body
local len = select('#', ...) -- 獲取長度
local params = {...}
for i=1,len do
print(params[i])
end
end
fun(3, 4, 10, 25, 12)
27、具名實參:傳遞table
28、深入理解函數(shù)
在lua中,函數(shù)是一種第一類值,就是和其它傳統(tǒng)類型一樣可以存儲到變量或table中,可以作為實參傳遞給其它函數(shù),還可以作為其它函數(shù)的返回值。
函數(shù)與所有其它值一樣都是“匿名”的:函數(shù)定義有一下兩種方法,表明fun只是一個持有函數(shù)的變量,而不是函數(shù)的名稱。
function fun( ... )
-- body
end
fun = function( ... )
-- body
print('fun')
end
使用匿名函數(shù):
local tb = {
{name = "grauna", level = "50"},
{name = "lua", level = "10"},
{name = "arraial", level = "30"}
}
table.sort( tb,
function(a, b) --匿名函數(shù)
return (a.name < b.name)
end)
print(tb[1].name)
print(tb[2].name)
print(tb[3].name)
table.sort用于對table中元素排序
深入理解函數(shù)
29、閉合函數(shù):在函數(shù)內部定義的函數(shù),能夠通過建立一個閉合函數(shù)(closure),在外部函數(shù)已經執(zhí)行完畢的情況下,還能訪問到在外部函數(shù)中的局部變量。
后來發(fā)現(xiàn)一個規(guī)律:所有定義在函數(shù)內部的函數(shù),都不能直接在外部函數(shù)外部訪問,只能通過外部函數(shù)把內部函數(shù)返回出來,這樣在外部函數(shù)的外部才可以使用,這也就是閉包函數(shù)通俗的理解吧。例如下面代碼中的 c1、c2
function fun()
-- body
local i = 1
print(i);
return function() --返回了閉包函數(shù)
-- body
i = i+1
return i
end
end
c1 = fun() --創(chuàng)建閉合函數(shù) c1
c2 = fun() --創(chuàng)建閉合函數(shù) c2 注意:這里進行的操作其實是執(zhí)行了fun函數(shù),但并沒有執(zhí)行匿名函數(shù),而是把匿名函數(shù)賦值給c1、c2了,所以c1()、c2()其實執(zhí)行里面匿名函數(shù)
print(c1()) --也可以print(fun()())這樣調用,print(fun())打印是fun函數(shù)里面的匿名函數(shù)的地址
print(c1())
print(c1())
--輸出 1 2 3 4
在fun函數(shù)中匿名函數(shù),仍能訪問到fun作用域下的局部變量,i 對于匿名函數(shù)來講被稱作“非局部變量”。其中c1和c2是同一個函數(shù)創(chuàng)建的兩個不同的closure,它們各自擁有局部變量的獨立實例。
30、非全局函數(shù):函數(shù)可以存儲在全局變量、局部變量、table字段中。只要將函數(shù)存到一個局部變量中,該函數(shù)只能在某個特定的作用域使用。
定義:
local a = function()
-- body
end
local function a()
-- body
end
31、尾調用消除
尾調用:當 f 調用完 g 之后就再無其他事情可做了,這種情況下,當 g 返回時,執(zhí)行控制權直接返回到調用 f 的那個點上。
消除:在尾調用之后,程序不需要保存任何關于該函數(shù)的棧信息了,使得在進行尾調用時不耗費任何棧空間。
function f()
-- body
print("f")
g() -- g()是函數(shù)f的最后調用
end
編譯、執(zhí)行、錯誤
32、編譯:區(qū)分解釋型語言的主要特征不是能否去編譯它們,而是在編譯器是否是運行時庫的一部分,是否有能力執(zhí)行動態(tài)生成的代碼。
dofile:實際是調用loadfile,從文件加載lua代碼,編譯并運行,會引發(fā)錯誤
loadfile:從文件加載lua代碼,編譯但不運行(編譯成函數(shù),作為匿名函數(shù)返回),不會引發(fā)錯誤,有錯誤時只是返回錯誤值
源代碼 foo.lua
function foo(x)
-- body
print(x)
end
運行:
f = loadfile("foo.lua") --只是返回了函數(shù),但沒有執(zhí)行
print(foo) --nil
f() --定義foo函數(shù)
foo("ok") --ok
load(低版本中是loadstring):從一個字符串讀取代碼,返回一個函數(shù)。(比直接定義函數(shù)寫代碼慢得多)
i = 32
local i = 0
f = load("i = i+1;print(i)") --load總是在全局環(huán)境中編譯的,所以這里訪問到的i是全局變量i
g = function() i = i+1;print(i) end
f()
g()
--輸出 33 1
33、C代碼:需要使用前先鏈接入應用程序
loadlib函數(shù)加載指定的庫,并將其鏈接入Lua:將C函數(shù)作為Lua函數(shù)返回
local path = "/usr/local/lib/lua/5.1/socket.so"
local f = package.loadlib(path, "luaopen_socket")
34、錯誤(error):只要發(fā)生錯誤,Lua就會結束當前程序塊并返回應用程序
error
if not n then
error("invalid input") --顯示拋出錯誤信息
end
assert(p1, p2):如果p1為true,就簡單返回該參數(shù),否者引發(fā)一個錯誤。p2是錯誤信息,可選
35、錯誤處理和異常:pcall(protected call)來包裝需要執(zhí)行的代碼,捕獲函數(shù)執(zhí)行中任何錯誤。
function fun( ... )
-- body
print("fun")
error("exception")
end
local status, err = pcall(fun)
if status then
--運行fun時沒有發(fā)生錯誤
print("not have error")
else
--錯誤處理代碼
print("have error")
print(err) --只要錯誤消息是字符串,Lua就會附加一些錯誤發(fā)生位置的信息
end