前言
Lua 算是一個相對小眾的語言,通常會學 Lua 的程式人都有某種明確的目的,比較不會把 Lua 當成通用型語言來學。常見的使用方式是搭配遊戲引擎來寫電腦遊戲,像是 Corona 或 Defold 等;或是寫遊戲的外掛,像是魔獸世界 (World of Warcraft);或者是把 Lua 內嵌到宿主軟體中。
Lua 在 5.1、5.2、5.3 版之間有一些不相容的變動,在撰寫 Lua 程式或閱讀 Lua 資料時需注意。本書的 Lua 目標版本是 5.3 版,如果要寫舊版本的 Lua 程式,仍然可參考本書,只是要小幅修改一部分程式。
由於本書的作者是 Lua 的爸爸 Roberto Ierusalimschy,本書相當有指標性,可視為 Lua 的聖經。此外,購買本書時同時在贊助 Lua 的永續發展。
本書共 310 頁,分為 33 章,介於輕量級和中量級之間;但本書字偏多,沒辦法很快讀完。本書分為四部,前三部講 Lua,最後一部講 Lua C API,如果用不到 C 的部分,最後一部可以略過不讀。接下來,我們會逐一介紹各個章節。
I. The Basics
本部講解 Lua 基礎語法元件。
1. Getting Started
本書談到如何撰寫 Lua 程式,分為互動模式 (interactive mode) 和批次模式 (batch mode),實務上都會寫好 Lua 命令稿後再執行,互動模式僅是用來練習語法。接著講解識別字 (identifier)、型別 (types)、註解 (comments) 等基本概念。
2. Interlude: The Eight-Queen Puzzle
本章展示如何用 Lua 解經典的八皇后問題,有空可以欣賞一下。
3. Numbers
本章講述 Lua 的數字型別。Lua 5.3 版引入整數,和先前的版本有一些差異,在撰寫程式時需注意。此外,本章說明數字運算相關的運算子、數學函式庫、產生亂數等議題。
4. Strings
本章講述 Lua 的字串型別。原本 Lua 的字串是 byte 序列,沒有針對特定的編碼來儲存;但 Lua 在 5.3 版引入一個處理 UTF8 字串的函式庫。此外,也講到字串相關的運算子及字串函式庫。
5. Tables
表 (tables) 是 Lua 唯一的資料結構,由於表在 Lua 中相當實用,可做為陣列 (array)、關連式陣列 (associative array)、物件 (object) 等,學習表的使用相當重要。本章學習表的基本用法,不會觸及物件導向程式的部分。
6. Functions
本章講解基本的函式寫法,其他較進階的寫法就留在後續章節中。
7. The External World
本章講解 Lua 的基本輸出入,像是檔案的讀取或寫入、呼叫外部命令、讀取環境變數等。
8. Filling Some Gaps
本章講解一些未歸類的語法特性:
- 局部變數 (local variables)
- 區塊 (blocks)
- 控制結構 (control structures)
因這些內容篇幅偏短,故集中到同一章節來介紹。
II. Real Programming
本部講解一些相對進階的 Lua 語法。
9. Closures
本章帶入一些基本的函數式程式的概念,像是函式物件 (function object) 等。Lua 其實也支援一些函數式程式的特性,本章算是暖身,為之後講解函數式程式做準備。
10. Pattern Matching
正規表示式 (regular expression) 引擎所占的程式碼量較大,若要在 Lua 這類輕薄短小的語言中塞入整個正規表示式引擎,整個語言的空間量會過大,不符內嵌語言的設計目標。因此,Lua 引入 pattern matching 做為替代的方案,本章講解 pattern matching 的語法和使用方式。
11. Interlude: Most Frequent Words
承續上一章,本章展示如何用 Lua 計算文字出現頻率,有空可以欣賞一下。
12. Date and Time
本章講述 Lua 如何處理日期 (date) 和時間 (time) 這兩個在程式中重要的概念,主要是透過 os.date
和 os.time
這兩個函式庫來處理。
13. Bits and Bytes
本章講述二進位運算及二進位檔案的處理。
14. Data Structures
由於 Lua 只有表 (table) 這個資料結構,本章講述如何用表模擬其他的資料結構,包括陣列 (array)、矩陣 (matrix)、串列 (linked list)、佇列 (queue)、集合 (set)、圖 (graph) 等。本章內容算蠻實用的,有需要的讀者可以閱讀一下。
15. Data Files and Serialization
本章探討資料序列化的議題。一般來說,XML 或 JSON 是常見的資料序列化格式;但 Lua 本身其實也可以做為一種資料描述語言,雖然通用性不若 JSON 來得好,但在 Lua 程式中更加方便。本章展示數個資料序列化的手法;由於資料序列化沒有制式的方法,會隨需求而變,需耐心閱讀本章,從中找出適合自己的方案。
16. Compilation, Execution, and Errors
本章講解幾個和 Lua 運作相關的概念:
- 動態載入
- 預先編譯的位元碼 (bytecode)
- 錯誤處理
雖然不會這些內容還是可以寫 Lua 程式,了解一下對撰寫 Lua 程式會有一些概念。
17. Modules and Packages
當程式變大後,把全部程式寫在同一個命令稿會不易管理;此外,把程式以模組拆開也利於重覆使用。本文介紹 Lua 模組的使用和撰寫,算是重要的章節,可以花時間閱讀。
III. Lua-ism
本部仍在講解 Lua 語法,但屬於較進階的部分。筆者個人以為,除了物件導向程式比較重要外,其他的部分就儘量學習即可,如果不習慣這些特性,不使用仍可撰寫 Lua 程式。
18. Iterators and the Generic for
本章講述撰寫迭代器 (iterators) 的方法以及如何用 for
來走訪迭代器。本章較偏函數式程式設計,如果不習慣這種程式的寫法,可以用其他寫法來代替。
19. Interlude: Markov Chain Algorithom
本章展示如何以 Lua 實作馬可夫鏈,這個演算法相對較難,如果用不到的話,欣賞一下即可。
20. Metatables and Metamethods
Metatable 是 Lua 中用來實作物件導向程式的機制,而 metamethods 則是 Lua 中實作運算子重載的機制,兩者都算是重要的概念。原作者特別把這些內容獨立在一章中,為物件導向程式進行暖身。
21. Object-oriented Programming
沿續上一章的內容,本章講述物件導向程式的寫法。Lua 的物件算是輕量級的,沒有實作所有的物件導向特性,在寫物件時需注意。這兩章應該是這一部中最重要的章節,需仔細閱讀。
22. The Environment
本章說明 Lua 處理全域變數的機制。重點在於利用 _ENV
變數減少命令空間的汙染;另外,應儘量減少全域變數的使用。對於中大型程式來說,全域變數會變成重要的議題。
23. Garbage
本章講述如何在 Lua 中控制垃圾回收相關的議題。一開始寫程式時不會立即用到這個部分,但程式碰到瓶頸時,可以回頭看一看這個部分,看能不能對程式做出一些改善措施。
24. Coroutines
Coroutine 類似於線程 (thread),不過 coroutine 只能單線程運作,這是和線程最大的差異。在 Lua 中 coroutine 有數個用途,像是寫迭代器、寫事件導向程式、寫協同性多工程式等。Coroutine 算是比較進階的概念,一開始無法立即掌握也無妨。
25. Reflection
透過 Lua 的 reflection,我們可以把程式當成資料處理,例如,利用 debug.getinfo
函式來得到函式的元數據 (metadata)。Reflection 多用於除錯 (debugging) 和效能分析 (profiling),我們平常不會用到 reflection 相關的功能。此外,reflection 相關功能可能會拖累程式效能。如果一開始覺得陌生也無妨,因為不會很常用到。
26. Interlude: Multithreading with Coroutines
本章展示如何用 coroutine 寫多線程程式,本章的特點在於利用網路下載的等待時間執行下一段程式,藉以達成有限度的多線程程式。這個範例算比較進階的用法,一開始無法掌握也無妨。
IV. The C API
本部講解如何在 C 程式中嵌入 Lua,如果沒有要在宿主軟體中嵌入 Lua 的話,這部可以略過不看。
由於 Lua 在不同版本間的 C API 有一些不相容,一開始時就要選擇好 Lua 的版本。有些程式人會偏好 LuaJIT 這個非官方的 Lua 實作品,因 LuaJIT 比起 Lua 有更好的效能 (參考這裡),LuaJIT 約略介於 Lua 5.1 和 5.2 版之間。本書的 Lua 目標版本是 5.3,如果要使用別的版本,仍然可以透過本部來學 Lua C API,但要查閱一下其他版本的 API 手冊。
27. An Overview of the C API
本章講述 Lua C API 的基本寫法,重點在於操作 Lua 狀態機。Lua 狀態機以堆疊為基礎,Lua 程式的數據都儲存在這個狀態機中,不會給宿主 C 程式帶來額外的全域變數。本章算是蹲馬步,看完本章還沒辦法直接用這套 API 寫應用程式。
28. Extending Your Application
本章用三個情境來說明如何以 Lua 擴展 C 程式的功能:
- 讀取常數
- 讀取表 (tables)
- 讀取函式 (functions)
透過這些手法,之後我們只要透過修改該 Lua 命令稿就可以更動應用程式的功能,不需重新編譯整個應用程式。
29. Calling C from Lua
在本章中,我們學習如何以 C 撰寫 Lua 函式,透過這些手法,我們可以擴展 Lua 程式的行為。這章剛好和上一章相反,我們在宿主軟體中撰寫函式來擴展 Lua 程式的行為,達到原本 Lua 標準函式庫沒有的行為。
30. Techniques for Writing C Functions
本章沿續前一章,講述一些用於撰寫函式的輔助 API,針對以下情境:
- 操作陣列
- 操作字串
- 儲存函式的狀態
31. User-Defined Types in C
本章學習如何用 C 撰寫 Lua 自定義型別,也就是在 Lua 程式中的 user data。由於自定義型別可以用物件導向的方式來使用,比起函式是更高一階的擴展。
32. Managing Resources
在本章中,我們撰寫可控制外部資源的宿主程式,再將這些程式包裝成 Lua 可用的函式或物件。一些實例像是開啟資料夾或解析 XML 文件等。
33. Threads and States
本章探討如何在 Lua C API 層級撰寫多線程程式。這裡有兩種意義,一種是 coroutine,一種是系統級的多線程程式;前者以 Lua C API 即可完成,後者則需依賴系統上的多線程函式庫。本章提供一個以 POSIX Threads 實作的多線程程式。
結語
雖然 300 多頁乍看不是很多,本書的文字相當紮實,細細閱讀得花上一些時間。本書注重原理講解,大部分程式碼範例偏短且不是完整的程式,而是程式碼片段。歐美的程式設計書籍多用這種風格來寫,好處是易抓到重點,但要自己去花時間寫範例程式,以確認自己所學和書上的知識相符。