Lua語言是一門非常小巧精悍的腳本語言,是C/C++天然的好伴侶。這門語言非常的簡單,但是卻有很多語法細節與C語系不同。熟悉C語系語言的同學們剛接觸這門語言的話,可能會因為數組下標從1開始等原因而感到憤怒。這些細節在剛上手的時候全部記下來確實有些困難。湊巧,我也經歷了這麼一個過程。所以在這裡我就把我... ...
本文在我的獨立博客中的鏈接:http://www.bughui.com/2017/04/01/lua-grammar-points/
這篇文章其實是我在四月一號發佈的,由於我重新註冊了一個博客園帳號,所以今天重新發佈。以下為原文內容:
本文所有內容全部都是我在閱讀了《Programming in Lua》這本書之後整理出來的,可以理解本文為一個快速查詢的“手冊”,因此並不適合零基礎的讀者。入門學習的話,還是建議先看看《Programming in Lua》這本書。本文並未包含所有的Lua語法,並不是Lua語法大全。很多高級特性,相信在你用到的時候,以下內容肯定都已經瞭然於心了,不再需要這種入門級的小手冊了。
另外,今天是愚人節!但是我並不想騙你們!以下內容並不是愚人節的禮物,如果踩到坑,恕我無心。
關於Lua語言的一些基本常識
- Chunk 是一系列語句,Lua 執行的每一塊語句,比如一個文件或者交互模式下的每一行都是一個 Chunk。一個 Chunk 可以是一個語句,也可以是一系列語句的組合,還可以是函數,Chunk可以很大,在 Lua 中幾個 MByte 的 Chunk 是很常見的。
- 每個語句結尾的分號(;)是可選的,但如果同一行有多個語句最好用;分開
- 命令lua -la -lb首先在一個 Chunk 內先運行 a 然後運行 b。(註意:-l 選項會調用 require,將會在指定的目錄下搜索文件,如果環境變數沒有設好,上面的命令可能不能正確運行。)
- lua -i -la -lb,-i 選項要求 Lua 運行指定 Chunk 後進入交互模式.
- dofile 函數載入文件並執行它
- 全局變數不需要聲明,給一個變數賦值後即創建了這個全局變數,訪問一個沒有初始化的全局變數也不會出錯,只不過得到的結果是:nil.當且僅當一個變數不等於 nil 時,這個變數存在。
- Lua 是大小寫敏感的.
- 單行註釋:-- 註釋內容
- 多行註釋:--[[ 註釋內容,這裡可以有多行。 --]]
- 可以直接通過命令參數傳入Lua語句。prompt> lua -e "print(math.sin(12))" --> -0.53657291800043 -e:直接將命令傳入 Lua
- 全局變數 arg 存放 Lua 的命令行參數。
類型和值
- Lua 是動態類型語言,變數不要類型定義。 Lua 中有 8 個基本類型分別為: nil、boolean、number、string、userdata、function、thread 和 table。函數 type 可以測試給定變數或者值的類型。
- 關於布爾值需要註意:Lua 中所有的值都可以作為布爾值來用。在控制結構的條件中除了 false 和 nil 為假,其他值都為真。Lua 認為 0 和空串都是真。
- 可以使用單引號或者雙引號表示字元串
- 除了雙引號和單引號,還可以使用[[...]]表示字元串。這種形式的字元串可以包含多行,可以嵌套且不會解釋轉義序列,如果第一個字元是換行符會被自動忽略掉。這種形式的字元串用來包含一段代碼是非常方便的。
- 運行時,Lua 會自動在 string 和 numbers 之間自動進行類型轉換,當一個字元串使用算術操作符時,string 就會被轉成數字。反過來,當 Lua 期望一個 string 而碰到數字時,會將數字轉成 string。
..在 Lua 中是字元串連接符,當在一個數字後面寫..時,必須加上空格以防止被解釋錯。print(10 .. 20) --> 1020
表達式和運算符
- Lua中不等號是~=
- 如果兩個值類型不同,Lua 認為兩者不同;nil 只和自己相等。Lua 通過引用比較 tables、userdata、functions。也就是說當且僅當兩者表示同一個對象時相等。
- Lua 比較數字按傳統的數字大小進行,比較字元串按字母的順序進行,但是字母順序依賴於本地環境。
- Lua的邏輯運算符是 and or not
- list 風格初始化和 record 風格初始化是[expression]一般初始化的特例
Lua數組下標從1開始
基本語法
- Lua 可以對多個變數同時賦值,變數列表和值列表的各個元素用逗號分開,賦值語句右邊的值會依次賦給左邊的變數。a,b = 10, 2x <--> a=10; b=2x。多值賦值經常用來交換變數,或將函數調用返回給變數:a, b = f()
- 遇到賦值語句 Lua 會先計算右邊所有的值然後再執行賦值操作,所以我們可以這樣進行交換變數的值:x, y = y, x。變數個數 > 值的個數 按變數個數補足 nil,變數個數 < 值的個數 多餘的值會被忽略。
- 使用local 創建一個局部變數,與全局變數不同,局部變數只在被聲明的那個代碼塊內有效。代碼塊:指一個控制結構內,一個函數體,或者一個 chunk(變數被聲明的那個文件或者文本串)。
do..end(相當於 c/c++的{})
流程式控制制語句
條件
if conditions then
print("hello elvin!");
end;
if conditions then
print("hello elvin!");
else
print("hello elvin!");
end;
if conditions then
print("hello elvin!");
elseif conditions then
print("hello elvin!");
-- 多個elseif
else
print("hello elvin!");
end
迴圈
while 語句
while condition do
print("hello elvin!");
end;
repeat-until 語句
repeat
print("hello elvin!");
until conditions;
for 語句
for 語句有兩大類
第一類,數值for迴圈
for var=exp1,exp2,exp3 do
loop-part
end
for 將用 exp3 作為 step 從 exp1(初始值)到 exp2(終止值),執行 loop-part。其中exp3可以省略,預設step=1。
有幾點需要註意:
三個表達式只會被計算一次,並且是在迴圈開始前。
for i=1,f(x) do print(i) end for i=10,1,-1 do print(i) end
第一個例子 f(x)只會在迴圈前被調用一次。
控制變數 var 是局部變數自動被聲明,並且只在迴圈內有效.
for i=1,10 do print(i) end max = i -- probably wrong! 'i' here is global
如果需要保留控制變數的值,需要在迴圈中將其保存
-- find a value in a list local found = nil for i=1,a.n do if a[i] == value then found = i -- save value of 'i' break end end print(found)
迴圈過程中不要改變控制變數的值,那樣做的結果是不可預知的。
如果要退出迴圈,使用 break 語句。
第二類,範型for迴圈
前面已經見過一個例子:
-- 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
範型 for 和數值 for 有兩點相同:
- 控制變數是局部變數
- 不要修改控制變數的值
再看一個例子,假定有一個表:
days = {"Sunday", "Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday"}
現在想把對應的名字轉換成星期幾,一個有效地解決問題的方式是構造一個反向表:
revDays = {
["Sunday"] = 1,
["Monday"] = 2,
["Tuesday"] = 3,
["Wednesday"] = 4,
["Thursday"] = 5,
["Friday"] = 6,
["Saturday"] = 7
}
下麵就可以很容易獲取問題的答案了:
x = "Tuesday"
print(revDays[x])
--> 3
其實,我們不需要手工做這件事情,可以自動構造反向表。
revDays = {}
for i,v in ipairs(days) do
revDays[v] = i
end
Lua 語法要求break和return只能出現在block的結尾一句(也就是說:作為 chunk的最後一句,或者在end之前,或者else前,或者until前),有時候為了調試或者其他目的需要在block的中間使用return或者break,可以顯式的使用do..end來實現:
do return end
函數
函數定義語法
function func_name(arguments-list) statements-list; end;
調用函數的時候,如果參數列表為空,必須使用()表明是函數調用。上述規則有一個例外,當函數只有一個參數並且這個參數是字元串或者表構造的時候,()可有可無:
print "Hello World" -- -> print("Hello World") dofile 'a.lua' -- -> dofile ('a.lua') f{x=10, y=20} -- -> f({x=10, y=20}) type{} -- -> type({})
Lua 函數實參和形參的匹配與賦值語句類似,多餘部分被忽略,缺少部分用 nil 補足。
Lua 函數可以接受可變數目的參數,和 C 語言類似在函數參數列表中使用三點(...)表示函數有可變的參數。Lua 將函數的參數放在一個叫 arg 的表中,除了參數以外,arg表中還有一個域 n 表示參數的個數。
如果我們只想要 string.find 返回的第二個值。一個典型的方法是使用啞元(dummy variable,下劃線):
local _, x = string.find(s, p)
使用圓括弧強制使調用返回一個值。
print((foo0())) -- 將只列印foo0的第一個返回值。
Lua中定義方法的另一種特殊方式
Lib = {} function Lib.foo (x,y) return x + y end
- 當我們將函數保存在一個局部變數內時,我們得到一個局部函數,也就是說局部函數像局部變數一樣在一定範圍內有效。
- 方式一
local f = function (...) end local g = function (...) f() -- external local `f' is visible here end
- 方式二
local function f (...) end
在定義非直接遞歸局部函數時要先聲明然後定義才可以
環境
Lua 用一個名為 environment 普通的表來保存所有的全局變數。
Lua 將環境本身存儲在一個全局變數_G 中,(_G._G 等於_G)。