第六日筆記
1. 基礎概念
程式塊
定義
- 在 lua 中任何一個源代碼檔案或在互動模式中輸入的一行代碼
- 程式塊可以是任意大小的
- 程式塊可以是一連串陳述句或一條命令
- 也可由函式定義構成,一般將函式定義寫在檔案中,然后用解釋器執行這個檔案
- 換行在代碼中不起任何作用,只是為了提升可讀性
- 分隔符 ; 起分隔作用
a = a * 2
b = a * b
a = a * 2;
b = a * b
a = a * b; b = a * b
a = a * b b = a * b
互動模式
在互動模式中輸入的一行內容會被解釋器當作一個完整的程式塊,如果這一行的內容不足以構成一個完整的程式塊,就會等待輸入
退出互動模式
Ctrl + Z是 end-of-file 控制字符,在 dos 中是這個快捷鍵os.exit()標準庫中的退出函式
區域設定
- lua 中識別什么是字母是通過區域設定來判別的
- 如設定希臘,就可以識別希臘字母作為變數
- 但在不支持該區域的系統上無法執行
執行函式檔案
- lua 函式檔案路徑
dofile("檔案路徑 / 需要轉義")加載函式庫
-- 階乘函式
function fact(n)
if n == 0 then
return 1 --0 的階乘是 1
else
return n * fact(n - 1) -- 3 的階乘, 3 * 2 * 1
end
end
print("Enter a number:")
a = io.read("*number") -- 讀取用戶輸入且需為數字型別的
print(fact(a)) --呼叫階乘函式,并傳入實參 a
-- lib1 函式庫
function norm(x, y)
return (x ^ 2 + y ^ 2) ^ 0.5 -- 兩個數的平方和再開平方根
end
function twice(x)
return 2 * x -- 一個數的兩倍
end
識別符號
定義
- 由任意數字、字母、下劃線構成的字串叫做識別符號
- 識別符號不能由數字開頭
- 識別符號不能以下劃線開頭后跟多個大寫字母
- 如: _PROMPT, _VERSION
- lua 將它們保留用作特殊用途,被稱為啞變數
_PROMPT = ">lua" -- 修改互動模式中的提示符,默認為 >
保留字
流程控制
ifthenelseifendfordoinwhilerepeatuntil
if 條件運算式 then
elseif 條件運算式 then
end
for 控制變數, 終止變數, 步長 do
<回圈體>
end
a = {}
for i,v in ipairs(a) do
<回圈體>
end
while i < 10 do
i = i + 1
print(i)
end
repeat
i = 0
i = i + 1
until i > 10
條件控制
truefalse
邏輯控制
andornot
型別
functionlocalnil
需要注意的點
nil == nil是相等的and和And不同,lua 區分大小寫- lua 中條件值不僅僅只有
true和false - 在 lua 中任何值除了
false和nil都可以用作表示「真」 - 包括空字串
""和數字0
注釋
- 單行注釋
-- - 多行注釋
--[[]] - 使多行注釋中的代碼生效
---[[ <代碼塊> --]] - 多行注釋中包含多行注釋
--[==[ <多行注釋> ]==]
全域變數
- 全域變數不需要宣告,只需要將一個值賦給它即可
- lua 中可以訪問一個未初始化的變數且不會發生錯誤
- 但這個未初始化的變數的值為
nil - 洗掉全域變數賦值
nil即可 - lua 將全域變數存盤在一個普通的 table 中
解釋器
引數
-i先執行程式塊,后進入互動模式-e直接執行代碼塊-l加載庫檔案
解釋器執行引數前
- 會先尋找一個叫做
LUA_INIT的環境變數 - 找到了,且內容為
@檔案名的話,就執行這個檔案 - 沒找到,就假設內容為 lua 代碼, 并執行
解釋器運行腳本前
- lua 將腳本前的引數存盤到 arg 這個 table 中,用作啟動引數
- 腳本名在這個 table 中的索引為 0,其后引數依此類推
- 腳本名前的引數為負數索引
lua -i -e "hello" script a b
arg[0] = "script"
arg[1] = "a"
arg[-1] = "hello"
arg[-2] = "-e"
arg[-3] = "-i"
- 在 lua 中也可以通過變長引數語法來檢索腳本引數
- 變長引數為
...三個點,作為函式引數傳遞時表示傳遞所有引數
2. 型別與值
- lua 是動態型別語言
- 每個值都攜帶有它的型別資訊
獲取值的型別
type()可以回傳一個值的型別名稱type()的回傳結果永遠是string型別的
print(type(3)) -- number
print(type("a")) -- string
print(type({"a", "b", "c"})) -- table
print(type(io.read)) -- function
print(type(true)) -- boolean
number
- 實數,即雙精度浮點數
- 可使用科學計數法,如
2e2表示 200 - 可重新編譯 lua,使用其他型別的值來表示數字型別,如
long tonumber()用于將一個字串顯式的轉換為數字型別
boolean
在 lua 中,有兩個布爾值一個是
true表示為「真」,一個是false表示為「假」但,這兩個值不是用來表示條件的唯一值,在 lua 中 除
nil和false外的任何值,都可以用來表示「真」, 包括空字串
""和數字0
nil
- 只有一個值,
nil - 僅用來表示為空,表示未初始化的變數或 table 元素
- 也可用來洗掉變數或 table 元素
string
- 是物件,由自動記憶體回收器進行分配和釋放
- 是字符序列,是8位編碼
- 可以包含數值編碼,如二進制
- lua 中的字串是唯一不可變的值
..字串連接符,用于連接兩個字串,但數字型別使用時需要用空格隔開#長度運算子,后跟字串,可以獲取字串長度[[]]在期內的特殊字符不需要轉義[==[ <多行注釋> ]==]可以正確列印多行注釋的內容"3" + 4這樣的值會是number型別,發生了運行時隱式轉換
print("\97" == "a") -- 在 ASCII 編碼表中,\97 表示為 a
print(type(3 .. "")) -- string
print(3..4) --報錯
print(3 .. 4) -- 34
print(#"hello") -- 5
-- 獲取子串,證明字串是不可變的值
a = "hello"
b = a .. " ,world"
print(a) -- hello
print(b) -- hello, world
a = [[
<html>
<head><title>蕪湖</title></head>
<body></body>
</html>
]]
a = [==[
--[[
print("多行注釋")
print("多行注釋")
]]
]==]
print(type("3" + 4)) -- number
顯式轉換為字串
tostring().. ""任意數字連接一個空字串即可轉換為字串
table
是物件,由自動記憶體回收器進行分配和釋放
table 沒有固定大小,可以動態添加元素
{}是 table 構造式,用來創建一個 table#長度運算子可以獲取 table 的大小table 中的元素在未被初始化前都是
nil可以將 table 中的元素賦值
nil來進行洗掉如果 table 中間部分的元素值為
nil就說明這是一個有「空隙」的 table有「空隙」的 table 要使用
table.maxn()來回傳這個函式的最大正索引數table 可以用來表示模塊、包、物件
table 中的索引可以是任何值,除了
niltable 是匿名的
程式僅保留了對 table 的一個參考
一個僅有 table 的變數和 table 自身并沒有關系
a.x等價于a["x"]是以字串為索引的a[x]是以變數x為索引的
a = {}
for i = 1, 10 do
a[i] = i
print(a[i])
end
for i = 1, #a do
print(a[i])
end
print(a[10]) -- 10
print(#a) -- 10
a[10] = nil
print(#a) -- 9
a[10000] = 666
print(#a) -- 9
print(table.maxn(a)) -- 10000
a = {}
b = {}
c = a
print(type(a == b)) -- false
print(type(a == c)) -- true
x = "y"
a["x"] = 666
a["y"] = 777
print(a.x) --666
print(a[x]) -- 777
function
- 第一類值
- 可以存盤在變數中
- 可以作為函式的回傳值或引數
- lua 可以呼叫自身撰寫的函式也可以呼叫 C 語言撰寫的函式
- lua 標準庫中的函式都是用 C 語言撰寫的
userdata
- 由應用程式或 C 語言撰寫創建的新型別
- 沒有太多的預定義操作
- 僅用來做賦值和條件測驗
3. 運算式
定義
運算式用于表示值
在 lua 中,函式呼叫,函式定義,數字常量、字面字串,變數,一元和二元運算子,table 構造式都是運算式
算數運算子
一元運算子
-負號
二元運算子
+-減號*/%^
-- % 的技巧
-- x % 1
print(3.13 % 1) -- 得到小數部分
-- x - x % 1
print(3.14 - 3.14 % 1) -- 得到整數部分
-- x - x % 0.1
print(3.14 - 3.14 % 0.1) -- 得到整數部分 + 一位小數部分
-- x - x % 0.01 以此類推,是整數部分 + 兩位小數部分
關系運算子
><>=<===相等性判斷~=不等性判斷
邏輯運算子
and第一個運算元為假,回傳第一個,否則回傳第二個or第一個運算元為真,回傳第一個,否則回傳第二個not只會回傳true或false
-- 短路操作的使用技巧
print(x = x or v) -- 初始化一個值,如果 x 為 nil 沒有被初始化過,就賦值 v
-- 等價于
if not x then
x = v
end
-- 實作 C 語言中的三元運算子, a ? b : c
print((a and b) or c) -- b 必須為真,才可以這樣操作
-- 等價于
if a == true then
return b
elseif a == false then
return c
end
-- 實作回傳兩個數中的較大值
max = (x > y) and x or y -- 因為 lua 將數字視為「真」
-- 等價于
if x > y then
return x
else
return y
end
字串連接
..字串連接
優先級
1級優先
^
2級優先
-負號not#
3級優先
*/%
4級優先
+-減號
5級優先
..字串連接
6級優先
><>=<===~=
7級優先
and
8級優先
or
table 構造式
- lua 標準庫中的函式對 table 的索引都是從 1 開始處理的
記錄風格的 table
a = {x = 10, y = 20} -- 等價于 a.x = 10, a.y = 20
混合使用的 table
- 這種方式的 table 不能以負數和運算子作為索引
a = {
color = {"red", "green", "blue"}
width = 200,
height = 300
}
鏈表
- 由一系列節點組成,節點就是元素
- 節點可以再運行時動態生成
- 每個節點包括兩部分
- 存盤資料的資料域
- 存盤下一個地址節點的指標域
list = nil
for line in io.lines() do
list = {next = list, value = https://www.cnblogs.com/door-leaf/p/line}
end
local l = list
while l do
print(l.value)
l = l.next
end
指定索引的 table
options = {["+"] = "add", ["-"] = "sub",
["*"] = "mul", ["/"] = "div"}
print(options["+"]) -- "add"
4. 陳述句
- 在 lua 中包括賦值,程式結構和程序呼叫
- 還有多重賦值和區域變數宣告
賦值
- lua 支持多重賦值,即
a, b = 1, 2 - 會先計算等號右邊所有元素的值,然后再賦值
- 如果右邊的值少于左邊變數,未被初始化的變數就置為
nil - 如果左邊變數少于右邊的值,多余的值會被「拋棄」
- 可用來收集函式的多個回傳值
- 初始化變數就是為每一個變數賦一個初始值
a, b = 1, 2
x, y = y, x -- 交換變數
a, b = 1 -- a = 1, b = nil
a, b = 1, 2, 3 -- a = 1, b = 2, 3 被拋棄
a, b = f() -- a 接收函式 f 的第一個回傳值,b 接收第二個
a, b, c = 0, 0, 0 -- 初始化賦值
區域變數與塊
塊
- 一個塊就是程式結構的執行體,或函式的執行體
- 在塊內宣告的變數僅在塊內生效,即作用域為宣告它們的塊
- 可顯式宣告一個塊使用
do <要執行的內容> end將要執行的內容包裹在一個塊內
區域變數
local用來宣告一個區域變數- 區域變數僅在宣告它的塊內生效,在塊的外部無效
- 如:在回圈內部宣告在的變數在回圈外部則無法使用
a = 3
b = 0
if a then
local a = 5
b = a -- 將 then 塊內的區域變數 a ,保存到全域變數 b 中
print(a)
end
print(a) -- 3
print(b) -- 5
do
-- code block
end
盡量使用區域變數
- 使用區域變數要比全域變數要快
- 避免污染全域環境
- 區域變數僅在宣告它的塊中有效,即在塊外這個變數就被釋放掉了
- lua 將區域變數宣告當作陳述句處理,即可以在任何支持書寫陳述句的地方書寫區域變數宣告
- 宣告可以包含初始化賦值
程式結構
- 程式結構中的條件運算式可以是任何值
條件結構
ifelseifelse
if 條件運算式 then
<執行體> -- 符合條件運算式執行
end
if 條件運算式1 then
<執行體 1> -- 符合條件運算式 1 執行
elseif 條件運算式2 then
<執行體 2> -- 符合條件運算式 2 執行
end
if 條件運算式 then
<執行體 1> -- 條件運算式為真時執行
else
<執行體 2> -- 條件運算式為假是執行
end
回圈結構
forwhile條件運算式為假時退出repeat ... until條件運算式為真時推出,條件測驗是在回圈體之后做的,因此回圈體至少會執行一次- 在回圈體內宣告的區域變數的作用域包含了條件測驗
- 回圈的三個運算式是在回圈開始前一次性求值的
- 控制變數會被自動宣告為 for 塊中的區域變數,即作用域為 for 塊,在回圈結束后不可見
- 不要在回圈程序中修改控制變數的值
- 可以使用
break或return在回圈正常結束前提前結束它
for exp1, exp2, exp3 do
<回圈體>
end
while 條件運算式 do
<回圈體>
end
repeat
<回圈體>
until 條件運算式
a = 20
repeat
local a = 0
print(a)
until a == 0 -- 可訪問在 repeat 塊內宣告的 a, 而不是全域變數 a
數值型 for
for i = 10, 0, -1 do
print(i)
end
泛型 for
ipairs()用來遍歷陣列i每次回圈時都會賦予一個新的索引值,v則是索引值所對應的元素
a = {1, 2, 3, 4, 5, 6}
for i,v in ipairs(a) do
print(i)
print(v)
end
for i,v in pairs(a) do
print(i)
print(v)
end
兩種 for 的共同點
- 回圈變數都是回圈體的區域變數
- 不應該對回圈遍歷進行賦值
days = {"第一天", "第二天", "第三天"}
revdays = {}
for i, v in ipairs(days) do
revdays[v] = i -- 逆向陣列,將陣列索引和陣列元素調換,可獲取陣列元素的位置
end
print(revdays["第二天"]) -- 獲取第二天所在位置
break 和 return
break用于結束一個回圈,跳出內層回圈后在外層回圈中繼續執行return用于回傳函式結果或簡單的結束函式的執行- 任何函式的結尾處實際都有一句隱式的
return - 如果函式無回傳值,就無需在結尾處加
return
兩者的共同點
- 都是用作跳出當前塊
- 都需要放在一個塊的結尾處,即一個塊的最后一條陳述句
- 因為
return或break后的陳述句將無法執行到 - 可以用
do ... end塊包裹return,用與除錯,即呼叫函式但不執行函式內容的情況
a = 1
if a then
print("hello")
break
print("world") -- 會報錯
end
for i = 1, 10 do
print(i)
if i > 3 then
break -- 只會列印 1 2 3 4 然后就跳出回圈了
end
end
-- 除錯
function foo(...)
do
return
end
print("執行 foo 函式") -- 不會列印
end
foo(1, 2 ,3)
5. 函式
- 函式是對陳述句和運算式進行抽象的主要機制
函式的兩種用法
- 一是可以完成特定的任務,一句函式呼叫被視為一條陳述句
- 二是只用來計算并回傳特定結果,視為一句運算式
print("hello") -- 用來完成列印任務,視為一條陳述句
a = os.date() -- os.date() 用來回傳日期,視為一句運算式
兩種用法的共同點
- 都需要將所有引數放到一對圓括號中
() - 但當引數為字面字串或 table 構造式的時候
()可以省略 - 即使呼叫函式沒有引數,也必須要有一對空的圓括號
()
print "hello" -- hello
print {1, 2, 3} -- 1 2 3
print(os.date) -- 當前日期
定義
function是創建函式的關鍵字function addadd 是函式的名稱function add(n)n 是函式的形式引數,簡稱為形參add(4)4 是呼叫add()函式時的實際參 ,簡稱為實參- 實參多余形參,多余的實參被「拋棄」
- 形參多余實參,多余的形參被置為
nil
function foo(a, b)
return a or b
end
foo(1) -- a = 1, b = nil
foo(1, 2) -- a = 1, b = 2
foo(1, 2, 31) -- a = 1, b = 2, 多余的 31 被拋棄
-- 面向物件式呼叫
o.foo(o, x)
o:foo(x) -- 與上面的效果一樣,: 冒號運算子,隱式的將 o 作為第一個引數
多重回傳值
- lua 中的函式允許回傳多個結果
- 用逗號分隔所需要回傳的所有引數
string.find("you are cool", "are") -- 5 7 回傳找到的字串的開頭位置和結尾位置
-- 查找陣列中的最大元素,并回傳這個元素的所在位置
function maximum(a)
local val = 1
local max = a[val]
for i,v in ipairs(a) do
if max < a[i] then
max = a[i]
val = i
end
end
return max, val
end
a = {1, 2, 55, 22, 29, 4}
maximum(a)
不同情況下的回傳值
- 如果將函式作為單獨的陳述句執行,lua 會丟棄所有的回傳值
- 如果將函式作為運算式的一部分呼叫,只保留函式的第一個回傳值
- 只有當函式是一系列運算式中的最后一個元素(或只有一個元素的時候),才會獲取所有的回傳值
一系列運算式的4種情況
多重賦值
- 如果一個函式呼叫是最后(或僅有)的一個運算式,lua 會保留盡可能多的回傳值,用來匹配賦值的變數
- 如果一個函式沒有回傳值或沒有回傳足夠多的回傳值,那么 lua 會用
nil來補充缺失的值 - 如果一個函式呼叫不是一系列運算式中的最后一個元素,就只能回傳一個值
function foo() end
function foo1() return "a" end
function foo2() return "a", "b" end
-- 第一種情況,最后(或僅有)的一個運算式
x, y = foo1() -- x = a, y = b
-- 第二種情況,沒有回傳值
x = foo() -- nil
-- 第二種情況,沒有回傳足夠多的回傳值
x, y, z = foo1() -- x = a, y = b, z = nil
-- 第三種情況,不是運算式中的最后一個元素
x, y = foo2(), 10 -- x = a, y = 10
函式呼叫時傳入的實參串列
- 如果一個函式呼叫作為另一個函式呼叫的最后一個(或僅有的)實參的時候,第一個函式的所有回傳值都會作為實參傳遞給另一個函式
function foo() end
function foo1() return "a" end
function foo2() return "a", "b" end
-- 第四種情況,作為 print 函式中的最后一個(或僅有的)實參
print(foo()) -- nil
print(foo1()) -- "a"
print(foo2()) -- "a" "b"
print(foo1() .. "test") -- "atest"
print(foo2() .. "test") -- "atest"
table 構造式
- table 構造式會完整接收一個函式呼叫的所有結果,即不會由任何數量方面的調整
- 但這種行為,只有當一個函式呼叫作為最后一個元素時才發生
- 其他位置上的函式呼叫總是只產生一個結果值
function foo() end
function foo1() return "a" end
function foo2() return "a", "b" end
-- 函式呼叫是 table 中的最后一個元素
a = {foo2()} -- a = {"a", "b"}
a = {foo2(), 10} -- a = {"a", 10}
return 陳述句
- 將函式呼叫放入一對圓括號 () 中,使其只回傳一個結果
- return 陳述句后面的內容不需要 () 圓括號
- 如果強行加上則會使一個多回傳值的函式,強制其只回傳一個 return(f())
function foo0() end
function foo1() return "a" end
function foo2() return "a", "b" end
function foo(i)
if i == 0 then return foo0()
elseif i == 1 then return foo1()
elseif i == 2 then return foo2()
end
end
print(foo(1)) -- a
print(foo(2)) -- a, b
print(foo(0)) -- 無回傳值,在互動模式中會是一個空行
-- () 包裹
print((foo(1)) -- a
print((foo(2)) -- a
print((foo(0)) -- nil ,應該是強制回傳了一個未初始化的值,因為 foo0() 沒有回傳值
unpack 函式
- 接收一個陣列作為引數
- 并從下標 1 開始回傳該陣列的所有元素
- 這個預定義函式由 C 語言撰寫
print(unpack{10, 20, 30}) -- 10 20 30
a, b = unpack{10, 20, 30} -- a = 10, b = 20
- 用于泛型呼叫
- 泛型呼叫就是可以以任何實參來呼叫任何函式
-- 呼叫任意函式 f, 而所有的引數都在陣列 a 中
-- unpack 將回傳 a 中的所有值,這些值作為 f 的實參
f(unpack(a))
f = string.find
a = {"hello", "ll"}
f(unpack(a)) -- 3 4 等效于 string.find("hello", "ll")
用 lua 遞回實作 unpack
function unpack(t, i)
i = i or 1
if t[i] then
return t[i], unpack(t, i + 1)
end
end
變長引數
- lua 中的函式可以接收不同數量的實參
- 當這個函式被呼叫時,它的所有引數都會被收集到一起
- 這部分收集起來的實參稱為這個函式的「變長引數」
...三個點表示該函式接收不同數量的實參- 一個函式要訪問它的變長引數時,需要用
...三個點,此時...三個點是作為一個運算式使用的 - 運算式
...三個點的行為類似一個具有多重回傳值的函式,它回傳的是當前函式的所有變長引數 - 具有變長引數的函式也可以擁有任意數量的固定引數
- 但固定引數一定要在變長引數之前
- 當變長引數中包含 nil ,則需要用 select 訪問變長引數
- 呼叫
select時,必須傳入一個固定引數selector(選擇開關) 和一系列變長引數 - 如果
selector為數字 n ,那么select回傳它的第 n 個可變實參 - 否則,
select只能為字串"#",這樣select會回傳變長引數的總數,包括nil
-- 回傳所有引數的和
function add(...)
local s = 0
for i, v in ipairs{...} do -- 運算式{...}表示一個由變長引數構成的陣列
s = s + v
end
return s
end
print(add(3, 4, 5, 100)) -- 115
-- 除錯技巧 ,類似與直接呼叫函式 foo ,但在呼叫 foo 前先呼叫 print 列印其所有的實參
function foo1(...)
print("calling foo:", ...)
return foo(...)
end
-- 獲取函式的實參串列
function foo(a, b, c) end
function foo(...)
local a, b, c = ...
end
-- 格式化文本 string.format ,輸出文本 io.write
-- 固定引數一定要在變長引數之前
function fwrite(fmt, ...)
return io.write(string.format(fmt, ...))
end
fwrite() -- fmt = nil
fwrite("a") -- fmt = a
fwrite("%d%d", 4, 5) -- fmt = "%d%d" , 變長引數 = 4, 5
for i = 1, select('#', ...) do
local arg = select('#', ...)
<回圈體>
end
具名引數
- lua 中的引數傳遞機制是具有 「位置性」的
- 就是說在呼叫一個函式時,實參是通過它在引數表中的位置與形參匹配起來的
- 第一個實參的值與第一個形參相匹配,依此類推
- 定義:通過名稱來指定實參
- 可將所有的實參組織到一個 table 中,并將這個 table 作為唯一的實參傳給函式
- lua 中特殊的函式呼叫語法,當實參只有一個 table 構造式時,函式呼叫中的圓括號
()是可有可無的
os.rename -- 檔案改名,希望達到的效果 os.rename(old = "temp.lua", new = "temp1.lua")
-- lua 不支持注釋的寫法
rename = {old = "temp.lua", new = "temp1.lua"}
function rename (arg)
return os.rename(arg.old, arg.new)
end
x = Window{x = 0, y = 0, width = 300, height = 200, title = "Lua", background = "blue", border = "true"}
-- Window 函式根據要求檢查必填引數,或為某些函式添加默認值
-- 假設 _Window 是真正用于創建新視窗的函式,要求所有引數以正確次序傳入
function Window(options)
if type(options.title) ~= "string" then
error("no title")
elseif type(options.width) ~= "number" then
error("no width")
elseif type(options.height) ~= "height" then
error("no height")
end
_Window(options.title,
options.x or 0 -- 默認值
options.y or 0 -- 默認值
options.width, options.height,
options.background or "white" -- 默認值
options.border -- 默認值為 false(nil)
)
end
因為,目前只學到第五章函式篇,所以只有前五章的復習匯總,很基礎,也很重要,也祝愿大家可以踏踏實實地打好地基,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/5314.html
標籤:其他
上一篇:lua學習之函式篇
下一篇:lua學習之深入函式第一篇
