lua程序設計(一)
- 2019 年 11 月 8 日
- 筆記
摘要:lua程序設計第二版學習筆記
腳本語言的基礎語法大都比較簡單,這裡只列舉一些lua獨有,或者需要特別注意的語法點。
書中前三章的內容是一些慣常的引言,基礎數據類型,運算符等內容,相對簡單,這裡就不再贅述。
語句
1、do…end 可以用來包含一個程序塊。
2、在循環語句中聲明的局部變量,在條件判斷時依然存在
3、for循環分為數字型和泛型
數字型:
1 for var=exp1, exp2, exp3 do 2 <執行體> 3 end
類似於C中將括號和句號去掉,var初始值為exp1,增長到exp2,步進為exp3。
exp3可選,默認為+1,不設上限可將exp2設置為math.huge。
for的控制變量會被自動聲明為for的局部變量,無需單獨聲明,同時也無法在外部訪問。
泛型for:通過迭代器訪問table
1 for i,v in ipairs(a) do print(v) end -- 打印所有值 2 for k in pairs(t) do print(k) end -- 打印所有key
4、lua中提供多種迭代器,也可自行編寫
迭代文件中的行 io.lines
迭代table元素 pairs
迭代數組元素 ipairs
迭代字符串中單詞 string.gmatch
5、return ,break只能作為代碼塊的最後一條語句,或是end,else,until前的最後一條語句,否則lua語法報錯,可以通過 do return end 顯式包住一條return。
函數
6、lua中函數,若只有一個參數,且該參數為字面字符串或table構造式,函數的()可以省略。
7、lua為面向對象提供特殊語法——冒號操作符,將本身作為第一個值傳入。
8、lua中函數可以返回多個值,同時用多個變量接受,類似於多重賦值,但是若函數調用不是一系列表達式的最後一個值,則只產生一個值。
9、上一條中的現象,在多返回值函數作為另一個函數的非最後一個參數時也有效。
10、通過將函數調用放入一對圓括號中,可以迫使其之返回一個值。
11、unpack() 函數接受一個數組為參數,並且從下標1逐個返回所有參數,常用於泛型調用。
12、聲明函數時,參數為(…),即為變長參數,在函數中 ‘…’ 被當作表達式使用。
13、變長參數和固定參數同時出現在函數參數中時,變長參數需放到最後。
14、當變長參數中含有故意傳入的nil時,需要用select函數訪問,select函數首先必須傳入一個固定實參,如果這個實參為數字n,那麼函數返回第n個變長參數(包括nil),否則變長參數必須為‘#’,返回變長數的總和。
15、具名函數,類似於python中指定函數參數賦值,但是需要把參數名和參數值寫到一個table中,傳入函數。
16、lua中的函數為第一類值,實際上將lua中的函數名理解為一個持有函數的變量更為合適。
1 function foo (x) return 2*x end 等價於 2 foo = function (x) return 2*x end
這使得lua可以輕鬆實現回調模式,例如C++中STL提供的排序函數,需要傳入一個返回值為bool類型的函數指針,用以比較容器變量的大小,lua中可以這樣實現,
例如一個給table排序的函數:
1 network = {............} 2 table.sort(network, function (a, b) return (a.id > b.id) end)
對於這個特性的一個高階應用,求導數:
1 function derivative(f, delta) 2 delta = delta or 1e-4 3 return function(x) 4 return (f(x + delta) - f(x))/delta 5 end 6 end 7 8 c = derivate(math.sin) 9 print(math.cos(10), c(10)) 10 --> -0.83907152907645 -0.83904432662041
深入函數
17、closure(閉合函數),從翻譯上不太好理解這個概念。首先,在一個函數內部定義另一個函數時,那麼內層的函數可以訪問外層函數的變量,這項特徵被稱為“詞法域”。而這個變量相對於內部的函數稱
為“非局部的變量”,一個closure就是一個函數加上該函數所需訪問的所有非局部的變量。
1 function newCount() 2 local i = 0 3 return function() i = i + 1 return i end 4 end 5 6 c1 = newCount() 7 print(c1()) --> 1 8 print(c1()) --> 2 9 c2 = newCount() 10 print(c2()) --> 1 11 print(c1()) --> 3 12 print(c2()) --> 2
c1 和 c2是同一個函數所創建的兩個不同的closure,他們各自擁有局部變量i的實例。
18、closure的另一個用處是創建一個安全的沙盒環境去運行一些不受信任的代碼。
1 do 2 local oldOpen = oi.open 3 local access_OK = function(filename, mode) 4 <檢查訪問權限> 5 end 6 io.open = function(filename, mode) 7 if access_OK(filename, mode) then 8 return oldOpen(filename, mode) 9 else 10 return nil, "access denied" 11 end 12 end 13 end
19、非全局函數,如某個table的成員或用local修飾了聲明的函數,只有在當前塊可以訪問該函數。有一點需要注意,當定義遞歸的局部函數時,在遞歸時由於局部的函數尚未定義完畢,所以其實調用的是
一個同名的全局函數,可以通過先定義一個局部的變量作為函數名來解決這個問題。
20、對於非全局函數,lua中有一種語法糖:
1 local function foo(<參數>) <函數體> end
lua將其展開為:
1 local foo 2 foo = function (<參數>) <函數體> end
這樣定義不會產生遞歸錯誤。當然對於間接遞歸這是無效的,間接遞歸必須一個前向聲明,示例代碼就不貼了,間接遞歸實在是種糟糕的語法,如非必要不要使用。
21、尾調用,lua中正確的尾調用形式如下:
1 return <function>(<args>)
尾調用相當於一條goto語句,因為調用函數已經沒有需要執行的代碼,所以返回值可以直接被被調函數,也就是尾調用的函數返回值覆蓋,此時遞歸的話不會產生棧溢出問題,同時也可以利用lua的這一特
性實現狀態機,用尾調用跳轉到下一個狀態,不會出現任何內存問題。