Lua 快速指南



Lua - 概述

Lua 是一種用 C 語言編寫的可擴充套件、輕量級程式語言。它始於 1993 年 Roberto Ierusalimschy、Luiz Henrique de Figueiredo 和 Waldemar Celes 的內部專案。

它從一開始就被設計成一種可以與用 C 語言和其他傳統語言編寫的程式碼整合的軟體。這種整合帶來了許多好處。它不試圖做 C 語言已經能夠做的事情,而是旨在提供 C 語言不擅長的事情:遠離硬體,動態結構,沒有冗餘,易於測試和除錯。為此,Lua 提供了安全的環境、自動記憶體管理以及處理動態大小的字串和其他型別資料的好工具。

特性

Lua 提供了一組獨特的特性,使其有別於其他語言。這些包括:

  • 可擴充套件的
  • 簡單的
  • 高效的
  • 可移植的
  • 免費和開放的

示例程式碼

print("Hello World!")

Lua是如何實現的?

Lua 由兩部分組成——Lua 直譯器部分和功能軟體系統。功能軟體系統是一個實際的計算機應用程式,可以解釋用 Lua 程式語言編寫的程式。Lua 直譯器是用 ANSI C 編寫的,因此它具有高度的可移植性,可以在從高階網路伺服器到小型裝置的各種裝置上執行。

Lua 的語言及其直譯器都成熟、小巧且快速。它從其他程式語言和頂級軟體標準發展而來。體積小巧使其能夠在記憶體較小的小型裝置上執行。

學習 Lua

學習 Lua 最重要的一點是專注於概念,而不要迷失在它的技術細節中。

學習程式語言的目的是成為一名更好的程式設計師;也就是說,在設計和實現新系統以及維護舊系統方面變得更有效率。

Lua 的一些用途

  • 遊戲程式設計

  • 獨立應用程式中的指令碼

  • Web 中的指令碼

  • 資料庫(如 MySQL Proxy 和 MySQL WorkBench)的擴充套件和附加元件

  • 安全系統,如入侵檢測系統。

Lua - 環境

本地環境設定

如果您仍然希望為 Lua 程式語言設定環境,則您的計算機上需要以下軟體:(a) 文字編輯器,(b) Lua 直譯器和 (c) Lua 編譯器。

文字編輯器

您需要一個文字編輯器來鍵入您的程式。一些編輯器的示例包括 Windows 記事本、OS Edit 命令、Brief、Epsilon、EMACS 和 vim 或 vi。

文字編輯器的名稱和版本在不同的作業系統上可能有所不同。例如,Notepad 將在 Windows 上使用,而 vim 或 vi 可以在 Windows 以及 Linux 或 UNIX 上使用。

您使用編輯器建立的檔案稱為原始檔,這些檔案包含程式原始碼。Lua 程式的原始檔通常以副檔名 “.lua”命名。

Lua 直譯器

它只是一個小型程式,使您能夠鍵入 Lua 命令並立即執行它們。與執行完整的編譯器不同,如果遇到錯誤,它會停止執行 Lua 檔案。

Lua 編譯器

當我們將 Lua 擴充套件到其他語言/應用程式時,我們需要一個與 Lua 應用程式程式設計介面相容的編譯器的軟體開發工具包。

在 Windows 上安裝

有一個名為“SciTE”的單獨 IDE 為 Windows 環境開發,可以從 https://code.google.com/p/luaforwindows/ 下載部分下載。

執行下載的可執行檔案以安裝 Lua IDE。

因為它是一個 IDE,所以您可以使用相同的 IDE 建立和構建 Lua 程式碼。

如果您有興趣以命令列模式安裝 Lua,則需要安裝 MinGW 或 Cygwin,然後在 Windows 中編譯和安裝 Lua。

在 Linux 上安裝

要下載和構建 Lua,請使用以下命令:

$ wget http://www.lua.org/ftp/lua-5.2.3.tar.gz
$ tar zxf lua-5.2.3.tar.gz
$ cd lua-5.2.3
$ make linux test

為了在其他平臺(如 aix、ansi、bsd、通用 Linux、mingw、posix、solaris)上安裝,請用相應的平臺名稱替換 make Linux 中的 Linux,並進行測試。

我們有一個 Lua 中的 helloWorld.lua,如下所示:

print("Hello World!")

現在,我們可以透過使用 cd 切換到包含該檔案的資料夾,然後使用以下命令來構建和執行 Lua 檔案(例如 helloWorld.lua):

$ lua helloWorld

我們可以看到以下輸出。

Hello World!

在 Mac OS X 上安裝

要在 Mac OS X 中構建/測試 Lua,請使用以下命令:

$ curl -R -O http://www.lua.org/ftp/lua-5.2.3.tar.gz
$ tar zxf lua-5.2.3.tar.gz
$ cd lua-5.2.3
$ make macosx test

在某些情況下,您可能沒有安裝 Xcode 和命令列工具。在這種情況下,您將無法使用 make 命令。從 Mac App Store 安裝 Xcode。然後轉到 Xcode 的“偏好設定”,然後切換到“下載”並安裝名為“命令列工具”的元件。該過程完成後,make 命令將可供您使用。

您不必執行“make macosx test”語句。即使不執行此命令,您仍然可以在 Mac OS X 中使用 Lua。

我們有一個 Lua 中的 helloWorld.lua,如下所示:

print("Hello World!")

現在,我們可以透過使用 cd 切換到包含該檔案的資料夾,然後使用以下命令來構建和執行 Lua 檔案(例如 helloWorld.lua):

$ lua helloWorld

我們可以看到以下輸出:

Hello World!

Lua IDE

如前所述,對於 Windows SciTE,Lua IDE 是 Lua 建立團隊提供的預設 IDE。另一個可用的 IDE 來自 ZeroBrane Studio,可在 Windows、Mac 和 Linux 等多個平臺上使用。

還有一些 Eclipse 外掛可以啟用 Lua 開發。使用 IDE 可以使開發更容易,並具有程式碼完成等功能,因此強烈推薦使用。IDE 還提供與 Lua 命令列版本類似的互動式模式程式設計。

Lua - 基本語法

讓我們開始建立我們的第一個 Lua 程式!

第一個 Lua 程式

互動式模式程式設計

Lua 提供了一種稱為互動式模式的模式。在此模式下,您可以一個接一個地鍵入指令並獲得即時結果。這可以透過在 shell 中使用 lua -i 或僅使用 lua 命令來呼叫。鍵入此命令後,按 Enter 鍵即可啟動互動模式,如下所示。

$ lua -i 
$ Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
quit to end; cd, dir and edit also available

您可以使用以下語句列印內容:

print("test")

按 Enter 鍵後,您將獲得以下輸出:

test

預設模式程式設計

使用 Lua 檔名引數呼叫直譯器將開始執行該檔案,並持續到指令碼完成。指令碼完成後,直譯器將不再活動。

讓我們編寫一個簡單的 Lua 程式。所有 Lua 檔案都將具有副檔名 .lua。因此,請將以下原始碼放入 test.lua 檔案中。

print("test")

假設 Lua 環境已正確設定,讓我們使用以下程式碼執行程式:

$ lua test.lua

我們將獲得以下輸出:

test

讓我們嘗試另一種執行 Lua 程式的方法。以下是修改後的 test.lua 檔案:

#!/usr/local/bin/lua

print("test")

在這裡,我們假設您的 Lua 直譯器位於 /usr/local/bin 目錄中。如果第一行以 # 符號開頭,則直譯器會忽略它。現在,嘗試按如下方式執行此程式:

$ chmod a+rx test.lua
$./test.lua

我們將獲得以下輸出。

test

現在讓我們看看 Lua 程式的基本結構,這樣您就可以輕鬆理解 Lua 程式語言的基本構建塊。

Lua 中的標記

Lua 程式由各種標記組成,標記要麼是關鍵字,要麼是識別符號,要麼是常量,要麼是字串文字,要麼是符號。例如,以下 Lua 語句包含三個標記:

io.write("Hello world, from ",_VERSION,"!\n")

各個標記是:

io.write
(
   "Hello world, from ",_VERSION,"!\n"
)

註釋

註釋就像 Lua 程式中的幫助文字,直譯器會忽略它們。它們以 --[[ 開頭,以 --]] 結尾,如下所示:

--[[ my first program in Lua --]]

識別符號

Lua 識別符號是用於標識變數、函式或任何其他使用者定義項的名稱。識別符號以字母“A 到 Z”或“a 到 z”或下劃線“_”開頭,後跟零個或多個字母、下劃線和數字(0 到 9)。

Lua 不允許在識別符號中使用標點符號,例如 @、$ 和 %。Lua 是一種**區分大小寫**的程式語言。因此,Manpowermanpower 是 Lua 中的兩個不同識別符號。以下是一些可接受的識別符號示例:

mohd         zara      abc     move_name    a_123
myname50     _temp     j       a23b9        retVal

關鍵字

以下列表顯示了 Lua 中的一些保留字。這些保留字不能用作常量或變數或任何其他識別符號名稱。

and break do else
elseif end false for
function if in local
nil not or repeat
return then true until
while

Lua 中的空格

僅包含空格(可能帶有註釋)的行稱為空行,Lua 直譯器會完全忽略它。

在Lua中,“空白”指的是空格、製表符、換行符和註釋。空白字元用於分隔語句的不同部分,使直譯器能夠識別語句中一個元素(例如int)的結束位置和下一個元素的開始位置。因此,在下面的語句中:

local age

必須在local和age之間至少有一個空白字元(通常是空格),以便直譯器能夠區分它們。另一方面,在下面的語句中:

fruit = apples + oranges   --get the total fruit

fruit和=之間,以及=和apples之間不需要空白字元,儘管為了可讀性,您可以隨意新增一些。

Lua - 變數

變數只不過是程式可以操作的儲存區域的名稱。它可以儲存不同型別的數值,包括函式和表。

變數名可以由字母、數字和下劃線組成。它必須以字母或下劃線開頭。Lua區分大小寫,因此大小寫字母是不同的。Lua中有八種基本型別的數值:

在Lua中,雖然我們沒有變數資料型別,但我們根據變數的作用域有三種類型。

  • 全域性變數 - 除非明確宣告為區域性變數,否則所有變數都被視為全域性變數。

  • 區域性變數 - 當變數的型別指定為local時,其作用域僅限於其作用域內的函式。

  • 表字段 - 這是一種特殊的變數型別,可以儲存除nil之外的任何值,包括函式。

Lua中的變數定義

變數定義是指告訴直譯器在哪裡以及如何為變數建立儲存空間。變數定義可以有一個可選的型別,幷包含一個或多個該型別的變數列表,如下所示:

type variable_list;

這裡,type 可選地是local或指定的型別,使其成為全域性變數,而variable_list 可以包含一個或多個以逗號分隔的識別符號名稱。這裡顯示了一些有效的宣告:

local    i, j
local    i
local    a,c

語句local i, j 同時宣告和定義了變數i和j;它指示直譯器建立名為i、j的變數,並將作用域限制為區域性作用域。

可以在變數宣告中初始化(賦值初始值)變數。初始化程式由一個等號後跟一個常量表達式組成,如下所示:

type variable_list = value_list;

一些例子:

local d , f = 5 ,10     --declaration of d and f as local variables. 
d , f = 5, 10;          --declaration of d and f as global variables. 
d, f = 10               --[[declaration of d and f as global variables. 
                           Here value of f is nil --]]

對於沒有初始化程式的定義:具有靜態儲存持續時間的變數會隱式初始化為nil。

Lua中的變數宣告

正如你在上面的例子中看到的,多個變數的賦值遵循variable_list和value_list的格式。在上面的例子local d, f = 5,10中,我們有variable_list中的d和f,以及value_list中的5和10。

Lua中的值賦值方式是:將variable_list中的第一個變數與value_list中的第一個值關聯,依次類推。因此,d的值為5,f的值為10。

示例

嘗試以下示例,其中變數已在頂部宣告,但在主函式內定義和初始化:

-- Variable definition:
local a, b

-- Initialization
a = 10
b = 30

print("value of a:", a)

print("value of b:", b)

-- Swapping of variables
b, a = a, b

print("value of a:", a)

print("value of b:", b)

f = 70.0/3.0
print("value of f", f)

當編譯並執行上述程式碼時,會產生以下結果:

value of a:	10
value of b:	30
value of a:	30
value of b:	10
value of f	23.333333333333

Lua中的左值和右值

Lua中有兩種表示式:

  • 左值 (lvalue) - 指向記憶體位置的表示式稱為“左值”表示式。左值可以出現在賦值的左側或右側。

  • 右值 (rvalue) - 右值指的是儲存在記憶體某個地址的資料值。右值是一個不能被賦值的表示式,這意味著右值可以出現在賦值的右側,但不能出現在左側。

變數是左值,因此可以出現在賦值的左側。數字字面量是右值,因此不能賦值,不能出現在左側。以下是一個有效的語句:

g = 20

但以下語句無效,會生成編譯時錯誤:

10 = 20

在Lua程式語言中,除了上述型別的賦值外,同一個語句中還可以有多個左值和右值。如下所示。

g,l = 20,30

在上述語句中,20被賦值給g,30被賦值給l。

Lua - 資料型別

Lua是一種動態型別語言,因此變數沒有型別,只有值才有型別。值可以儲存在變數中,作為引數傳遞,並作為結果返回。

在Lua中,雖然我們沒有變數資料型別,但是我們有數值的型別。數值的資料型別列表如下所示。

序號 數值型別及描述
1

nil

用於區分數值是否有資料或無(nil)資料。

2

boolean

包含true和false作為值。通常用於條件檢查。

3

number

表示實數(雙精度浮點數)。

4

string

表示字元陣列。

5

function

function

6

表示用C或Lua編寫的函式。

userdata

7

表示任意的C資料。

thread

8

表示獨立的執行執行緒,用於實現協程。

table

表示普通陣列、符號表、集合、記錄、圖、樹等,並實現關聯陣列。它可以儲存任何值(除了nil)。

type函式

print(type("What is my type"))   --> string
t = 10

print(type(5.8*t))               --> number
print(type(true))                --> boolean
print(type(print))               --> function
print(type(nil))                 --> nil
print(type(type(ABC)))           --> string

在Lua中,有一個名為“type”的函式,它使我們能夠知道變數的型別。以下程式碼中給出了一些示例。

string
number
boolean
function
nil
string

構建並執行上述程式後,它會在Linux上產生以下結果:

Lua - 運算子

預設情況下,所有變數在賦值或初始化之前都指向nil。在Lua中,零和空字串在條件檢查中被認為是true。因此,在使用布林運算時必須小心。我們將在接下來的章節中更多地瞭解這些型別。

  • 運算子是一個符號,它告訴直譯器執行特定的數學或邏輯操作。Lua語言富含內建運算子,並提供以下型別的運算子:
  • 算術運算子
  • 關係運算符
  • 邏輯運算子

其他運算子

運算子是一個符號,它告訴直譯器執行特定的數學或邏輯操作。Lua語言富含內建運算子,並提供以下型別的運算子:

本教程將逐一解釋算術、關係、邏輯和其他雜項運算子。

下表顯示了Lua語言支援的所有算術運算子。假設變數A為10,變數B為20,則:

示例 運算子 示例
+ 描述 將兩個運算元相加
- A + B 將得到 30 從第一個運算元中減去第二個運算元
* A - B 將得到 -10 將兩個運算元相乘
/ A * B 將得到 200 將分子除以分母
% B / A 將得到 2 取模運算子,返回整數除法後的餘數
^ B % A 將得到 0 指數運算子,計算指數
- A^2 將得到 100 一元 - 運算子作為否定運算子

算術運算子

-A 將得到 -10

下表顯示了Lua語言支援的所有算術運算子。假設變數A為10,變數B為20,則:

示例 運算子 示例
== 下表顯示了Lua語言支援的所有關係運算符。假設變數A為10,變數B為20,則: 檢查兩個運算元的值是否相等,如果相等則條件為真。
~= (A == B) 為假。 檢查兩個運算元的值是否不相等,如果不相等則條件為真。
> (A ~= B) 為真。 檢查左側運算元的值是否大於右側運算元的值,如果大於則條件為真。
< (A > B) 為假。 檢查左側運算元的值是否小於右側運算元的值,如果小於則條件為真。
>= (A < B) 為真。 檢查左側運算元的值是否大於或等於右側運算元的值,如果大於或等於則條件為真。
<= (A >= B) 為假。 檢查左側運算元的值是否小於或等於右側運算元的值,如果小於或等於則條件為真。

關係運算符

(A <= B) 為真。

下表顯示了Lua語言支援的所有算術運算子。假設變數A為10,變數B為20,則:

示例 運算子 示例
and 下表顯示了Lua語言支援的所有邏輯運算子。假設變數A為true,變數B為false,則: 稱為邏輯與運算子。如果兩個運算元都不為零,則條件為真。
or (A and B) 為假。 稱為邏輯或運算子。如果兩個運算元中的任何一個不為零,則條件為真。
not (A or B) 為真。 稱為邏輯非運算子。用於反轉其運算元的邏輯狀態。如果條件為真,則邏輯非運算子將使其為假。

邏輯運算子

!(A and B) 為真。

下表顯示了Lua語言支援的所有算術運算子。假設變數A為10,變數B為20,則:

示例 運算子 示例
.. Lua語言支援的其他運算子包括連線長度 連線兩個字串。
# a..b,其中a為"Hello ",b為"World",將返回"Hello World"。 一元運算子,返回字串或表的長度。

#"Hello" 將返回 5

Lua中的運算子優先順序

運算子優先順序決定了表示式中項的分組方式。這會影響表示式的計算方式。某些運算子的優先順序高於其他運算子;例如,乘法運算子的優先順序高於加法運算子:

例如,x = 7 + 3 * 2;這裡x賦值為13,而不是20,因為運算子*的優先順序高於+,所以它先與3*2相乘,然後加到7中。

下表顯示了Lua語言支援的所有算術運算子。假設變數A為10,變數B為20,則:

這裡,優先順序最高的運算子出現在表的最上面,優先順序最低的出現在表的最下面。在一個表示式中,優先順序高的運算子將首先被計算。 示例 類別
結合性 一元 not # -
從右到左 .. not # -
連線 * / % 從左到右
乘法 + - 從左到右
加法 關係 從左到右
< > <= >= == ~= == ~= 從左到右
等式 and 從左到右
邏輯與 or 從左到右

Lua - 迴圈

邏輯或

可能會有這樣的情況:你需要多次執行一段程式碼。通常情況下,語句是順序執行的:函式中的第一個語句首先執行,然後是第二個語句,依此類推。

程式語言提供各種控制結構,允許更復雜的執行路徑。

Loop Architecture

迴圈語句允許我們多次執行一條語句或一組語句。以下是大多數程式語言中迴圈語句的一般形式:

Lua提供以下型別的迴圈來處理迴圈需求。 序號
1 迴圈型別及描述

while迴圈

2 當給定條件為真時,重複執行一條語句或一組語句。它在執行迴圈體之前測試條件。

for迴圈

3 repeat...until迴圈

重複執行一組語句,直到滿足until條件。

4 巢狀迴圈

你可以在任何其他的while、for或do..while迴圈內部使用一個或多個迴圈。

迴圈控制語句

迴圈控制語句改變了執行的正常順序。當執行離開一個作用域時,在該作用域中建立的所有自動物件都會被銷燬。

Lua支援以下控制語句。

Lua提供以下型別的迴圈來處理迴圈需求。 控制語句及描述
1 break語句

終止迴圈並將執行轉移到緊跟在迴圈或switch之後的語句。

無限迴圈

如果條件永遠不會變為false,則迴圈將變成無限迴圈。while迴圈通常用於此目的。由於我們直接為條件提供true,因此它會永遠執行下去。我們可以使用break語句來中斷此迴圈。

while( true )
do
   print("This loop will run forever.")
end

Lua - 決策

決策結構要求程式設計師指定一個或多個條件,由程式進行評估或測試,以及如果條件被確定為true,則要執行的語句,或者可選地,如果條件被確定為false,則要執行的其他語句。

以下是大多數程式語言中典型的決策結構的一般形式:

Decision making statements in Lua

Lua程式語言將任何布林true非nil值的組合視為true,如果是布林falsenil,則視為false值。需要注意的是,在Lua中,零將被視為true

Lua程式語言提供以下型別的決策語句。

Lua提供以下型別的迴圈來處理迴圈需求。 語句及描述
1 if語句

if語句由一個布林表示式和一個或多個語句組成。

2 if...else語句

if語句後面可以跟一個可選的else語句,當布林表示式為false時執行。

3 巢狀if語句

你可以在另一個ifelse if語句中使用一個ifelse if語句。

Lua - 函式

函式是一組共同執行一項任務的語句。你可以將程式碼分成單獨的函式。你如何將程式碼劃分到不同的函式中取決於你,但邏輯上劃分通常是唯一的,因此每個函式執行一項特定任務。

Lua語言提供了許多內建方法,你的程式可以呼叫這些方法。例如,方法print()用於將作為輸入傳遞的引數列印到控制檯。

函式以各種名稱而聞名,例如方法、子例程或過程等。

定義函式

Lua程式語言中方法定義的一般形式如下:

optional_function_scope function function_name( argument1, argument2, argument3........, 
argumentn)
function_body
return result_params_comma_separated
end

Lua程式語言中的方法定義由方法頭方法體組成。以下是方法的所有部分:

  • 可選函式作用域 - 你可以使用關鍵字local來限制函式的作用域或忽略作用域部分,這將使其成為全域性函式。

  • 函式名 - 這是函式的實際名稱。函式名和引數列表共同構成函式簽名。

  • 引數 - 引數就像一個佔位符。當呼叫函式時,你將值傳遞給引數。此值被稱為實際引數或自變數。引數列表是指方法的引數型別、順序和數量。引數是可選的;也就是說,方法可能不包含任何引數。

  • 函式體 - 方法體包含定義方法執行內容的一組語句。

  • 返回值 - 在Lua中,可以透過在return關鍵字後跟逗號分隔的返回值來返回多個值。

示例

以下是名為max()函式的原始碼。此函式接受兩個引數num1和num2,並返回兩者之間的最大值:

--[[ function returning the max between two numbers --]]
function max(num1, num2)

   if (num1 > num2) then
      result = num1;
   else
      result = num2;
   end

   return result; 
end

函式引數

如果函式要使用引數,則必須宣告接受引數值的變數。這些變數稱為函式的形式引數

形式引數在函式內部的行為類似於其他區域性變數,並在進入函式時建立,並在退出函式時銷燬。

呼叫函式

在建立Lua函式時,你將定義函式必須執行的操作。要使用方法,你必須呼叫該函式來執行定義的任務。

當程式呼叫函式時,程式控制將轉移到被呼叫的函式。被呼叫的函式執行定義的任務,當執行其return語句或達到其函式末尾時,它將程式控制返回到主程式。

要呼叫方法,你只需將所需引數與方法名稱一起傳遞,如果方法返回值,則可以儲存返回值。例如:

function max(num1, num2)

   if (num1 > num2) then
      result = num1;
   else
      result = num2;
   end

   return result; 
end

-- calling a function
print("The maximum of the two numbers is ",max(10,4))
print("The maximum of the two numbers is ",max(5,6))

執行上述程式碼後,我們將得到以下輸出。

The maximum of the two numbers is 	10
The maximum of the two numbers is 	6

賦值和傳遞函式

在Lua中,我們可以將函式賦值給變數,也可以將它們作為另一個函式的引數傳遞。這是一個在Lua中賦值和傳遞函式作為引數的簡單示例。

myprint = function(param)
   print("This is my print function -   ##",param,"##")
end

function add(num1,num2,functionPrint)
   result = num1 + num2
   functionPrint(result)
end

myprint(10)
add(2,5,myprint)

執行上述程式碼後,我們將得到以下輸出。

This is my print function -   ##	10	##
This is my print function -   ##	7	##

帶有可變引數的函式

可以使用'...'作為引數在Lua中建立具有可變引數的函式。我們可以透過檢視一個示例來了解這一點,在這個示例中,函式將返回平均值,並且它可以接受可變引數。

function average(...)
   result = 0
   local arg = {...}
   for i,v in ipairs(arg) do
      result = result + v
   end
   return result/#arg
end

print("The average is",average(10,5,3,4,5,6))

執行上述程式碼後,我們將得到以下輸出。

The average is	5.5

Lua - 字串

字串是一系列字元以及控制字元,如換頁符。字串可以用三種形式初始化,包括:

  • 單引號之間的字元
  • 雙引號之間的字元
  • [[和]]之間的字元

下面顯示了上述三種形式的示例。

string1 = "Lua"
print("\"String 1 is\"",string1)

string2 = 'Tutorial'
print("String 2 is",string2)

string3 = [["Lua Tutorial"]]
print("String 3 is",string3)

執行上述程式後,我們將得到以下輸出。

"String 1 is" Lua
String 2 is	Tutorial
String 3 is	"Lua Tutorial"

轉義序列字元用於字串中以更改字元的正常解釋。例如,要列印雙引號(""),我們在上述示例中使用了\"。轉義序列及其用途在下面的表格中列出。

轉義序列 用途
\a 響鈴
\b 退格
\f 換頁
\n 換行
\r 回車
\t 製表符
\v 垂直製表符
\\ 反斜槓
\" 雙引號
\' 單引號
\[ 左方括號
\] 右方括號

字串操作

Lua支援用於操作字串的字串:

Lua提供以下型別的迴圈來處理迴圈需求。 方法及用途
1

string.upper(argument)

返回引數的大寫表示形式。

2

string.lower(argument)

返回引數的小寫表示形式。

3

string.gsub(mainString,findString,replaceString)

透過將findString的出現替換為replaceString來返回一個字串。

4

string.find(mainString,findString,

optionalStartIndex,optionalEndIndex)

返回findString在主字串中的起始索引和結束索引,如果未找到則返回nil。

5

string.reverse(arg)

透過反轉傳遞字串的字元來返回一個字串。

6

string.format(...)

返回格式化的字串。

7

string.char(arg) 和 string.byte(arg)

返回輸入引數的內部數值和字元表示。

8

string.len(arg)

返回傳遞字串的長度。

9

string.rep(string, n))

透過重複相同的字串n次來返回一個字串。

10

..

因此,運算子連線兩個字串。

現在,讓我們深入瞭解一些示例,以準確瞭解這些字串操作函式的行為。

大小寫轉換

下面給出了一個用於將字串轉換為大寫和小寫的示例程式碼。

string1 = "Lua";

print(string.upper(string1))
print(string.lower(string1))

執行上述程式後,我們將得到以下輸出。

LUA
lua

替換子字串

下面給出了一個用另一個字串替換一個字串出現的示例程式碼。

string = "Lua Tutorial"

-- replacing strings
newstring = string.gsub(string,"Tutorial","Language")
print("The new string is "..newstring)

執行上述程式後,我們將得到以下輸出。

The new string is Lua Language

查詢和反轉

下面給出了一個用於查詢子字串索引和反轉字串的示例程式碼。

string = "Lua Tutorial"

-- replacing strings
print(string.find(string,"Tutorial"))
reversedString = string.reverse(string)
print("The new string is",reversedString)

執行上述程式後,我們將得到以下輸出。

5	12
The new string is	lairotuT auL

格式化字串

在我們的程式設計中,很多時候可能需要以格式化的方式列印字串。你可以使用string.format函式來格式化輸出,如下所示。

string1 = "Lua"
string2 = "Tutorial"

number1 = 10
number2 = 20

-- Basic string formatting
print(string.format("Basic formatting %s %s",string1,string2))

-- Date formatting
date = 2; month = 1; year = 2014
print(string.format("Date formatting %02d/%02d/%03d", date, month, year))

-- Decimal formatting
print(string.format("%.4f",1/3))

執行上述程式後,我們將得到以下輸出。

Basic formatting Lua Tutorial
Date formatting 02/01/2014
0.3333

字元和位元組表示

字元和位元組表示的示例程式碼,用於將字串從字串轉換為內部表示,反之亦然。

-- Byte conversion

-- First character
print(string.byte("Lua"))

-- Third character
print(string.byte("Lua",3))

-- first character from last
print(string.byte("Lua",-1))

-- Second character
print(string.byte("Lua",2))

-- Second character from last
print(string.byte("Lua",-2))

-- Internal Numeric ASCII Conversion
print(string.char(97))

執行上述程式後,我們將得到以下輸出。

76
97
97
117
117
a

其他常用函式

常見的字串操作包括字串連線、查詢字串長度,有時是重複相同的字串多次。下面給出了這些操作的示例。

string1 = "Lua"
string2 = "Tutorial"

-- String Concatenations using ..
print("Concatenated string",string1..string2)

-- Length of string
print("Length of string1 is ",string.len(string1))

-- Repeating strings
repeatedString = string.rep(string1,3)
print(repeatedString)

執行上述程式後,我們將得到以下輸出。

Concatenated string	LuaTutorial
Length of string1 is 	3
LuaLuaLua

Lua - 陣列

陣列是有序的物件排列,它可以是一維陣列,包含一系列行,也可以是多維陣列,包含多行多列。

在Lua中,陣列使用帶有整數的索引表實現。陣列的大小不是固定的,它可以根據我們的需求增長,但受記憶體限制。

一維陣列

一維陣列可以使用簡單的表結構表示,並可以使用簡單的for迴圈進行初始化和讀取。下面顯示了一個示例。

array = {"Lua", "Tutorial"}

for i = 0, 2 do
   print(array[i])
end

執行上述程式碼後,我們將得到以下輸出。

nil
Lua
Tutorial

正如你在上述程式碼中看到的,當我們試圖訪問陣列中不存在的索引中的元素時,它會返回nil。在Lua中,索引通常從索引1開始。但也可以在索引0和0以下建立物件。下面顯示了使用負索引的陣列,我們使用for迴圈初始化陣列。

array = {}

for i= -2, 2 do
   array[i] = i *2
end

for i = -2,2 do
   print(array[i])
end

執行上述程式碼後,我們將得到以下輸出。

-4
-2
0
2
4

多維陣列

多維陣列可以用兩種方式實現。

  • 陣列的陣列
  • 透過操作索引的一維陣列

下面使用陣列的陣列顯示了3x3多維陣列的示例。

-- Initializing the array
array = {}

for i=1,3 do
   array[i] = {}
	
   for j=1,3 do
      array[i][j] = i*j
   end
	
end

-- Accessing the array

for i=1,3 do

   for j=1,3 do
      print(array[i][j])
   end
	
end

執行上述程式碼後,我們將得到以下輸出。

1
2
3
2
4
6
3
6
9

下面顯示了使用操作索引的多維陣列的示例。

-- Initializing the array

array = {}

maxRows = 3
maxColumns = 3

for row=1,maxRows do

   for col=1,maxColumns do
      array[row*maxColumns +col] = row*col
   end
	
end

-- Accessing the array

for row=1,maxRows do

   for col=1,maxColumns do
      print(array[row*maxColumns +col])
   end
	
end

執行上述程式碼後,我們將得到以下輸出。

1
2
3
2
4
6
3
6
9

正如你在上面的示例中看到的,資料是基於索引儲存的。可以以稀疏的方式放置元素,這就是Lua矩陣實現的工作方式。由於它不在Lua中儲存nil值,因此與其他程式語言中使用的特殊技術相比,無需在Lua中使用任何特殊技術即可節省大量記憶體。

Lua - 迭代器

迭代器是一種構造,使你能夠遍歷所謂的集合或容器的元素。在Lua中,這些集合通常指表,用於建立各種資料結構,如陣列。

泛型for迭代器

泛型for迭代器提供集合中每個元素的鍵值對。下面給出了一個簡單的例子。

array = {"Lua", "Tutorial"}

for key,value in ipairs(array) 
do
   print(key, value)
end

執行上述程式碼後,我們將得到以下輸出:

1  Lua
2  Tutorial

上述示例使用Lua提供的預設ipairs迭代器函式。

在Lua中,我們使用函式來表示迭代器。根據這些迭代器函式中的狀態維護,我們主要有兩種型別:

  • 無狀態迭代器
  • 有狀態迭代器

無狀態迭代器

顧名思義,這種型別的迭代器函式不保留任何狀態。

現在讓我們來看一個使用簡單函式建立我們自己的迭代器的例子,該函式列印n個數的平方。

function square(iteratorMaxCount,currentNumber)

   if currentNumber<iteratorMaxCount
   then
      currentNumber = currentNumber+1
      return currentNumber, currentNumber*currentNumber
   end
	
end

for i,n in square,3,0
do
   print(i,n)
end

執行上述程式後,我們將得到以下輸出。

1	1
2	4
3	9

上面的程式碼可以稍微修改一下,以模擬迭代器的ipairs函式的工作方式。如下所示。

function square(iteratorMaxCount,currentNumber)

   if currentNumber<iteratorMaxCount
   then
      currentNumber = currentNumber+1
      return currentNumber, currentNumber*currentNumber
   end
	
end

function squares(iteratorMaxCount)
   return square,iteratorMaxCount,0
end  

for i,n in squares(3)
do 
   print(i,n)
end

執行上述程式後,我們將得到以下輸出。

1	1
2	4
3	9

有狀態迭代器

之前的使用函式的迭代示例不保留狀態。每次呼叫函式時,它都會根據傳送到函式的第二個變數返回集合的下一個元素。為了儲存當前元素的狀態,使用閉包。閉包跨函式呼叫保留變數值。要建立一個新的閉包,我們建立兩個函式,包括閉包本身和一個工廠,即建立閉包的函式。

現在讓我們來看一個建立我們自己的迭代器的例子,在這個例子中我們將使用閉包。

array = {"Lua", "Tutorial"}

function elementIterator (collection)

   local index = 0
   local count = #collection
	
   -- The closure function is returned
	
   return function ()
      index = index + 1
		
      if index <= count
      then
         -- return the current element of the iterator
         return collection[index]
      end
		
   end
	
end

for element in elementIterator(array)
do
   print(element)
end

執行上述程式後,我們將得到以下輸出。

Lua
Tutorial

在上面的例子中,我們可以看到elementIterator內部還有另一個方法,它使用區域性外部變數index和count,透過在每次呼叫函式時遞增index來返回集合中的每個元素。

我們可以像上面那樣使用閉包建立我們自己的函式迭代器,並且它可以在每次迭代集合時返回多個元素。

Lua - 表

簡介

表是Lua中唯一可用的資料結構,它幫助我們建立陣列和字典等不同型別。Lua使用關聯陣列,可以用數字和字串索引,除了nil。表沒有固定大小,可以根據我們的需要增長。

Lua在所有表示中都使用表,包括包的表示。當我們訪問方法string.format時,這意味著我們正在訪問string包中可用的format函式。

表示和用法

表被稱為物件,它們既不是值也不是變數。Lua使用構造表示式{}來建立一個空表。需要注意的是,持有表引用的變數和表本身之間沒有固定的關係。

--sample table initialization
mytable = {}

--simple table value assignment
mytable[1]= "Lua"

--removing reference
mytable = nil

-- lua garbage collection will take care of releasing memory

當我們有一個包含一組元素的表a,並且我們將它賦值給b時,ab都引用相同的記憶體。不會為b單獨分配單獨的記憶體。當a設定為nil時,表仍然可以透過b訪問。當沒有對錶的引用時,Lua中的垃圾收集負責清理過程,使這些未引用的記憶體可以再次重用。

下面給出一個例子來解釋上面提到的表的特性。

-- Simple empty table
mytable = {}
print("Type of mytable is ",type(mytable))

mytable[1]= "Lua"
mytable["wow"] = "Tutorial"

print("mytable Element at index 1 is ", mytable[1])
print("mytable Element at index wow is ", mytable["wow"])

-- alternatetable and mytable refers to same table
alternatetable = mytable

print("alternatetable Element at index 1 is ", alternatetable[1])
print("alternatetable Element at index wow is ", alternatetable["wow"])

alternatetable["wow"] = "I changed it"

print("mytable Element at index wow is ", mytable["wow"])

-- only variable released and and not table
alternatetable = nil
print("alternatetable is ", alternatetable)

-- mytable is still accessible
print("mytable Element at index wow is ", mytable["wow"])

mytable = nil
print("mytable is ", mytable)

執行上面的程式,我們將得到以下輸出:

Type of mytable is 	table
mytable Element at index 1 is 	Lua
mytable Element at index wow is 	Tutorial
alternatetable Element at index 1 is 	Lua
alternatetable Element at index wow is 	Tutorial
mytable Element at index wow is 	I changed it
alternatetable is 	nil
mytable Element at index wow is 	I changed it
mytable is 	nil

表操作

有一些內建函式用於表操作,它們列在下面的表中。

Lua提供以下型別的迴圈來處理迴圈需求。 方法及用途
1

table.concat (table [, sep [, i [, j]]])

根據給定的引數連線表中的字串。詳情請參見示例。

2

table.insert (table, [pos,] value)

在指定位置將值插入到表中。

3

table.maxn (table)

返回最大的數字索引。

4

table.remove (table [, pos])

從表中刪除值。

5

table.sort (table [, comp])

根據可選的比較器引數對錶進行排序。

讓我們看看上面一些函式的示例。

表連線

我們可以使用concat函式連線兩個表,如下所示:

fruits = {"banana","orange","apple"}

-- returns concatenated string of table
print("Concatenated string ",table.concat(fruits))

--concatenate with a character
print("Concatenated string ",table.concat(fruits,", "))

--concatenate fruits based on index
print("Concatenated string ",table.concat(fruits,", ", 2,3))

執行上面的程式,我們將得到以下輸出:

Concatenated string 	bananaorangeapple
Concatenated string 	banana, orange, apple
Concatenated string 	orange, apple

插入和刪除

在表操作中最常見的是表中專案的插入和刪除。解釋如下。

fruits = {"banana","orange","apple"}

-- insert a fruit at the end
table.insert(fruits,"mango")
print("Fruit at index 4 is ",fruits[4])

--insert fruit at index 2
table.insert(fruits,2,"grapes")
print("Fruit at index 2 is ",fruits[2])

print("The maximum elements in table is",table.maxn(fruits))

print("The last element is",fruits[5])

table.remove(fruits)
print("The previous last element is",fruits[5])

執行上面的程式,我們將得到以下輸出:

Fruit at index 4 is 	mango
Fruit at index 2 is 	grapes
The maximum elements in table is	5
The last element is	mango
The previous last element is	nil

排序表

我們經常需要按特定順序對錶進行排序。sort函式按字母順序對錶中的元素進行排序。下面是一個示例。

fruits = {"banana","orange","apple","grapes"}

for k,v in ipairs(fruits) do
   print(k,v)
end

table.sort(fruits)
print("sorted table")

for k,v in ipairs(fruits) do
   print(k,v)
end

執行上面的程式,我們將得到以下輸出:

1	banana
2	orange
3	apple
4	grapes
sorted table
1	apple
2	banana
3	grapes
4	orange

Lua - 模組

什麼是模組?

模組就像一個可以使用require載入的庫,它包含一個包含表的單個全域性名稱。這個模組可以包含許多函式和變數。所有這些函式和變數都被包裝到表中,該表充當名稱空間。此外,一個表現良好的模組有必要的規定來在require時返回這個表。

Lua模組的特性

在模組中使用表以多種方式幫助我們,並使我們能夠以與操作任何其他Lua表相同的方式操作模組。由於能夠操作模組,它提供了其他語言需要特殊機制的額外功能。由於Lua中模組的這種自由機制,使用者可以用多種方式呼叫Lua中的函式。下面列出其中一些。

-- Assuming we have a module printFormatter
-- Also printFormatter has a funtion simpleFormat(arg)
-- Method 1
require "printFormatter"
printFormatter.simpleFormat("test")

-- Method 2
local formatter = require "printFormatter"
formatter.simpleFormat("test")

-- Method 3
require "printFormatter"
local formatterFunction = printFormatter.simpleFormat
formatterFunction("test")

在上面的示例程式碼中,您可以看到Lua程式設計的靈活性,無需任何特殊的附加程式碼。

require函式

Lua提供了一個高階函式require來載入所有必要的模組。它儘可能保持簡單,以避免載入模組時有太多關於模組的資訊。require函式只是將模組視為定義某些值的程式碼塊,實際上是函式或包含函式的表。

示例

讓我們考慮一個簡單的例子,其中一個函式具有數學函式。讓我們將此模組稱為mymath,檔名是mymath.lua。檔案內容如下:

local mymath =  {}

function mymath.add(a,b)
   print(a+b)
end

function mymath.sub(a,b)
   print(a-b)
end

function mymath.mul(a,b)
   print(a*b)
end

function mymath.div(a,b)
   print(a/b)
end

return mymath	

現在,為了在另一個檔案中訪問此Lua模組,例如moduletutorial.lua,您需要使用以下程式碼段。

mymathmodule = require("mymath")
mymathmodule.add(10,20)
mymathmodule.sub(30,20)
mymathmodule.mul(10,20)
mymathmodule.div(30,20)

為了執行此程式碼,我們需要將兩個Lua檔案放在同一個目錄中,或者您可以將模組檔案放在包路徑中,這需要額外的設定。執行上面的程式,我們將得到以下輸出。

30
10
200
1.5

需要注意的事項

  • 將模組和執行的檔案放在同一個目錄中。

  • 模組名稱和檔名應相同。

  • 最佳實踐是為require函式返回模組,因此即使您可以在其他地方找到其他型別的實現,也最好如上所示實現模組。

舊的模組實現方式

現在讓我以舊的方式重寫相同的示例,該方式使用package.seeall型別的實現。這在Lua 5.1和5.0版本中使用。mymath模組如下所示。

module("mymath", package.seeall)

function mymath.add(a,b)
   print(a+b)
end

function mymath.sub(a,b)
   print(a-b)
end

function mymath.mul(a,b)
   print(a*b)
end

function mymath.div(a,b)
   print(a/b)
end

moduletutorial.lua中模組的使用如下所示。

require("mymath")
mymath.add(10,20)
mymath.sub(30,20)
mymath.mul(10,20)
mymath.div(30,20)

執行上面的程式,我們將得到相同的輸出。但是建議不要使用舊版本的程式碼,因為它被認為安全性較低。許多使用Lua進行程式設計的SDK,如Corona SDK,已經棄用了這種用法。

Lua - 元表

元表是一個表,它可以幫助修改與其關聯的表的行為,方法是使用金鑰集和相關的元方法。這些元方法是強大的Lua功能,可以啟用以下功能:

  • 更改/新增表的運算子功能。

  • 當表中沒有鍵時,使用元表中的__index查詢元表。

在處理元表時,使用兩種重要的方法:

  • setmetatable(table,metatable) − 此方法用於為表設定元表。

  • getmetatable(table) − 此方法用於獲取表的元表。

讓我們首先看看如何將一個表設定為另一個表的元表。如下所示。

mytable = {}
mymetatable = {}
setmetatable(mytable,mymetatable)

上面的程式碼可以表示為一行,如下所示。

mytable = setmetatable({},{})

_index

下面是一個簡單的元表示例,用於在表中找不到元表時查詢元表。

mytable = setmetatable({key1 = "value1"}, {
   __index = function(mytable, key)
	
      if key == "key2" then
         return "metatablevalue"
      else
         return mytable[key]
      end
   end
})

print(mytable.key1,mytable.key2)

執行上述程式後,我們將得到以下輸出。

value1 metatablevalue

讓我們逐步解釋上面示例中發生的情況。

  • 這裡的mytable表為{key1 = "value1"}

  • 為mytable設定元表,其中包含__index函式,我們將其稱為元方法。

  • 元方法的工作很簡單,查詢索引“key2”,如果找到,則返回“metatablevalue”,否則返回mytable中對應索引的值。

我們可以簡化上面的程式,如下所示。

mytable = setmetatable({key1 = "value1"}, 
   { __index = { key2 = "metatablevalue" } })
print(mytable.key1,mytable.key2)

__newindex

當我們將__newindex新增到元表時,如果表中沒有鍵,則新鍵的行為將由元方法定義。下面是一個簡單的示例,當主表中沒有索引時,設定元表的索引。

mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })

print(mytable.key1)

mytable.newkey = "new value 2"
print(mytable.newkey,mymetatable.newkey)

mytable.key1 = "new  value 1"
print(mytable.key1,mymetatable.newkey1)

執行上面的程式,您將得到以下輸出。

value1
nil	new value 2
new  value 1	nil

您可以在上面的程式中看到,如果鍵存在於主表中,它只會更新它。當主表中沒有鍵時,它會將該鍵新增到元表。

另一個使用rawset函式更新同一個表的示例如下所示。

mytable = setmetatable({key1 = "value1"}, {

   __newindex = function(mytable, key, value)
      rawset(mytable, key, "\""..value.."\"")
   end
})

mytable.key1 = "new value"
mytable.key2 = 4

print(mytable.key1,mytable.key2)

執行上面的程式,我們將得到以下輸出。

new value	"4"

rawset在不使用元表的__newindex的情況下設定值。類似地,rawget在不使用__index的情況下獲取值。

向表新增運算子行為

下面是一個使用+運算子組合兩個表的簡單示例:

mytable = setmetatable({ 1, 2, 3 }, {
   __add = function(mytable, newtable)
	
      for i = 1, table.maxn(newtable) do
         table.insert(mytable, table.maxn(mytable)+1,newtable[i])
      end
      return mytable
   end
})

secondtable = {4,5,6}

mytable = mytable + secondtable

for k,v in ipairs(mytable) do
   print(k,v)
end

執行上述程式後,我們將得到以下輸出。

1	1
2	2
3	3
4	4
5	5
6	6

__add鍵包含在元表中以新增+運算子的行為。鍵和相應運算子的表如下所示。

Lua提供以下型別的迴圈來處理迴圈需求。 模式和描述
1

__add

更改'+'運算子的行為。

2

__sub

更改'-'運算子的行為。

3

__mul

更改'*'運算子的行為。

4

__div

更改'/'運算子的行為。

5

__mod

更改'%'運算子的行為。

6

__unm

更改'-'運算子的行為。

7

__concat

更改'..'運算子的行為。

8

__eq

更改'=='運算子的行為。

9

__lt

更改'<'運算子的行為。

10

__le

更改'<='運算子的行為。

__call

使用__call語句新增方法呼叫的行為。一個簡單的示例,返回主表中值與傳遞表的總和。

mytable = setmetatable({10}, {
   __call = function(mytable, newtable)
   sum = 0
	
      for i = 1, table.maxn(mytable) do
         sum = sum + mytable[i]
      end
	
      for i = 1, table.maxn(newtable) do
         sum = sum + newtable[i]
      end
	
      return sum
   end
})

newtable = {10,20,30}
print(mytable(newtable))

執行上述程式後,我們將得到以下輸出。

70

__tostring

要更改print語句的行為,我們可以使用__tostring元方法。下面是一個簡單的示例。

mytable = setmetatable({ 10, 20, 30 }, {
   __tostring = function(mytable)
   sum = 0
	
      for k, v in pairs(mytable) do
         sum = sum + v
      end
		
      return "The sum of values in the table is " .. sum
   end
})
print(mytable)

執行上述程式後,我們將得到以下輸出。

The sum of values in the table is 60

如果您充分了解元表的強大功能,您可以輕鬆執行許多如果沒有元表則非常複雜的操作。因此,請嘗試更多地使用元表及其提供的各種選項(如示例中所述),並建立您自己的示例。

Lua - 協程

簡介

協程本質上是協作式的,允許兩種或多種方法以受控方式執行。使用協程時,在任何給定時間,只有一個協程執行,並且此執行協程只有在其明確請求掛起時才會掛起其執行。

上述定義可能看起來比較模糊。讓我們假設我們有兩個方法,一個是主程式方法,另一個是協程。當我們使用 resume 函式呼叫協程時,它開始執行;當我們呼叫 yield 函式時,它掛起執行。同一個協程可以使用另一個 resume 函式呼叫從其掛起的地方繼續執行。此過程可以一直持續到協程執行結束。

協程中可用的函式

下表列出了 Lua 中所有可用的協程函式及其對應的用途。

Lua提供以下型別的迴圈來處理迴圈需求。 方法及用途
1

coroutine.create (f)

使用函式 f 建立一個新的協程,並返回一個“執行緒”型別的物件。

2

coroutine.resume (co [, val1, ...])

恢復協程 co 並傳遞任何引數(如有)。它返回操作的狀態和可選的其他返回值。

3

coroutine.running ()

返回正在執行的協程,如果在主執行緒中呼叫則返回 nil。

4

coroutine.status (co)

根據協程的狀態,返回 running、normal、suspended 或 dead 中的一個值。

5

coroutine.wrap (f)

與 coroutine.create 類似,coroutine.wrap 函式也建立一個協程,但它不返回協程本身,而是返回一個函式,當呼叫該函式時,它會恢復協程。

6

coroutine.yield (...)

掛起正在執行的協程。傳遞給此方法的引數充當 resume 函式的附加返回值。

示例

讓我們來看一個例子來理解協程的概念。

co = coroutine.create(function (value1,value2)
   local tempvar3 = 10
   print("coroutine section 1", value1, value2, tempvar3)
	
   local tempvar1 = coroutine.yield(value1+1,value2+1)
   tempvar3 = tempvar3 + value1
   print("coroutine section 2",tempvar1 ,tempvar2, tempvar3)
	
   local tempvar1, tempvar2= coroutine.yield(value1+value2, value1-value2)
   tempvar3 = tempvar3 + value1
   print("coroutine section 3",tempvar1,tempvar2, tempvar3)
   return value2, "end"
	
end)

print("main", coroutine.resume(co, 3, 2))
print("main", coroutine.resume(co, 12,14))
print("main", coroutine.resume(co, 5, 6))
print("main", coroutine.resume(co, 10, 20))

執行上述程式後,我們將得到以下輸出。

coroutine section 1	3	2	10
main	true	4	3
coroutine section 2	12	nil	13
main	true	5	1
coroutine section 3	5	6	16
main	true	2	end
main	false	cannot resume dead coroutine

上述示例做了什麼?

如前所述,我們使用 resume 函式啟動操作,使用 yield 函式停止操作。此外,您可以看到協程的 resume 函式接收多個返回值。

  • 首先,我們建立一個協程並將其賦值給變數名 co,協程接收兩個變數作為其引數。

  • 當我們呼叫第一個 resume 函式時,值 3 和 2 會保留在臨時變數 value1 和 value2 中,直到協程結束。

  • 為了讓您理解這一點,我們使用了 tempvar3,它最初為 10,由於 value1 在協程執行過程中始終保持為 3,因此它通過後續的協程呼叫更新為 13 和 16。

  • 第一個 coroutine.yield 將兩個值 4 和 3 返回給 resume 函式,我們透過更新 yield 語句中的輸入引數 3 和 2 來獲得這些值。它還接收協程執行的真/假狀態。

  • 關於協程的另一個方面是如何處理 resume 呼叫的下一個引數,在上面的示例中;您可以看到變數 coroutine.yield 接收下一個呼叫的引數,這提供了一種強大的方法來使用現有引數值的關係執行新操作。

  • 最後,一旦協程中的所有語句都執行完畢,後續呼叫將返回 false 和“無法恢復已終止的協程”語句作為響應。

另一個協程示例

讓我們來看一個簡單的協程,它使用 yield 函式和 resume 函式返回 1 到 5 的數字。如果不存在協程,則建立協程;否則恢復現有協程。

function getNumber()
   local function getNumberHelper()
      co = coroutine.create(function ()
      coroutine.yield(1)
      coroutine.yield(2)
      coroutine.yield(3)
      coroutine.yield(4)
      coroutine.yield(5)
      end)
      return co
   end
	
   if(numberHelper) then
      status, number = coroutine.resume(numberHelper);
		
      if coroutine.status(numberHelper) == "dead" then
         numberHelper = getNumberHelper()
         status, number = coroutine.resume(numberHelper);
      end
		
      return number
   else
      numberHelper = getNumberHelper()
      status, number = coroutine.resume(numberHelper);
      return number
   end
	
end

for index = 1, 10 do
   print(index, getNumber())
end

執行上述程式後,我們將得到以下輸出。

1	1
2	2
3	3
4	4
5	5
6	1
7	2
8	3
9	4
10	5

協程經常與多程式語言的執行緒進行比較,但我們需要理解,協程具有與執行緒類似的功能,但它們一次只執行一個,並且永遠不會併發執行。

我們控制程式執行順序以滿足需求,並暫時保留某些資訊。將全域性變數與協程一起使用可以為協程提供更大的靈活性。

Lua - 檔案 I/O

I/O 庫用於在 Lua 中讀取和操作檔案。Lua 中有兩種檔案操作,即隱式檔案描述符和顯式檔案描述符。

對於以下示例,我們將使用如下所示的示例檔案 test.lua。

-- sample test.lua
-- sample2 test.lua

簡單的檔案開啟操作使用以下語句。

file = io.open (filename [, mode])

下表列出了各種檔案模式。

Lua提供以下型別的迴圈來處理迴圈需求。 模式和描述
1

"r"

只讀模式,也是預設模式,其中開啟現有檔案。

2

"w"

啟用寫入模式,該模式會覆蓋現有檔案或建立新檔案。

3

"a"

追加模式,開啟現有檔案或建立新檔案以進行追加。

4

"r+"

現有檔案的讀寫模式。

5

"w+"

如果檔案存在,則刪除所有現有資料;或者建立具有讀寫許可權的新檔案。

6

"a+"

啟用讀模式的追加模式,開啟現有檔案或建立新檔案。

隱式檔案描述符

隱式檔案描述符使用標準輸入/輸出模式,或者使用單個輸入和單個輸出檔案。使用隱式檔案描述符的示例如下所示。

-- Opens a file in read
file = io.open("test.lua", "r")

-- sets the default input file as test.lua
io.input(file)

-- prints the first line of the file
print(io.read())

-- closes the open file
io.close(file)

-- Opens a file in append mode
file = io.open("test.lua", "a")

-- sets the default output file as test.lua
io.output(file)

-- appends a word test to the last line of the file
io.write("-- End of the test.lua file")

-- closes the open file
io.close(file)

執行程式時,您將獲得 test.lua 檔案第一行的輸出。對於我們的程式,我們獲得了以下輸出。

-- Sample test.lua

這是 test.lua 檔案中我們語句的第一行。此外,"-- End of the test.lua file" 將被追加到 test.lua 程式碼的最後一行。

在上面的示例中,您可以看到隱式描述符如何使用 io."x" 方法與檔案系統一起工作。上面的示例使用 io.read() 沒有可選引數。可選引數可以是以下任何一個。

Lua提供以下型別的迴圈來處理迴圈需求。 模式和描述
1

"*n"

從當前檔案位置讀取,如果檔案位置存在數字則返回該數字,否則返回 nil。

2

"*a"

從當前檔案位置返回檔案的全部內容。

3

"*l"

從當前檔案位置讀取行,並將檔案位置移動到下一行。

4

number

讀取函式中指定的位元組數。

其他常見的 I/O 方法包括:

  • io.tmpfile() − 返回一個用於讀取和寫入的臨時檔案,該檔案將在程式退出時刪除。

  • io.type(file) − 根據輸入檔案返回檔案、已關閉的檔案或 nil。

  • io.flush() − 清空預設輸出緩衝區。

  • io.lines(可選檔名) − 提供一個通用的for迴圈迭代器,該迭代器遍歷檔案並在最後關閉檔案(如果提供了檔名或使用了預設檔案,並且在迴圈結束時未關閉)。

顯式檔案描述符

我們經常使用顯式檔案描述符,它允許我們一次操作多個檔案。這些函式與隱式檔案描述符非常相似。在這裡,我們使用 file:function_name 代替 io.function_name。以下示例顯示了相同隱式檔案描述符示例的檔案版本。

-- Opens a file in read mode
file = io.open("test.lua", "r")

-- prints the first line of the file
print(file:read())

-- closes the opened file
file:close()

-- Opens a file in append mode
file = io.open("test.lua", "a")

-- appends a word test to the last line of the file
file:write("--test")

-- closes the open file
file:close()

執行程式時,您將獲得與隱式描述符示例類似的輸出。

-- Sample test.lua

外部描述符的檔案開啟的所有模式和讀取引數與隱式檔案描述符相同。

其他常見的檔案方法包括:

  • file:seek(可選 whence,可選 offset) − Whence 引數為“set”、“cur”或“end”。使用從檔案開頭更新的檔案位置設定新的檔案指標。在此函式中,偏移量為基於零的。如果第一個引數為“set”,則偏移量是從檔案開頭測量的;如果為“cur”,則從檔案中的當前位置測量;如果為“end”,則從檔案結尾測量。預設引數值為“cur”和 0,因此可以透過不帶引數呼叫此函式來獲得當前檔案位置。

  • file:flush() − 清空預設輸出緩衝區。

  • io.lines(可選檔名) − 提供一個通用的for迴圈迭代器,該迭代器遍歷檔案並在最後關閉檔案(如果提供了檔名或使用了預設檔案,並且在迴圈結束時未關閉)。

使用 seek 方法的示例如下所示。它將游標偏移到檔案結尾之前的 25 個位置。read 函式列印從 seek 位置開始的檔案其餘部分。

-- Opens a file in read
file = io.open("test.lua", "r")

file:seek("end",-25)
print(file:read("*a"))

-- closes the opened file
file:close()

您將獲得一些類似於以下內容的輸出。

sample2 test.lua
--test

您可以嘗試所有不同的模式和引數,以瞭解 Lua 檔案操作的全部功能。

Lua - 錯誤處理

錯誤處理的必要性

錯誤處理非常關鍵,因為現實世界中的操作通常需要使用複雜的操作,包括檔案操作、資料庫事務和 Web 服務呼叫。

在任何程式設計中,都需要進行錯誤處理。錯誤可以分為兩種型別,包括:

  • 語法錯誤
  • 執行時錯誤

語法錯誤

語法錯誤是由於不正確地使用各種程式元件(如運算子和表示式)造成的。語法錯誤的一個簡單示例如下所示。

a == 2

如您所知,“等於”和“等於等於”的使用之間存在差異。使用一個代替另一個可能會導致錯誤。“等於”是指賦值,“等於等於”是指比較。類似地,表示式和函式具有其預定義的實現方式。

另一個語法錯誤示例如下所示:

for a= 1,10
   print(a)
end

執行上面的程式,我們將得到以下輸出:

lua: test2.lua:2: 'do' expected near 'print'

語法錯誤比執行時錯誤更容易處理,因為 Lua 直譯器比執行時錯誤更清晰地定位錯誤。從上述錯誤中,我們可以很容易地知道,根據 Lua 結構,需要在 print 語句之前新增一個do語句。

執行時錯誤

對於執行時錯誤,程式可以成功執行,但由於輸入錯誤或函式處理不當,可能會導致執行時錯誤。一個簡單的執行時錯誤示例如下所示。

function add(a,b)
   return a+b
end

add(10)

當我們構建程式時,它將成功構建並執行。一旦執行,就會顯示執行時錯誤。

lua: test2.lua:2: attempt to perform arithmetic on local 'b' (a nil value)
stack traceback:
	test2.lua:2: in function 'add'
	test2.lua:5: in main chunk
	[C]: ?

這是一個執行時錯誤,這是由於沒有傳遞兩個變數造成的。期望引數b,但此處為 nil,從而產生錯誤。

Assert 和 Error 函式

為了處理錯誤,我們經常使用兩個函式:asserterror。一個簡單的示例如下所示。

local function add(a,b)
   assert(type(a) == "number", "a is not a number")
   assert(type(b) == "number", "b is not a number")
   return a+b
end

add(10)

執行上述程式時,我們將獲得以下錯誤輸出。

lua: test2.lua:3: b is not a number
stack traceback:
	[C]: in function 'assert'
	test2.lua:3: in function 'add'
	test2.lua:6: in main chunk
	[C]: ?

error (message [, level]) 終止最後一次呼叫的受保護函式,並將 message 作為錯誤訊息返回。此 error 函式永遠不會返回。通常,error 會在訊息開頭新增一些關於錯誤位置的資訊。level 引數指定如何獲取錯誤位置。使用 level 1(預設值),錯誤位置是呼叫 error 函式的位置。level 2 將錯誤指向呼叫 error 的函式的呼叫位置;依此類推。傳遞 level 0 將避免將錯誤位置資訊新增到訊息中。

pcall 和 xpcall

在 Lua 程式設計中,為了避免丟擲這些錯誤並進行錯誤處理,我們需要使用 pcall 或 xpcall 函式。

pcall (f, arg1, ...) 函式以保護模式呼叫請求的函式。如果函式 f 中發生某些錯誤,它不會丟擲錯誤。它只返回錯誤的狀態。下面顯示了一個使用 pcall 的簡單示例。

function myfunction ()
   n = n/nil
end

if pcall(myfunction) then
   print("Success")
else
	print("Failure")
end

執行上述程式後,我們將得到以下輸出。

Failure

xpcall (f, err) 函式呼叫請求的函式並設定錯誤處理程式。f 內部的任何錯誤都不會傳播;相反,xpcall 會捕獲錯誤,使用原始錯誤物件呼叫 err 函式,並返回狀態程式碼。

下面顯示了 xpcall 的一個簡單示例。

function myfunction ()
   n = n/nil
end

function myerrorhandler( err )
   print( "ERROR:", err )
end

status = xpcall( myfunction, myerrorhandler )
print( status)

執行上述程式後,我們將得到以下輸出。

ERROR:	test2.lua:2: attempt to perform arithmetic on global 'n' (a nil value)
false

作為程式設計師,最重要的是確保您在編寫的程式中處理好適當的錯誤處理。使用錯誤處理可以確保處理超出邊界條件的意外情況,而不會干擾程式的使用者。

Lua - 除錯

Lua 提供了一個除錯庫,該庫為我們提供了建立自己的偵錯程式所需的所有基本函式。儘管沒有內建的 Lua 偵錯程式,但我們有許多由不同開發者建立的 Lua 偵錯程式,其中許多是開源的。

Lua 除錯庫中可用的函式及其用途列在下表中。

Lua提供以下型別的迴圈來處理迴圈需求。 方法及用途
1

debug()

進入互動式除錯模式,該模式一直處於活動狀態,直到我們在某一行鍵入 cont 並按 Enter 鍵為止。使用者可以在此模式下使用其他函式檢查變數。

2

getfenv(object)

返回物件的執行環境。

3

gethook(optional thread)

返回執行緒的當前鉤子設定,作為三個值:當前鉤子函式、當前鉤子掩碼和當前鉤子計數。

4

getinfo(optional thread, function or stack level, optional flag)

返回一個包含函式資訊的表。您可以直接提供函式,也可以提供一個數字作為函式的值,這意味著在給定執行緒的呼叫堆疊的函式級別執行的函式——級別 0 是當前函式(getinfo 本身);級別 1 是呼叫 getinfo 的函式;以此類推。如果函式的數字大於活動函式的數量,則 getinfo 返回 nil。

5

getlocal(optional thread, stack level, local index)

返回堆疊級別 level 的函式中索引為 local 的區域性變數的名稱和值。如果不存在具有給定索引的區域性變數,則返回 nil;如果使用超出範圍的級別呼叫,則會引發錯誤。

6

getmetatable(value)

返回給定物件的元表,如果它沒有元表則返回 nil。

7

getregistry()

返回登錄檔,一個預定義的表,任何 C 程式碼都可以使用它來儲存任何 Lua 值。

8

getupvalue(function, upvalue index)

此函式返回函式 func 中索引為 up 的 upvalue 的名稱和值。如果不存在具有給定索引的 upvalue,則該函式返回 nil。

9

setfenv(function or thread or userdata, environment table)

將給定物件的執行環境設定為給定的表。返回物件。

10

sethook(optional thread, hook function, hook mask string with "c" and/or "r" and/or "l", optional instruction count)

將給定函式設定為鉤子。字串掩碼和計數數字描述了何時呼叫鉤子。這裡,c、r 和 l 分別在 Lua 呼叫、返回和進入函式的每一行程式碼時被呼叫。

11

setlocal(optional thread, stack level, local index, value)

將值賦給堆疊級別 level 的函式中索引為 local 的區域性變數。如果不存在具有給定索引的區域性變數,則該函式返回 nil;如果使用超出範圍的級別呼叫,則會引發錯誤。否則,它返回區域性變數的名稱。

12

setmetatable(value, metatable)

將給定物件的元表設定為給定的表(可以為 nil)。

13

setupvalue(function, upvalue index, value)

此函式將值賦給函式 func 中索引為 up 的 upvalue。如果不存在具有給定索引的 upvalue,則該函式返回 nil。否則,它返回 upvalue 的名稱。

14

traceback(optional thread, optional message string, optional level argument)

構建包含回溯的擴充套件錯誤訊息。

以上列表是 Lua 中除錯函式的完整列表,我們經常使用一個使用上述函式並提供更輕鬆除錯的庫。使用這些函式並建立我們自己的偵錯程式相當複雜,並不推薦。無論如何,我們將看到一個簡單使用除錯函式的示例。

function myfunction ()
   print(debug.traceback("Stack trace"))
   print(debug.getinfo(1))
   print("Stack trace end")

   return 10
end

myfunction ()
print(debug.getinfo(1))

執行上述程式時,我們將得到如下所示的堆疊跟蹤。

Stack trace
stack traceback:
	test2.lua:2: in function 'myfunction'
	test2.lua:8: in main chunk
	[C]: ?
table: 0054C6C8
Stack trace end

在上面的示例程式中,堆疊跟蹤是透過使用除錯庫中提供的 debug.trace 函式列印的。debug.getinfo 獲取函式的當前表。

除錯 - 示例

我們經常需要知道函式的區域性變數才能進行除錯。為此,我們可以使用 getupvalue,要設定這些區域性變數,我們使用 setupvalue。下面顯示了一個簡單的示例。

function newCounter ()
   local n = 0
   local k = 0
	
   return function ()
      k = n
      n = n + 1
      return n
   end
	
end

counter = newCounter ()

print(counter())
print(counter())

local i = 1

repeat
   name, val = debug.getupvalue(counter, i)
	
   if name then
      print ("index", i, name, "=", val)
		
      if(name == "n") then
         debug.setupvalue (counter,2,10)
      end
		
      i = i + 1
   end -- if
	
until not name

print(counter())

執行上述程式後,我們將得到以下輸出。

1
2
index	1	k	=	1
index	2	n	=	2
11

在這個例子中,計數器每次呼叫時都會增加一。我們可以使用 getupvalue 函式檢視區域性變數的當前狀態。然後我們將區域性變數設定為一個新值。這裡,在呼叫 set 操作之前,n 為 2。使用 setupvalue 函式,它被更新為 10。現在當我們呼叫計數器函式時,它將返回 11 而不是 3。

除錯型別

  • 命令列除錯
  • 圖形化除錯

命令列除錯

命令列除錯是一種使用命令列和列印語句進行除錯的除錯型別。Lua 有許多可用的命令列偵錯程式,其中一些列在下面。

  • RemDebug - RemDebug 是 Lua 5.0 和 5.1 的遠端偵錯程式。它允許您遠端控制另一個 Lua 程式的執行,設定斷點並檢查程式的當前狀態。RemDebug 還可以除錯 CGILua 指令碼。

  • clidebugger - 一個用純 Lua 編寫的 Lua 5.1 的簡單命令列介面偵錯程式。它不依賴於標準 Lua 5.1 庫以外的任何東西。它受到 RemDebug 的啟發,但沒有它的遠端功能。

  • ctrace - 用於跟蹤 Lua API 呼叫的工具。

  • xdbLua - Windows 平臺的簡單 Lua 命令列偵錯程式。

  • LuaInterface - Debugger - 此專案是 LuaInterface 的偵錯程式擴充套件。它將內建的 Lua 除錯介面提升到更高的級別。與偵錯程式的互動是透過事件和方法呼叫完成的。

  • Rldb - 這是一個透過套接字進行遠端 Lua 除錯的工具,可在 Windows 和 Linux 上使用。它可以提供比任何現有工具都多的功能。

  • ModDebug - 這允許遠端控制另一個 Lua 程式的執行,設定斷點並檢查程式的當前狀態。

圖形化除錯

藉助 IDE 可以進行圖形化除錯,其中您可以對各種狀態(如變數值、堆疊跟蹤和其他相關資訊)進行視覺化除錯。藉助 IDE 中的斷點、單步進入、單步跳過和其他按鈕,可以對執行進行視覺化表示和逐步控制。

Lua 有許多圖形化偵錯程式,包括以下這些。

  • SciTE - Lua 的預設 Windows IDE 提供多個除錯功能,例如斷點、單步、單步進入、單步跳過、監視變數等等。

  • Decoda - 這是一個具有遠端除錯支援的圖形化偵錯程式。

  • ZeroBrane Studio - 集成了遠端偵錯程式、堆疊檢視、監視檢視、遠端控制檯、靜態分析器等的 Lua IDE。適用於 LuaJIT、Love2d、Moai 和其他 Lua 引擎;Windows、OSX 和 Linux。開源。

  • akdebugger - Eclipse 的偵錯程式和編輯器 Lua 外掛。

  • luaedit - 它具有遠端除錯、本地除錯、語法高亮、程式碼補全列表、引數建議引擎、高階斷點管理(包括斷點上的條件系統和命中計數)、函式列表、全域性和區域性變數列表、監視、面向解決方案的管理等功能。

Lua - 垃圾回收

Lua 使用基於某些內建演算法的垃圾收集的自動記憶體管理。由於自動記憶體管理,作為開發者:

  • 無需擔心為物件分配記憶體。
  • 無需在不再需要時釋放它們,除非將其設定為 nil。

Lua 使用一個垃圾收集器,該收集器會不時執行以收集不再可從 Lua 程式訪問的死物件。

所有物件(包括表、使用者資料、函式、執行緒、字串等)都受自動記憶體管理。Lua 使用增量標記和清除收集器,它使用兩個數字來控制其垃圾收集週期,即垃圾收集器暫停垃圾收集器步長乘數。這些值以百分比表示,值 100 通常在內部等於 1。

垃圾收集器暫停

垃圾收集器暫停用於控制垃圾收集器需要等待多長時間,然後 Lua 的自動記憶體管理再次呼叫它。小於 100 的值意味著 Lua 將不會等待下一個週期。同樣,此值的較高值會導致垃圾收集器速度較慢且性質不那麼積極。值為 200 表示收集器等待使用中的總記憶體加倍後再啟動新週期。因此,根據應用程式的性質和速度,可能需要更改此值才能在 Lua 應用程式中獲得最佳效能。

垃圾收集器步長乘數

此步長乘數控制 Lua 程式中垃圾收集器與記憶體分配的相對速度。較大的步長值將導致垃圾收集器更積極,並且還會增加垃圾收集每個增量步驟的步長。小於 100 的值通常會導致垃圾收集器無法完成其週期,通常不推薦。預設值為 200,這意味著垃圾收集器的執行速度是記憶體分配速度的兩倍。

垃圾收集器函式

作為開發者,我們確實可以對 Lua 中的自動記憶體管理進行一些控制。為此,我們有以下方法。

  • collectgarbage("collect") - 執行一次完整的垃圾收集週期。

  • collectgarbage("count") - 返回程式當前使用的記憶體量(以千位元組為單位)。

  • collectgarbage("restart") - 如果垃圾收集器已停止,則重新啟動它。

  • collectgarbage("setpause") - 將作為第二個引數給出的值除以 100 設定為垃圾收集器暫停變數。其用途如上所述。

  • collectgarbage("setstepmul") - 將作為第二個引數給出的值除以 100 設定為垃圾收集器步長乘數變數。其用途如上所述。

  • collectgarbage("step") - 執行一步垃圾收集。第二個引數越大,這一步就越大。如果觸發的步驟是垃圾收集週期的最後一步,則 collectgarbage 將返回 true。

  • collectgarbage("stop") - 如果垃圾收集器正在執行,則停止它。

下面顯示了一個使用垃圾收集器示例的簡單示例。

mytable = {"apple", "orange", "banana"}

print(collectgarbage("count"))

mytable = nil

print(collectgarbage("count"))

print(collectgarbage("collect"))

print(collectgarbage("count"))

執行以上程式後,我們將得到以下輸出。請注意,由於作業系統型別的不同以及 Lua 的自動記憶體管理功能,此結果會有所不同。

23.1455078125   149
23.2880859375   295
0
22.37109375     380

您可以在上述程式中看到,一旦垃圾回收完成,它就會減少使用的記憶體。但是,這並非強制性的。即使我們不呼叫它們,Lua 直譯器也會在預定義的時間段後自動在後期執行它們。

顯然,如果需要,我們可以使用這些函式來更改垃圾收集器的行為。這些函式為開發人員提供了處理複雜情況的額外能力。根據程式的記憶體需求型別,您可以使用或不使用此功能。但是,瞭解應用程式中的記憶體使用情況並在程式設計過程中檢查它以避免部署後出現意外結果非常有用。

Lua - 面向物件

面向物件程式設計簡介

面向物件程式設計 (OOP) 是現代程式設計時代最常用的程式設計技術之一。許多程式語言都支援 OOP,包括:

  • C++
  • Java
  • Objective-C
  • Smalltalk
  • C#
  • Ruby

OOP 的特性

  • − 類是建立物件的可擴充套件模板,提供狀態(成員變數)的初始值和行為的實現。

  • 物件 − 它是類的例項,併為此自身分配了單獨的記憶體。

  • 繼承 − 這是一種概念,一個類的變數和函式可以被另一個類繼承。

  • 封裝 − 它是將資料和函式組合到一個類中的過程。可以使用函式從類外部訪問資料。它也稱為資料抽象。

Lua 中的 OOP

您可以藉助 Lua 的表和一等函式在 Lua 中實現面向物件。透過將函式和相關資料放入表中,形成一個物件。繼承可以藉助元表來實現,為父物件中不存在的函式(方法)和欄位提供查詢機制。

Lua 中的表具有類似於物件的狀態和標識的特性,這些特性與其值無關。具有相同值的兩個物件(表)是不同的物件,而一個物件可以在不同時間具有不同的值,但它始終是同一個物件。與物件一樣,表的生命週期與其建立者或建立位置無關。

一個真實的例子

面向物件的概念被廣泛使用,但您需要清楚地理解它才能獲得最佳益處。

讓我們考慮一個簡單的數學例子。我們經常遇到處理不同形狀(如圓形、矩形和正方形)的情況。

這些形狀可以具有共同的屬性“面積”。因此,我們可以從具有公共屬性“面積”的基物件“形狀”擴充套件其他形狀。每個形狀都可以有其自身的屬性和函式,例如矩形可以具有屬性長度、寬度、面積以及函式 printArea 和 calculateArea。

建立簡單的類

下面顯示了一個具有三個屬性(面積、長度和寬度)的矩形的簡單類實現。它還有一個 printArea 函式來列印計算出的面積。

-- Meta class
Rectangle = {area = 0, length = 0, breadth = 0}

-- Derived class method new

function Rectangle:new (o,length,breadth)
   o = o or {}
   setmetatable(o, self)
   self.__index = self
   self.length = length or 0
   self.breadth = breadth or 0
   self.area = length*breadth;
   return o
end

-- Derived class method printArea

function Rectangle:printArea ()
   print("The area of Rectangle is ",self.area)
end

建立物件

建立物件是為類例項分配記憶體的過程。每個物件都有自己的記憶體,並共享公共類資料。

r = Rectangle:new(nil,10,20)

訪問屬性

我們可以使用點運算子訪問類中的屬性,如下所示:

print(r.length)

訪問成員函式

您可以使用冒號運算子和物件一起訪問成員函式,如下所示:

r:printArea()

記憶體被分配,並且設定初始值。初始化過程可以與其他面嚮物件語言中的建構函式進行比較。它只不過是一個允許設定值的函式,如上所示。

完整示例

讓我們看一個在 Lua 中使用面向物件的完整示例。

-- Meta class
Shape = {area = 0}

-- Base class method new

function Shape:new (o,side)
   o = o or {}
   setmetatable(o, self)
   self.__index = self
   side = side or 0
   self.area = side*side;
   return o
end

-- Base class method printArea

function Shape:printArea ()
   print("The area is ",self.area)
end

-- Creating an object
myshape = Shape:new(nil,10)

myshape:printArea()

執行上述程式後,您將得到以下輸出。

The area is 	100

Lua 中的繼承

繼承是從簡單基物件(如形狀)擴充套件到矩形、正方形等的過程。它經常在現實世界中用於共享和擴充套件基本屬性和函式。

讓我們來看一個簡單的類擴充套件。我們有一個如下所示的類。

-- Meta class
Shape = {area = 0}

-- Base class method new

function Shape:new (o,side)
   o = o or {}
   setmetatable(o, self)
   self.__index = self
   side = side or 0
   self.area = side*side;
   return o
end

-- Base class method printArea

function Shape:printArea ()
   print("The area is ",self.area)
end

我們可以將形狀擴充套件到正方形類,如下所示。

Square = Shape:new()

-- Derived class method new

function Square:new (o,side)
   o = o or Shape:new(o,side)
   setmetatable(o, self)
   self.__index = self
   return o
end

重寫基函式

我們可以重寫基類函式,即派生類可以使用其自身的實現來代替基類中的函式,如下所示:

-- Derived class method printArea

function Square:printArea ()
   print("The area of square is ",self.area)
end

繼承完整示例

我們可以使用元表透過另一種新方法擴充套件如上所示的 Lua 中的簡單類實現。基類中的所有成員變數和函式都保留在派生類中。

-- Meta class
Shape = {area = 0}

-- Base class method new

function Shape:new (o,side)
   o = o or {}
   setmetatable(o, self)
   self.__index = self
   side = side or 0
   self.area = side*side;
   return o
end

-- Base class method printArea

function Shape:printArea ()
   print("The area is ",self.area)
end

-- Creating an object
myshape = Shape:new(nil,10)
myshape:printArea()

Square = Shape:new()

-- Derived class method new

function Square:new (o,side)
   o = o or Shape:new(o,side)
   setmetatable(o, self)
   self.__index = self
   return o
end

-- Derived class method printArea

function Square:printArea ()
   print("The area of square is ",self.area)
end

-- Creating an object
mysquare = Square:new(nil,10)
mysquare:printArea()

Rectangle = Shape:new()

-- Derived class method new

function Rectangle:new (o,length,breadth)
   o = o or Shape:new(o)
   setmetatable(o, self)
   self.__index = self
   self.area = length * breadth
   return o
end

-- Derived class method printArea

function Rectangle:printArea ()
    print("The area of Rectangle is ",self.area)
end

-- Creating an object

myrectangle = Rectangle:new(nil,10,20)
myrectangle:printArea()

執行上面的程式,我們將得到以下輸出:

The area is 	100
The area of square is 	100
The area of Rectangle is 	200

在上面的例子中,我們從基類 Square 建立了兩個派生類——Rectangle 和 Square。可以在派生類中重寫基類的函式。在這個例子中,派生類重寫了函式 printArea。

Lua - Web程式設計

Lua 是一種高度靈活的語言,它經常用於多個平臺,包括 Web 應用程式。2004 年成立的 Kepler 社群致力於提供 Lua 中的開源 Web 元件。

儘管已經開發出其他使用 Lua 的 Web 框架,但我們將主要關注 Kepler 社群提供的元件。

應用程式和框架

  • Orbit 是一個基於 WSAPI 的 Lua MVC Web 框架。

  • WSAPI 是一個將 Web 宿主伺服器與 Lua Web 應用程式抽象出來的 API,它是許多專案的基石。

  • Xavante 是一個提供 WSAPI 介面的 Lua Web 伺服器。

  • Sputnik 是一個基於 Kepler 專案的 WSAPI 上開發的 wiki/CMS,用於幽默和娛樂。

  • CGILua 提供 LuaPages 和 LuaScripts 網頁建立,基於 WSAPI,但不再支援。請改用 Orbit、Sputnik 或 WSAPI。

在本教程中,我們將嘗試讓您瞭解 Lua 的功能,並瞭解有關其安裝和使用的更多資訊,請參考 Kepler 網站

Orbit

Orbit 是一個用於 Lua 的 MVC Web 框架。它完全放棄了 CGILua 的“指令碼”模型,轉而採用應用程式,其中每個 Orbit 應用程式都可以放在單個檔案中,但如果需要,您也可以將其拆分為多個檔案。

所有 Orbit 應用程式都遵循 WSAPI 協議,因此它們目前可與 Xavante、CGI 和 Fastcgi 一起使用。它包含一個啟動器,可以輕鬆啟動 Xavante 例項以進行開發。

安裝 Orbit 最簡單的方法是使用 LuaRocks。`luarocks install orbit` 是安裝命令。為此,您需要先安裝 LuaRocks

如果您尚未安裝所有依賴項,以下是設定 Unix/Linux 環境中 Orbit 的步驟。

安裝 Apache

連線到您的伺服器。安裝 Apache2、其支援模組並啟用所需的 Apache2 模組:

$ sudo apt-get install apache2 libapache2-mod-fcgid libfcgi-dev build-essential
$ sudo a2enmod rewrite
$ sudo a2enmod fcgid
$ sudo /etc/init.d/apache2 force-reload

安裝 LuaRocks

$ sudo apt-get install luarocks

安裝 WSAPI、FCGI、Orbit 和 Xavante

$ sudo luarocks install orbit
$ sudo luarocks install wsapi-xavante
$ sudo luarocks install wsapi-fcgi

設定 Apache2

$ sudo raj /etc/apache2/sites-available/default

在配置檔案的 `` 部分下方新增以下部分。如果此部分具有“AllowOverride None”,則需要將“None”更改為“All”,以便 .htaccess 檔案可以在本地覆蓋配置。

<IfModule mod_fcgid.c>

   AddHandler fcgid-script .lua
   AddHandler fcgid-script .ws
   AddHandler fcgid-script .op
	
   FCGIWrapper "/usr/local/bin/wsapi.fcgi" .ws
   FCGIWrapper "/usr/local/bin/wsapi.fcgi" .lua
   FCGIWrapper "/usr/local/bin/op.fcgi" .op
	
   #FCGIServer "/usr/local/bin/wsapi.fcgi" -idle-timeout 60 -processes 1
   #IdleTimeout 60
   #ProcessLifeTime 60
	
</IfModule>

重新啟動伺服器以確保所做的更改生效。

要啟用您的應用程式,您需要將 `+ExecCGI` 新增到 Orbit 應用程式根目錄中的 .htaccess 檔案中——在本例中為 `/var/www`。

Options +ExecCGI
DirectoryIndex index.ws

簡單示例 - Orbit

#!/usr/bin/env index.lua

-- index.lua
require"orbit"

-- declaration
module("myorbit", package.seeall, orbit.new)

-- handler

function index(web)
   return my_home_page()
end

-- dispatch
myorbit:dispatch_get(index, "/", "/index")

-- Sample page

function my_home_page()

   return [[
      <head></head>
      <html>
         <h2>First Page</h2>
      </html>
   ]]
	
end

現在,您應該能夠啟動您的 Web 瀏覽器。訪問 https://:8080/,您應該看到以下輸出:

First Page

Orbit 提供了另一種選擇,即 Lua 程式碼可以生成 html。

#!/usr/bin/env index.lua

-- index.lua
require"orbit"

function generate()
   return html {
      head{title "HTML Example"},
		
      body{
         h2{"Here we go again!"}
      }
   }
end

orbit.htmlify(generate)

print(generate())

建立表單

下面顯示了一個簡單的表單示例:

#!/usr/bin/env index.lua
require"orbit"

function wrap (inner)
   return html{ head(), body(inner) }
end

function test ()
   return wrap(form (H'table' {
      tr{td"First name",td( input{type = 'text', name='first'})},
      tr{td"Second name",td(input{type = 'text', name='second'})},
      tr{ td(input{type = 'submit', value = 'Submit!'}),
         td(input{type = 'submit',value = 'Cancel'})
      },
   }))
end

orbit.htmlify(wrap,test)

print(test())

WSAPI

如前所述,WSAPI 充當許多專案的基石,並嵌入了多個功能。您可以使用 WSAPI 並支援以下平臺:

  • Windows
  • 基於 UNIX 的系統

WSAPI 支援的伺服器和介面包括:

  • CGI
  • FastCGI
  • Xavante

WSAPI 提供了許多庫,這使得我們使用 Lua 進行 Web 程式設計更加容易。Lua 中支援的一些功能包括:

  • 請求處理
  • 輸出緩衝
  • 身份驗證
  • 檔案上傳
  • 請求隔離
  • 多路複用

下面顯示了 WSAPI 的一個簡單示例:

#!/usr/bin/env wsapi.cgi

module(..., package.seeall)
function run(wsapi_env)
   local headers = { ["Content-type"] = "text/html" }
   
   local function hello_text()
      coroutine.yield("<html><body>")
      coroutine.yield("<p>Hello Wsapi!</p>")
      coroutine.yield("<p>PATH_INFO: " .. wsapi_env.PATH_INFO .. "</p>")
      coroutine.yield("<p>SCRIPT_NAME: " .. wsapi_env.SCRIPT_NAME .. "</p>")
      coroutine.yield("</body></html>")
   end

   return 200, headers, coroutine.wrap(hello_text)
end

您可以在上面的程式碼中看到一個簡單的 html 頁面是如何形成並返回的。您可以看到協程的使用,它使得逐句將語句返回給呼叫函式成為可能。最後,返回 html 狀態程式碼 (200)、標題和 html 頁面。

Xavante

Xavante 是一個使用基於 URI 對映處理程式的模組化架構的 Lua HTTP 1.1 Web 伺服器。Xavante 目前提供:

  • 檔案處理程式
  • 重定向處理程式
  • WSAPI 處理程式

檔案處理程式用於通用檔案。重定向處理程式啟用 URI 重新對映,WSAPI 處理程式用於處理 WSAPI 應用程式。

下面顯示了一個簡單的示例。

require "xavante.filehandler"
require "xavante.cgiluahandler"
require "xavante.redirecthandler"

-- Define here where Xavante HTTP documents scripts are located
local webDir = XAVANTE_WEB

local simplerules = {

   { -- URI remapping example
      match = "^[^%./]*/$",
      with = xavante.redirecthandler,
      params = {"index.lp"}
   }, 

   { -- cgiluahandler example
      match = {"%.lp$", "%.lp/.*$", "%.lua$", "%.lua/.*$" },
      with = xavante.cgiluahandler.makeHandler (webDir)
   },
    
   { -- filehandler example
      match = ".",
      with = xavante.filehandler,
      params = {baseDir = webDir}
   },
} 

xavante.HTTP{
   server = {host = "*", port = 8080},
    
   defaultHost = {
      rules = simplerules
   },
}

要將虛擬主機與 Xavante 一起使用,對 xavante.HTTP 的呼叫將更改為類似以下內容:

xavante.HTTP{
   server = {host = "*", port = 8080},
    
   defaultHost = {},
    
   virtualhosts = {
      ["www.sitename.com"] = simplerules
   }
}

Lua Web 元件

  • Copas,一個基於協程的排程器,可由 TCP/IP 伺服器使用。

  • Cosmo,一個“安全模板”引擎,可以保護您的應用程式免受模板中任意程式碼的攻擊。

  • Coxpcall 使用與協程相容的 Lua 原生 pcall 和 xpcall 進行封裝。

  • LuaFileSystem,一種訪問底層目錄結構和檔案屬性的可移植方式。

  • Rings,一個提供從 Lua 內部建立新 Lua 狀態的方法的庫。

結束語

有很多基於 Lua 的 Web 框架和元件可供我們使用,可以根據需要進行選擇。還有其他可用的 Web 框架,包括以下:

  • Moonstalk 能夠高效地開發和託管使用 Lua 語言構建的動態生成的基於 Web 的專案;從基本的頁面到複雜的應用程式。

  • Lapis 是一個用於使用 MoonScript(或 Lua)構建 Web 應用程式的框架,它執行在一個名為 OpenResty 的定製版 Nginx 中。

  • Lua 伺服器頁面 (Lua Server Pages),一個 Lua 指令碼引擎外掛,它優於任何其他嵌入式 Web 開發方法,為傳統的 C 伺服器頁面提供了巨大的捷徑。

這些 Web 框架可以利用您的 Web 應用程式,並幫助您執行強大的操作。

Lua - 資料庫訪問

對於簡單的資料操作,我們可以使用檔案,但是,有時這些檔案操作可能效率不高,不可擴充套件且不夠強大。為此,我們通常會切換到使用資料庫。LuaSQL 是 Lua 與多個數據庫管理系統之間的一個簡單介面。LuaSQL 是一個庫,它提供對不同型別 SQL 的支援,包括:

  • SQLite
  • MySQL
  • ODBC

在本教程中,我們將介紹在 Lua 中處理 MySQL 和 SQLite 資料庫的方法。這兩種資料庫都使用通用的介面,因此可以將此實現移植到其他型別的資料庫。

MySQL 資料庫設定

為了使以下示例按預期工作,我們需要進行初始資料庫設定。假設條件如下:

  • 您已安裝並設定了 MySQL,預設使用者為 root,密碼為“123456”。

  • 您已建立了一個名為 test 的資料庫。

  • 您已學習過 MySQL 教程,瞭解MySQL 基礎知識。

匯入 MySQL

假設您的 Lua 實現正確,我們可以使用簡單的require語句匯入 sqlite 庫。

mysql = require "luasql.mysql"

變數 mysql 將透過引用主 mysql 表來提供對函式的訪問。

設定連線

我們可以透過初始化 MySQL 環境,然後為該環境建立連線來設定連線。如下所示:

local env  = mysql.mysql()
local conn = env:connect('test','root','123456')

上述連線將連線到現有的 MySQL 檔案,並與新建立的檔案建立連線。

執行函式

連線提供了一個簡單的 execute 函式,它可以幫助我們執行所有資料庫操作,例如建立、插入、刪除和更新等。語法如下:

conn:execute([[ 'MySQLSTATEMENT' ]])

在上述語法中,我們需要確保 conn 是開啟的且存在的 MySQL 連線,並將“MySQLSTATEMENT”替換為正確的語句。

建立表示例

下面是一個簡單的建立表示例。它建立一個包含兩個引數的表:id(整數型別)和 name(varchar 型別)。

mysql = require "luasql.mysql"

local env  = mysql.mysql()
local conn = env:connect('test','root','123456')

print(env,conn)

status,errorString = conn:execute([[CREATE TABLE sample2 (id INTEGER, name TEXT);]])
print(status,errorString )

執行上述程式後,將建立一個名為 sample 的表,其中包含名為 id 和 name 的兩列。

MySQL environment (004BB178)	MySQL connection (004BE3C8)
0	nil

如果出現任何錯誤,系統將返回錯誤語句而不是 nil。下面是一個簡單的錯誤語句示例。

LuaSQL: Error executing query. MySQL: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"id INTEGER, name TEXT)' at line 1

插入語句示例

下面是 MySQL 的插入語句示例。

conn:execute([[INSERT INTO sample values('11','Raj')]])

更新語句示例

下面是 MySQL 的更新語句示例。

conn:execute([[UPDATE sample3 SET name='John' where id ='12']])

刪除語句示例

下面是 MySQL 的刪除語句示例。

conn:execute([[DELETE from sample3 where id ='12']])

選擇語句示例

至於選擇語句,我們需要遍歷每一行並提取所需的資料。下面是一個簡單的選擇語句示例。

cursor,errorString = conn:execute([[select * from sample]])
row = cursor:fetch ({}, "a")

while row do
   print(string.format("Id: %s, Name: %s", row.id, row.name))
   -- reusing the table of results
   row = cursor:fetch (row, "a")
end

在上述程式碼中,conn 是一個開啟的 MySQL 連線。藉助 execute 語句返回的遊標,您可以遍歷表響應並獲取所需的選擇資料。

完整示例

下面是一個包含所有上述語句的完整示例。

mysql = require "luasql.mysql"

local env  = mysql.mysql()
local conn = env:connect('test','root','123456')
print(env,conn)

status,errorString = conn:execute([[CREATE TABLE sample3 (id INTEGER, name TEXT)]])
print(status,errorString )

status,errorString = conn:execute([[INSERT INTO sample3 values('12','Raj')]])
print(status,errorString )

cursor,errorString = conn:execute([[select * from sample3]])
print(cursor,errorString)

row = cursor:fetch ({}, "a")

while row do
   print(string.format("Id: %s, Name: %s", row.id, row.name))
   row = cursor:fetch (row, "a")
end

-- close everything
cursor:close()
conn:close()
env:close()

執行上述程式後,您將得到以下輸出。

MySQL environment (0037B178)	MySQL connection (0037EBA8)
0	nil
1	nil
MySQL cursor (003778A8)	nil
Id: 12, Name: Raj

執行事務

事務是一種確保資料一致性的機制。事務應該具有以下四個屬性:

  • 原子性 - 事務要麼完成,要麼什麼也不發生。

  • 一致性 - 事務必須從一致狀態開始,並使系統保持一致狀態。

  • 隔離性 - 事務的中間結果在當前事務之外不可見。

  • 永續性 - 一旦事務提交,其效果將是持久的,即使在系統故障後也是如此。

事務以 START TRANSACTION; 開始,並以 commit 或 rollback 語句結束。

啟動事務

為了啟動事務,我們需要在 Lua 中執行以下語句,假設 conn 是一個開啟的 MySQL 連線。

conn:execute([[START TRANSACTION;]])

回滾事務

我們需要執行以下語句來回滾在啟動事務後所做的更改。

conn:execute([[ROLLBACK;]])

提交事務

我們需要執行以下語句來提交在啟動事務後所做的更改。

conn:execute([[COMMIT;]])

我們已經瞭解了上述 MySQL,以下部分將介紹基本的 SQL 操作。請記住,雖然沒有再次解釋 SQLite3 的事務,但相同的語句也應該適用於 SQLite3。

匯入 SQLite

假設您的 Lua 實現正確,我們可以使用簡單的 require 語句匯入 SQLite 庫。安裝過程中,會建立一個包含資料庫相關檔案的 libsql 資料夾。

sqlite3 = require "luasql.sqlite3"

變數 sqlite3 將透過引用主 sqlite3 表來提供對函式的訪問。

設定連線

我們可以透過初始化 SQLite 環境,然後為該環境建立連線來設定連線。如下所示:

local env  = sqlite3.sqlite3()
local conn = env:connect('mydb.sqlite')

上述連線將連線到現有的 SQLite 檔案,或者建立一個新的 SQLite 檔案,並與新建立的檔案建立連線。

執行函式

連線提供了一個簡單的 execute 函式,它可以幫助我們執行所有資料庫操作,例如建立、插入、刪除和更新等。語法如下:

conn:execute([[ 'SQLite3STATEMENT' ]])

在上述語法中,我們需要確保 conn 是開啟的且存在的 sqlite3 連線,並將“SQLite3STATEMENT”替換為正確的語句。

建立表示例

下面是一個簡單的建立表示例。它建立一個包含兩個引數的表:id(整數型別)和 name(varchar 型別)。

sqlite3 = require "luasql.sqlite3"

local env  = sqlite3.sqlite3()
local conn = env:connect('mydb.sqlite')
print(env,conn)

status,errorString = conn:execute([[CREATE TABLE sample ('id' INTEGER, 'name' TEXT)]])
print(status,errorString )

執行上述程式後,將建立一個名為 sample 的表,其中包含名為 id 和 name 的兩列。

SQLite3 environment (003EC918)	SQLite3 connection (00421F08)
0	nil

如果出現錯誤,系統將返回錯誤語句而不是 nil。下面是一個簡單的錯誤語句示例。

LuaSQL: unrecognized token: ""'id' INTEGER, 'name' TEXT)"

插入語句示例

下面是 SQLite 的插入語句示例。

 conn:execute([[INSERT INTO sample values('11','Raj')]])

選擇語句示例

至於選擇語句,我們需要遍歷每一行並提取所需的資料。下面是一個簡單的選擇語句示例。

cursor,errorString = conn:execute([[select * from sample]])
row = cursor:fetch ({}, "a")

while row do
   print(string.format("Id: %s, Name: %s", row.id, row.name))
   -- reusing the table of results
   row = cursor:fetch (row, "a")
end

在上述程式碼中,conn 是一個開啟的 sqlite3 連線。藉助 execute 語句返回的遊標,您可以遍歷表響應並獲取所需的選擇資料。

完整示例

下面是一個包含所有上述語句的完整示例。

sqlite3 = require "luasql.sqlite3"

local env  = sqlite3.sqlite3()
local conn = env:connect('mydb.sqlite')
print(env,conn)

status,errorString = conn:execute([[CREATE TABLE sample ('id' INTEGER, 'name' TEXT)]])
print(status,errorString )

status,errorString = conn:execute([[INSERT INTO sample values('1','Raj')]])
print(status,errorString )

cursor,errorString = conn:execute([[select * from sample]])
print(cursor,errorString)

row = cursor:fetch ({}, "a")

while row do
   print(string.format("Id: %s, Name: %s", row.id, row.name))
   row = cursor:fetch (row, "a")
end

-- close everything
cursor:close()
conn:close()
env:close()

執行上述程式後,您將得到以下輸出。

SQLite3 environment (005EC918)	SQLite3 connection (005E77B0)
0	nil
1	nil
SQLite3 cursor (005E9200)	nil
Id: 1, Name: Raj

我們可以使用此 libsql 庫執行所有可用的查詢。因此,請不要侷限於這些示例。嘗試在 Lua 中使用 MySQL、SQLite3 和其他受支援的資料庫中可用的各種查詢語句。

Lua - 遊戲程式設計

Lua 因其簡單的語言結構和語法而被廣泛用於許多遊戲引擎。垃圾收集功能在遊戲中經常非常有用,因為遊戲由於使用了豐富的圖形而會消耗大量的記憶體。一些使用 Lua 的遊戲引擎包括:

  • Corona SDK
  • Gideros Mobile
  • ShiVa3D
  • Moai SDK
  • LOVE
  • CryEngine

這些遊戲引擎都是基於 Lua 的,並且每個引擎都提供豐富的 API。

Corona SDK

Corona SDK 是一個跨平臺移動遊戲引擎,支援 iPhone、iPad 和 Android 平臺。Corona SDK 有一個免費版本,可用於具有有限功能的小型遊戲。您可以在需要時升級到其他版本。

Corona SDK 提供了許多功能,包括:

  • 物理和碰撞處理 API
  • Web 和網路 API
  • 遊戲網路 API
  • 廣告 API
  • 分析 API
  • 資料庫和檔案系統 API
  • 加密和數學 API
  • 音訊和媒體 API

使用上述 API 開發應用程式比分別為 iOS 和 Android 使用原生 API 更容易、更快。

Gideros Mobile

Gideros 提供跨平臺 SDK,用於為 iOS 和 Android 建立遊戲。它是免費使用的,帶有“由 Gideros 製作”的啟動畫面。Gideros 的一些顯著優勢包括:

  • 開發 IDE - 它提供自己的 IDE,使開發 Gideros 應用程式更容易。

  • 即時測試 - 在開發遊戲時,只需 1 秒鐘即可透過 Wifi 在真實裝置上進行測試。您無需浪費時間進行匯出或部署過程。

  • 外掛 - 您可以輕鬆地使用外掛擴充套件核心功能。匯入現有的 (C、C++、Java 或 Obj-C) 程式碼,繫結到 Lua 並直接解釋它們。已經有數十個開源外掛可以立即使用。

  • 簡潔的面向物件方法 - Gideros 提供了自己的類系統,包含所有基本的面向物件標準,使您可以為將來的任何遊戲編寫簡潔且可重用的程式碼。

  • 原生速度 - 基於 C/C++ 和 OpenGL 構建,您的遊戲以原生速度執行,並充分利用底層 CPU 和 GPU 的強大功能。

ShiVa3D

ShiVa3D 是一個 3D 遊戲引擎,它提供了一個圖形編輯器,用於為 Web、遊戲機和移動裝置建立應用程式和影片遊戲。它支援多個平臺,包括 Windows、Mac、Linux、iOS、Android、BlackBerry、Palm OS、Wii 和 WebOS。

一些主要功能包括:

  • 標準外掛
  • 網格修改 API
  • IDE
  • 內建地形、海洋和動畫編輯器
  • ODE 物理引擎支援
  • 完全的光照貼圖控制
  • 材質、粒子、軌跡和 HUD 的即時預覽
  • Collada 交換格式支援

Shiva3d 的 Web 版本是完全免費的,其他版本需要訂閱。

Moai SDK

Moai SDK 是一個跨平臺移動遊戲引擎,支援 iPhone、iPad 和 Android 平臺。Moai 平臺最初由 Moai SDK(一個開源遊戲引擎)和 Moai Cloud(一個用於託管和部署遊戲服務的雲平臺即服務)組成。現在 Moai Cloud 已關閉,只有遊戲引擎可用。

Moai SDK 執行在多個平臺上,包括 iOS、Android、Chrome、Windows、Mac 和 Linux。

LOVE

LOVE 是一個可用於製作 2D 遊戲的框架。它是免費且開源的。它支援 Windows、Mac OS X 和 Linux 平臺。

它提供了多個功能,包括:

  • 音訊 API
  • 檔案系統 API
  • 鍵盤和操縱桿 API
  • 數學 API
  • 視窗和滑鼠API
  • 物理API
  • 系統和計時器API

CryEngine

CryEngine是由德國遊戲開發商Crytek開發的遊戲引擎。它已經從第一代發展到第四代,是一款先進的開發解決方案。它支援PC、Xbox 360、PlayStation 3和Wii U遊戲。

它提供了多個功能,包括:

  • 視覺效果,例如自然光照和動態軟陰影、即時動態全域性光照、光傳播體積、粒子著色、細分等等。

  • 角色動畫系統和角色個性化系統。

  • 引數化骨骼動畫和獨特的專用面部動畫編輯器

  • AI系統,例如多層導航網格和戰術點系統。還提供使用者友好的AI編輯系統。

  • 遊戲內混音和分析,資料驅動的音效系統動態音效和互動音樂等等。

  • 物理特性,例如程式變形和高階繩索物理。

結束語

這些遊戲SDK/框架各有優缺點。在它們之間做出正確的選擇會讓你的任務更容易,你也能更好地完成它。因此,在使用它之前,你需要了解你遊戲的要求,然後分析哪個滿足你所有的需求,然後再使用它們。

Lua - 標準庫

Lua標準庫提供了一套豐富的函式,這些函式是直接用C API實現的,並且內置於Lua程式語言中。這些庫提供Lua程式語言內部的服務,以及外部服務,如檔案和資料庫操作。

這些內置於官方C API的標準庫作為單獨的C模組提供。它包括以下內容:

  • 基本庫,包括協程子庫
  • 模組庫
  • 字串操作
  • 表操作
  • 數學庫
  • 檔案輸入和輸出
  • 作業系統功能
  • 除錯功能

基本庫

在本教程的各個主題中,我們都使用了基本庫。下表提供了相關頁面的連結,並列出了本Lua教程各個部分中涵蓋的函式。

Lua提供以下型別的迴圈來處理迴圈需求。 庫/方法和用途
1

錯誤處理

包括錯誤處理函式,如assert、error,如Lua - 錯誤處理中所述。

2

記憶體管理

包括與垃圾收集相關的自動記憶體管理函式,如Lua - 垃圾收集中所述。

3

dofile ([filename])

它開啟檔案並執行檔案內容作為程式碼塊。如果沒有傳遞引數,則此函式執行標準輸入的內容。錯誤將傳播到呼叫者。

4

_G

這是一個全域性變數,它儲存全域性環境(即,_G._G = _G)。Lua本身不使用此變數。

5

getfenv ([f])

返回函式正在使用的當前環境。f可以是Lua函式或一個數字,指定該棧級別上的函式——級別1是呼叫getfenv的函式。如果給定的函式不是Lua函式,或者f為0,則getfenv返回全域性環境。f的預設值為1。

6

getmetatable (object)

如果物件沒有元表,則返回nil。否則,如果物件的元表具有“__metatable”欄位,則返回關聯的值。否則,返回給定物件的元表。

7

ipairs (t)

此函式獲取表的索引和值。

8

load (func [, chunkname])

使用函式func載入程式碼塊以獲取其片段。對func的每次呼叫都必須返回一個與先前結果連線的字串。

9

loadfile ([filename]))

類似於load,但從檔案filename或標準輸入(如果未給出檔名)獲取程式碼塊。

10

loadstring (string [, chunkname])

類似於load,但從給定的字串獲取程式碼塊。

11

next (table [, index])

允許程式遍歷表的全部欄位。它的第一個引數是一個表,第二個引數是該表中的一個索引。next返回表的下一個索引及其關聯的值。

12

pairs (t)

掛起正在執行的協程。傳遞給此方法的引數充當 resume 函式的附加返回值。

13

print (...)

掛起正在執行的協程。傳遞給此方法的引數充當 resume 函式的附加返回值。

14

rawequal (v1, v2)

檢查v1是否等於v2,不呼叫任何元方法。返回布林值。

15

rawget (table, index)

獲取table[index]的真實值,不呼叫任何元方法。table必須是一個表;index可以是任何值。

16

rawset (table, index, value)

將table[index]的真實值設定為value,不呼叫任何元方法。table必須是一個表,index為非nil的任何值,value為任何Lua值。此函式返回table。

17

select (index, ...)

如果index是數字,則返回索引號index之後的全部引數。否則,index必須是字串“#”,select返回它接收的額外引數的總數。

18

setfenv (f, table)

設定要由給定函式使用的環境。f可以是Lua函式或一個數字,指定該棧級別上的函式——級別1是呼叫setfenv的函式。setfenv返回給定的函式。作為特例,當f為0時,setfenv更改執行執行緒的環境。在這種情況下,setfenv不返回值。

19

setmetatable (table, metatable)

為給定表設定元表。(你不能從Lua更改其他型別的元表,只能從C更改。)如果metatable為nil,則刪除給定表的元表。如果原始元表具有“__metatable”欄位,則引發錯誤。此函式返回table。

20

tonumber (e [, base])

嘗試將它的引數轉換為數字。如果引數已經是數字或可轉換為數字的字串,則tonumber返回此數字;否則,它返回nil。

21

tostring (e)

接收任何型別的引數並將其轉換為合理格式的字串。要完全控制數字的轉換方式,請使用string.format。

22

type (v)

返回其唯一引數的型別,編碼為字串。此函式的可能結果是“nil”(一個字串,而不是值nil)、“number”、“string”、“boolean”、“table”、“function”、“thread”和“userdata”。

23

unpack (list [, i [, j]])

返回給定表中的元素。

24

_VERSION

一個全域性變數(不是函式),它儲存一個包含當前直譯器版本的字串。此變數的當前內容是“Lua 5.1”。

25

協程

包括協程操作函式,如Lua - 協程中所述。

模組庫

模組庫提供在Lua中載入模組的基本函式。它直接在全域性環境中匯出一個函式:require。其他所有內容都在表package中匯出。關於模組庫的詳細資訊在前面章節Lua - 模組教程中進行了說明。

字串操作

Lua提供了一套豐富的字串操作函式。之前的Lua - 字串教程詳細介紹了這一點。

表操作

Lua幾乎在其所有操作中都依賴於表。之前的Lua - 表教程詳細介紹了這一點。

檔案輸入和輸出

在程式設計中,我們經常需要資料儲存功能,Lua中的標準庫函式提供了檔案I/O功能。這在之前的Lua - 檔案I/O教程中進行了討論。

除錯功能

Lua提供了一個除錯庫,該庫為我們提供了建立自己的偵錯程式所需的所有基本函式。這在之前的Lua - 除錯教程中進行了討論。

Lua - 數學庫

在科學和工程計算中,我們經常需要數學運算,我們可以使用標準Lua庫math來實現這一點。math庫中可用的函式列表如下表所示。

Lua提供以下型別的迴圈來處理迴圈需求。 庫/方法和用途
1

math.abs (x)

返回x的絕對值。

2

math.acos (x)

返回x的反餘弦(以弧度為單位)。

3

math.asin (x)

返回x的反正弦(以弧度為單位)。

4

math.atan (x)

返回x的反正切(以弧度為單位)。

5

math.atan2 (y, x)

返回y/x的反正切(以弧度為單位),但使用兩個引數的符號來查詢結果的象限。(它也正確處理x為零的情況。)

6

math.ceil (x)

返回大於或等於x的最小整數。

7

math.cos (x)

返回x的餘弦(假定為弧度)。

8

math.cosh (x)

返回x的雙曲餘弦。

9

math.deg (x)

返回以弧度表示的角度x(以度為單位)。

10

math.exp (x)

返回e的x次冪。

11

math.floor (x)

返回小於或等於x的最大整數。

12

math.fmod (x, y)

返回x除以y的餘數,將商四捨五入到零。

13

math.frexp (x)

返回m和e,使得x = m2e,e是整數,m的絕對值在[0.5, 1)範圍內(或當x為零時為零)。

14

math.huge

HUGE_VAL的值,大於或等於任何其他數值的值。

15

math.ldexp (m, e)

返回m2e(e應為整數)。

16

math.log (x)

返回x的自然對數。

17

math.log10 (x)

返回x的以10為底的對數。

18

math.max (x, ...)

返回其引數中的最大值。

19

math.min (x, ...)

返回其引數中的最小值。

20

math.modf (x)

返回兩個數字,x的整數部分和x的小數部分。

21

math.pi

π的值。

22

math.pow (x, y)

返回xy。(你也可以使用表示式x^y來計算此值。)

23

math.rad (x)

返回以度表示的角度x(以弧度為單位)。

24

math.random ([m [, n]])

此函式是ANSI C提供的簡單偽隨機生成器函式rand的介面。在不帶引數的情況下呼叫時,返回範圍[0,1)內的均勻偽隨機實數。當用整數m呼叫時,math.random返回範圍[1, m]內的均勻偽隨機整數。當用兩個整數m和n呼叫時,math.random返回範圍[m, n]內的均勻偽隨機整數。

25

math.randomseed (x)

將x設定為偽隨機生成器的“種子”:相同的種子產生相同的數字序列。

26

math.sin (x)

返回x的正弦(假定為弧度)。

27

math.sinh (x)

返回x的雙曲正弦。

28

math.sqrt (x)

返回x的平方根。(你也可以使用表示式x^0.5來計算此值。)

29

math.tan (x)

返回x的正切(假定為弧度)。

30

math.tanh (x)

返回x的雙曲正切。

三角函式

下面顯示了一個使用三角函式的簡單示例。

radianVal = math.rad(math.pi / 2)

io.write(radianVal,"\n")

-- Sin value of 90(math.pi / 2) degrees
io.write(string.format("%.1f ", math.sin(radianVal)),"\n")

-- Cos value of 90(math.pi / 2) degrees
io.write(string.format("%.1f ", math.cos(radianVal)),"\n")

-- Tan value of 90(math.pi / 2) degrees
io.write(string.format("%.1f ", math.tan(radianVal)),"\n")

-- Cosh value of 90(math.pi / 2) degrees
io.write(string.format("%.1f ", math.cosh(radianVal)),"\n")

-- Pi Value in degrees
io.write(math.deg(math.pi),"\n")

執行上述程式後,我們將得到以下輸出。

0.027415567780804
0.0 
1.0 
0.0 
1.0 
180

其他常用數學函式

下面顯示了一個使用常用數學函式的簡單示例。

-- Floor
io.write("Floor of 10.5055 is ", math.floor(10.5055),"\n")

-- Ceil
io.write("Ceil of 10.5055 is ", math.ceil(10.5055),"\n")

-- Square root
io.write("Square root of 16 is ",math.sqrt(16),"\n")

-- Power
io.write("10 power 2 is ",math.pow(10,2),"\n")
io.write("100 power 0.5 is ",math.pow(100,0.5),"\n")

-- Absolute
io.write("Absolute value of -10 is ",math.abs(-10),"\n")

--Random
math.randomseed(os.time())
io.write("Random number between 1 and 100 is ",math.random(),"\n")

--Random between 1 to 100
io.write("Random number between 1 and 100 is ",math.random(1,100),"\n")

--Max
io.write("Maximum in the input array is ",math.max(1,100,101,99,999),"\n")

--Min
io.write("Minimum in the input array is ",math.min(1,100,101,99,999),"\n")

執行上述程式後,我們將得到以下輸出。

Floor of 10.5055 is 10
Ceil of 10.5055 is 11
Square root of 16 is 4
10 power 2 is 100
100 power 0.5 is 10
Absolute value of -10 is 10
Random number between 1 and 100 is 0.22876674703207
Random number between 1 and 100 is 7
Maximum in the input array is 999
Minimum in the input array is 1

以上示例只是一些常用示例,我們可以根據需要使用math庫,因此請嘗試使用所有函式以更熟悉它們。

Lua - 作業系統功能

在任何應用程式中,通常都需要訪問作業系統級函式,並且可以使用作業系統庫來實現。可用的函式列表如下表所示。

Lua提供以下型別的迴圈來處理迴圈需求。 庫/方法和用途
1

os.clock ()

返回程式使用的CPU時間量(以秒為單位)的近似值。

2

os.date ([format [, time]])

返回一個包含日期和時間的字串或表,其格式根據給定的字串格式。

3

os.difftime (t2, t1)

返回從時間t1到時間t2的秒數。在POSIX、Windows和一些其他系統中,此值正好是t2-t1。

4

os.execute ([command])

此函式等效於ANSI C函式system。它傳遞要由作業系統shell執行的命令。它的第一個結果是如果命令成功終止則為true,否則為nil。

5

os.exit ([code [, close]])

呼叫 ANSI C 函式 exit 來終止主機程式。如果 code 為真,則返回狀態為 EXIT_SUCCESS;如果 code 為假,則返回狀態為 EXIT_FAILURE;如果 code 為數字,則返回狀態為此數字。

6

os.getenv (varname)

返回程序環境變數 varname 的值,如果變數未定義則返回 nil。

7

os.remove (filename)

刪除具有給定名稱的檔案(或 POSIX 系統上的空目錄)。如果此函式失敗,它將返回 nil,加上描述錯誤的字串和錯誤程式碼。

8

os.rename (oldname, newname)

將名為 oldname 的檔案或目錄重新命名為 newname。如果此函式失敗,它將返回 nil,加上描述錯誤的字串和錯誤程式碼。

9

os.setlocale (locale [, category])

設定程式的當前區域設定。locale 是一個系統相關的字串,指定一個區域設定;category 是一個可選字串,描述要更改的類別:“all”、“collate”、“ctype”、“monetary”、“numeric”或“time”;預設類別為“all”。該函式返回新區域設定的名稱,如果請求無法滿足則返回 nil。

10

os.time ([table])

在不帶引數呼叫時返回當前時間,或表示由給定表指定的日期和時間的 time。此表必須具有 year、month 和 day 欄位,並且可能具有 hour(預設為 12)、min(預設為 0)、sec(預設為 0)和 isdst(預設為 nil)欄位。有關這些欄位的描述,請參閱 os.date 函式。

11

os.tmpname ()

返回一個字串,其中包含可用於臨時檔案的檔名。必須在使用臨時檔案之前顯式開啟它,並在不再需要時顯式刪除它。

常用作業系統函式

下面顯示了一個使用常用數學函式的簡單示例。

-- Date with format
io.write("The date is ", os.date("%m/%d/%Y"),"\n")

-- Date and time
io.write("The date and time is ", os.date(),"\n")

-- Time
io.write("The OS time is ", os.time(),"\n")

-- Wait for some time
for i=1,1000000 do
end

-- Time since Lua started
io.write("Lua started before ", os.clock(),"\n")

執行上述程式後,我們將得到類似於以下的輸出。

The date is 01/25/2014
The date and time is 01/25/14 07:38:40
The OS time is 1390615720
Lua started before 0.013

以上示例只是一些常用的示例,我們可以根據需要使用 OS 庫,因此請嘗試使用所有函式以更熟悉它們。例如,remove 函式可以幫助刪除檔案,execute 函式可以幫助我們執行上述解釋的作業系統命令。

廣告
© . All rights reserved.