Lua_(programming_language)

**Lua**（發音： /ˈluːə/，葡萄牙語「月亮」）是一個簡潔、輕量、可延伸的手稿語言。Lua 有著相對簡單的 C API 而很容易嵌入應用中。很多應用程式使用 Lua 作為自己的嵌入式手稿語言，以此來實現可組態性、可延伸性。

## 歷史

Lua 是在 1993 年由羅伯托・耶魯薩林斯希、Luiz Henrique de Figueiredo 和 Waldemar Celes 建立的，他們當時是巴西的里約熱內盧天主教大學的電腦圖形技術組（Tecgraf）成員。Lua 的先驅是資料描述 / 組態語言「SOL」（簡單物件語言）和「DEL」（資料錄入語言）。他們於 1992 年–1993 年在 Tecgraf 獨立開發了需要增加靈活性的兩個不同專案（都是用於工程應用的互動式圖形程式）。在 SOL 和 DEL 中缺乏很多控制流結構，需要向它們增加完全的編程能力。

在《The Evolution of Lua》中，這門語言的作者寫道：

> 在 1993 年，唯一真正的競爭者是 Tcl，它已經明確的設計用於嵌入到應用之中。但是，Tcl 有著不熟知的語法，未對資料描述提供良好的支援，並且只在 Unix 平台上執行。我們不考慮 LISP 或 Scheme，因為它們有著不友好的語法。Python 仍處在幼年期。在 Tecgraf 的自由的自力更生氛圍下，我們非常自然的嘗試開發自己的手稿語言 ... 由於這門語言的很多潛在使用者不是專業編程者，語言應當避免神秘的語法和語意。新語言的實現應當是高度可移植的，因為 Tecgraf 的客戶有著非常多樣的各種電腦平台。最後，由於我們預期 Tecgraf 的其他產品也需要嵌入手稿語言，新語言應當追隨 SOL 的例子並提供為帶有 C API 的庫。
> 
> 

Lua 主要受到了下列前輩語言的影響：

* Modula-2：從中引入了大部份控制結構語法，`if`、`while`、`repeat`/`until`。
* CLU：多賦值和從函式呼叫的多個返回值，這是對參照參數或顯式指標的更簡單的替代。
* C++：「允許局部變數只在需要的地方聲明的靈巧想法」。
* SNOBOL 和 AWK：關聯陣列。
* LISP 和 Scheme：在發表於《Dr. Dobb's Journal》的文章中，Lua 的創立者還聲稱，有著單一且無所不在的資料結構機制（列表）的 LISP 和 Scheme，對他們決定將表格開發為 Lua 的主要資料結構起到了主要影響。Lua 的語意久而久之日趨受到 Scheme 的影響，特別是介入了匿名函式和完全的詞法作用域。

Lua 在版本 5.0 之前在類似 BSD 授權條款之下發行。自從版本 5.0 以來，Lua 採用了 MIT 授權條款。

## 特性

Lua 是一種輕量語言，它的官方版本只包括一個精簡的核心和最基本的庫。這使得 Lua 體積小、啟動速度快。它用 ANSI C 語言編寫，並以原始碼形式開放，編譯後的完整參考直譯器只有大約 247kB，到 5.4.3 版本，該體積變成 283kB（Linux,amd64），依然非常小巧，可以很方便的嵌入別的程式裡。和許多「大而全」的語言不一樣，網路通訊、圖形介面等都沒有預設提供。但是 Lua 可以很容易地被擴充：由宿主語言（通常是 C 或 C++）提供這些功能，Lua 可以使用它們，就像是本來就內建的功能一樣。事實上，現在已經有很多成熟的擴充模組可供選用。

Lua 是一個動態型別語言，支援增量式垃圾收集策略。有內建的，與作業系統無關的協同運作式多執行緒支援。Lua 原生支援的資料類型很少，只提供了數值（預設是雙精度浮點數，可組態）、布林量、字串、表格、函式、執行緒以及使用者自訂資料這幾種。但是其處理表和字串的效率非常之高，加上元表的支援，開發者可以高效的類比出需要的複雜資料類型（比如集合、陣列等）。

## 語法和語意

Lua 是一種多重程式設計範式的程式設計語言：它只提供了很小的一個特性集合來滿足不同程式設計範式的需要，而不是為某種特定的程式設計範式提供繁雜的特性支援。例如，Lua 並不提供繼承這個特性，但是你可以用元表格來類比它。諸如命名空間、類這些概念都沒有在語言基本特性中實現，但是我們可以用表格結構（Lua 唯一提供的複雜資料結構）輕易類比。正是提供了這些基本的元特性，我們可以任意的對語言進行自需的改造。

Lua 實現了少量的進階特徵比如頭等函式、垃圾回收、閉包、正當尾呼叫、強制（於執行時間在字串和數值之間自動轉換）、協程（協同運作多工）和動態模組裝載。

### 詞法

經典的 Hello World! 程式可以寫為如下：

或者如下：

在 Lua 中注釋可以於雙連字元並列至此行的結束，類似於 Ada、Eiffel、Haskell、SQL 和 VHDL。多行字串和注釋用雙方括號來裝飾。

下例中實現了一個階乘函式：

### 控制流

Lua 有四種類型的迴圈：`while` 迴圈，`repeat` 迴圈（類似於 `do while` 迴圈），數值 `for` 迴圈，和通用 `for` 迴圈，和條件語句 `if exp then ... {elseif exp then ...} [else ...] end`。

通用 `for` 迴圈：

將在表格 `_G` 上使用標準迭代器函式 `pairs` 進行迭代，直到它返回 `nil`。

可以有巢狀的迴圈，就是在其他迴圈中的迴圈。

### 函式

Lua 將函式處理為頭等值，在下例子中用 print 函式的表現可以修改來展示：

任何對 `print` 的進一步呼叫都要經由新函式，並且由於 Lua 的詞法作用域，這個舊的 print 函式將只能被這個新的修改了的 print 存取到。

Lua 還支援閉包，展示如下：

每次呼叫的時候為變數 `x` 建立新的閉包，所以返回的每個新匿名函式都存取它自己的 `x` 參數。閉包由 Lua 的垃圾收集器來管理，如同任何其他物件一樣。

create\_a\_counter () 會返回一個匿名函式，這個匿名函式會把 count 加 1 後再回傳。在匿名函式中的變數 count 的值會一直被儲存在匿名函式中。因此呼叫 create\_a\_counter () 時產生一個記數器函式，每次呼叫記數器函式，都會得到一個比上次大 1 的值。

### 變數類型

Lua 是一種動態型別語言，因此語言中沒有類型的定義，不需要聲明變數類型，每個變數自己儲存了類型。

有 8 種基本類型：nil、布林值（boolean）、數字型（number）、字串型（string）、使用者自訂類型（userdata）、函式（function）、執行緒（thread）和表（table）。

### 表格

表格（table）是 Lua 中最重要的資料結構（並且是設計中唯一內建的複合資料類型），並且是所有使用者建立類型的基礎。它們是增加了自動數值鍵和特殊語法的關聯陣列。

表格是鍵和資料的有序對的搜集，其中的資料用鍵來參照；換句話說，它是雜湊異構關聯陣列。

表格使用 `{}` 構造器語法來建立。

表格總是用參照來傳遞的（參見傳共享呼叫）。

鍵（索引）可以是除了 `nil` 和 NaN 的任何值，包括函式。

通過使用字串作為鍵，表格經常用作結構（或記錄）。由於這種用法太常見，Lua 為存取這種欄位提供了特殊語法。

通過使用表格來儲存有關函式，它可以充當命名空間。

表格被自動的賦予了數值鍵，使得它們可以被用作陣列資料類型。第一個自動索引是 1 而非 0，因為很多其他程式語言都這樣（儘管顯式的索引 0 是允許的）。

數值鍵 `1` 不同於字串鍵 `"1"`。

表格的長度 `t` 被定義為任何整數索引 `n`，使得 `t[n]` 不是 `nil` 而 `t[n+1]` 是 `nil`；而且如果 `t[1]` 是 `nil`， `n` 可以是 0。對於一個正規表格，具有非 nil 值從 1 到給定 `n`，它的長度就精確的是 `n`，它的最後的值的索引。如果這個陣列有「洞」（就是說在其他非 nil 值之間的 nil 值），則 `#t` 可以是直接前導於 `nil` 值的任何一個索引（就是說可以把任何這種 nil 值當作陣列的結束）。

表格可以是物件的陣列。

使用雜湊對映來類比陣列通常比使用真正陣列要慢；但 Lua 表格為用作陣列而做了最佳化來避免這個問題。

### 元表格

可延伸的語意是 Lua 的關鍵特徵，而「元表格」概念允許以強力方式來客製化 Lua 的表格。下列例子展示了一個「無限」表格。對於任何 `n`，`fibs[n]` 會給出第 `n` 個斐波那契數，使用了動態規劃和記憶化。

## 物件導向程式設計

儘管 Lua 沒有內建的類的概念，可以用過兩個語言特徵實現物件導向程式設計：頭等函式和表格。通過放置函式和有關資料入表格，形成一個物件。繼承（單繼承和多重繼承二者）可以通過使用元表格機制來實現，告訴這個物件在哪些父物件中尋找不存在的方法。

對於這些技術不採用「類」的概念，而是採用原型，類似於 Self 或 JavaScript。建立新物件要麼通過工廠方法（從頭構造一個新物件），要麼通過複製現存的物件。

建立一個基本的向量物件：

Lua 為便利物件定向提供了一些語法糖。要聲明在原型表格內的成員函式，可以使用 `function table:func(args)`，它等價於 `function table.func(self, args)`。呼叫類別方法也使用冒號，`object:func(args)` 等價於 `object.func(object, args)`。

下面是使用 `:` 語法糖的對應的類：

#### 繼承

Lua 支援使用元表格來使得 Lua 具有類繼承。在這個例子中，我們允許向量在衍生的類中將它們的值乘以一個常數：

Lua 還支援多重繼承，`__index` 可以要麼是一個函式要麼是一個表格。運算子多載也可以進行，Lua 元表格擁有元素比如 `__add`、`__sub`，諸如此類。

## 實現

Lua 程式不是從文字式的 Lua 檔案直接解釋的，而是編譯成位元組碼，接著把它執行在 Lua 虛擬機器上。編譯過程典型的對於使用者是不可見並且是在執行時間進行的，但是它可以離線完成用來增加裝載效能或通過排除編譯器來減少對宿主環境的記憶體占用。Lua 位元組碼還可以在 Lua 之內產生和執行，使用來自字串庫的 `dump` 函式和 `load/loadstring/loadfile` 函式。Lua 版本 5.3.4 是用大約 24,000 行 C 代碼實現的。

像大多數 CPU，而不像多數虛擬機器（它們是基於堆疊的），Lua VM 是基於暫存器的，因此更加類似真實的硬體設計。暫存器架構既避免了過多的值複製又減少了每函式的指令的總數。Lua 5 的虛擬機器是第一個廣泛使用的基於暫存器的純 VM。Parrot 和 Android 的 Dalvik 是另外兩個周知的基於暫存器的 VM。PCScheme 的 VM 也是基於暫存器的。

下面的例子列出上面定義的階乘函式的位元組碼（通過 `luac` 5.1 編譯器來展示）：

```
function <factorial.lua:1,7> (9 instructions, 36 bytes at 0x8063c60)
1 param, 6 slots, 0 upvalues, 6 locals, 2 constants, 0 functions
	1	[2]	LOADK    	1 -1	; 1
	2	[3]	LOADK    	2 -2	; 2
	3	[3]	MOVE     	3 0
	4	[3]	LOADK    	4 -1	; 1
	5	[3]	FORPREP  	2 1	; to 7
	6	[4]	MUL      	1 1 5
	7	[3]	FORLOOP  	2 -2	; to 6
	8	[6]	RETURN   	1 2
	9	[7]	RETURN   	0 1
```

## C API

Lua 意圖被嵌入到其他應用之中，為了這個目的而提供了 C API。API 被分成兩部份：Lua 核心庫和輔助庫。Lua API 的設計消除了對用 C 代碼的手動參照管理的需要，不同於 Python 的 API。API 就像語言本身一樣是極簡主義的。進階功能通過輔助庫來提供，它們很大程度上構成自預處理器宏，用以幫助做複雜的表格操作。

Lua C API 是基於堆疊的。Lua 提供壓入和彈出最簡單 C 資料類型（整數、浮點數等）進入和離開堆疊的函式，還有通過堆疊操作表格的函式。Lua 堆疊稍微不同於傳統堆疊，例如堆疊可以直接的被索引。負數索引指示從棧頂開始往下的偏移量。例如−1 是在頂部的（最新進壓入的值），而整數索引指示從底部（最舊的值）往上的偏移量。在 C 和 Lua 函式之間集結資料也使用堆疊完成。要呼叫一個 Lua 函式，把實際參數壓入堆疊，並接著使用 `lua_call` 來呼叫實際的函式。在寫從 Lua 直接呼叫的 C 函式的時候，實際參數讀取自堆疊。

下面是從 C 呼叫 Lua 函式的例子:

如下這樣執行這個例子：

```
$ cc -o example example.c -llua
$ ./example
Result: 8
```

C API 還提供一些特殊表格，位於各種 Lua 堆疊中的「偽索引」之上。Lua 5.2 之前，在 `LUA_GLOBALSINDEX` 之上是全域表格；`_G` 來自 Lua 內部，它是主命名空間。還有一個登錄檔位於 `LUA_REGISTRYINDEX`，在這裡 C 程式可以儲存 Lua 值用於以後檢索。

還可以使用 Lua API 寫擴充模組。擴充模組是通過向 Lua 指令碼提供本地設施，用來擴充直譯器的功能的共享物件。從 Lua 方面看來，這種模組出現為一個命名空間表格，它持有自己的函式和變數。Lua 指令碼可以使用 `require` 裝載擴充模組，就像用 Lua 自身寫的模組一樣。一組仍在增長中的叫做「rock」的模組可以通過叫做 LuaRocks 的軟體套件管理器取得到，類似於 CPAN、RubyGems 和 Python eggs。對大多數流行的程式語言包括其他手稿語言，都存在預先寫好的 Lua 繫結。對於 C++，有許多基於模板的方式和一些自動繫結生成器。

## 應用

在電動遊戲開發中，Lua 被程式設計師廣泛的用作手稿語言，主要由於它的可感知到的易於嵌入、快速執行，和短學習曲線。

在 2003 年，GameDev.net 組織的一次投票，說明了 Lua 是遊戲編程的最流行手稿語言。在 2012 年 1 月 12 日，Lua 被《遊戲開發者》宣布為編程工具範疇的 Front Line 獎 2011 年度獲獎者。

大量非遊戲應用也出於可延伸性而使用 Lua，比如 TeX 排版設定語言實現 LuaTeX、鍵 - 值資料庫 Redis、文字編輯器 Neovim 和 web 伺服器 Nginx。

通過 Scribunto 擴充，Lua 可獲得為 MediaWiki 軟體中的伺服器端手稿語言，Wikipedia 和其他 wiki 都基於了它。它的應用包括允許從 Wikidata 整合資料到文章中，和助力於自動生物分類框系統。

Lua 可以用於嵌入式硬體，不僅可以嵌入其他程式語言，而且可以嵌入微處理器中，例如 NodeMCU 開源硬體項目將 Lua 嵌入到 Wi-Fi SoC 中，另外，遊戲平台 Roblox 所有遊戲都是利用 Lua 進行編程的。另外，還有中國大陸發起的開源 LuatOS 專案，也是將 Lua 應用到嵌入式領域，但不僅限於 Wifi，它包含 2G/4G/NBIOT 等通信模組，也包含 stm32/w800 等嵌入式 mcu，同時也支援在 windows/linux 平台上執行的模擬器。

## 編譯成 Lua 的語言

* Moonscript，是動態、空白敏感的手稿語言，它受到 CoffeeScript 的啟發，編譯成 Lua。
* Haxe，支援編譯成 Lua 目標，支援 Lua 5.1-5.3 還有 LuaJIT 2.0 和 2.1。
* Fennel，目標為 Lua 的 Lisp 方言。
* Urn，建造在 Lua 之上的 Lisp 方言。
* Amulet，類 ML 的函數式語言，其編譯器輸出 Lua 檔案。

## 參考資料

## 延伸閱讀

* Ierusalimschy, R. Programming in Lua 3rd. Lua.org. 2013 [2020-10-13]. ISBN978-85-903798-5-0. （原始內容存檔於 2021-01-18）.  (The 1st ed. is available online （頁面存檔備份，存於網際網路檔案館）.)
* Gutschmidt, T. Game Programming with Python, Lua, and Ruby. Course Technology PTR. 2003. ISBN 978-1-59200-077-7.
* Schuytema, P.; Manyen, M. Game Development with Lua. Charles River Media. 2005. ISBN 978-1-58450-404-7.
* Jung, K.; Brown, A. Beginning Lua Programming. Wrox Press. 2007 [7 July 2018]. ISBN978-0-470-06917-2. （原始內容存檔於 2018-07-08）.
* Figueiredo, L. H.; Celes, W.; Ierusalimschy, R. (編). Lua Programming Gems. Lua.org. 2008 [2020-10-14]. ISBN978-85-903798-4-3. （原始內容存檔於 2020-11-09）.
* Takhteyev, Yuri. Coding Places: Software Practice in a South American City. The MIT Press. 2012 [2020-10-14]. ISBN978-0-262-01807-4. （原始內容存檔於 2012-11-02）.  Chapters 6 and 7 are dedicated to Lua, while others look at software in Brazil more broadly.
* Varma, Jayant. Learn Lua for iOS Game Development. Apress. 2012 [2020-10-14]. ISBN978-1-4302-4662-6. （原始內容存檔於 2021-04-14）.
* Matheson, Ash. An Introduction to Lua. GameDev.net. 29 April 2003 [3 January 2013]. （原始內容存檔於 2012-12-18）.
* Fieldhouse, Keith. Introducing Lua. ONLamp.com. O'Reilly Media. 16 February 2006 [28 February 2006]. （原始內容存檔於 2006-03-12）.
* Streicher, Martin. Embeddable scripting with Lua. developerWorks. IBM. 28 April 2006 [2020-10-14]. （原始內容存檔於 2009-07-02）.
* Quigley, Joseph. A Look at Lua. Linux Journal. 1 June 2007 [2020-10-14]. （原始內容存檔於 2020-10-30）.
* Hamilton, Naomi. The A-Z of Programming Languages: Lua. Computerworld (IDG). 11 September 2008 [7 July 2018]. （原始內容存檔於 2018-07-08）.  Interview with Roberto Ierusalimschy.
* Ierusalimschy, Roberto; de Figueiredo, Luiz Henrique; Celes, Waldemar. Passing a Language through the Eye of a Needle. ACM Queue. 12 May 2011, **9** (5): 20–29 [7 July 2018]. doi:10.1145/1978862.1983083. （原始內容存檔於 2018-07-08）.  How the embeddability of Lua impacted its design.
* Ierusalimschy, Roberto; de Figueiredo, Luiz Henrique; Celes, Waldemar. A Look at the Design of Lua. Communications of the ACM. November 2018, **61** (11): 114–123 [2020-10-14]. doi:10.1145/3186277. （原始內容存檔於 2021-01-09）.
* Lua papers and theses （頁面存檔備份，存於網際網路檔案館）

## 外部連結

* 官方網站
* Lua Users （頁面存檔備份，存於網際網路檔案館）, 使用者社群