本頁使用了標題或全文手工轉換

Lua

維基百科,自由的百科全書
跳至導覽 跳至搜尋
Lua
Lua-Logo.svg
編程範型多範式指令碼指令式程序式基於原型物件導向),函數式
設計者Roberto Ierusalimschy
Waldemar Celes
Luiz Henrique de Figueiredo
面市時間1993年
穩定版本
5.4.1
( 2020年10月9日,​51天前​(2020-10-09
型態系統動態, 強型別, 鴨子
實作語言ANSI C
作業系統跨平台
授權條款MIT授權條款
副檔名.lua
網站www.lua.org
主要實作產品
Lua, LuaJIT, LuaVela
衍生副語言
Metalua, Idle, GSL Shell, Luau
啟發語言
SchemeSNOBOLModulaCLUC++
影響語言
GameMonkey, Io, JavaScript, Julia, MiniD, Red, Ring[1], Ruby, Squirrel, MoonScript, C--

Lua發音: /ˈlə/,葡萄牙語含義是月亮)是一個簡潔、輕量、可延伸的手稿語言。Lua有著相對簡單的C API而很容易嵌入應用中[2]。很多應用程式使用Lua作為自己的嵌入式手稿語言,以此來實現可組態性、可延伸性[3]

歷史[編輯]

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

在《The Evolution of Lua》中,這門語言的作者寫道[5]

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

Lua的控制結構語法英語Syntax (programming languages)大部份引入自Modulaifwhilerepeat/until),但也受到來自CLU(多賦值和從函式呼叫的多個返回值,是對參照參數或顯式指標的更簡單的替代)、C++(「允許局部變數只在需要的地方聲明的靈巧想法」[5])、SNOBOLAWK關聯陣列)的影響。在發表於《Dr. Dobb's Journal英語Dr. Dobb's Journal》的文章中,Lua的創立者還聲稱,有著單一且無所不在的資料結構機制(列表)的LISPScheme,對他們決定將表格開發為Lua的主要資料結構起到了主要影響[6]

Lua的語意久而久之日趨受到Scheme的影響[5],特別是介入了匿名函式和完全的詞法作用域。Lua在版本5.0之前在類似BSD授權條款之下發行。自從版本5.0以來,Lua採用了MIT授權條款

特性[編輯]

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

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

Lua是一種多重編程範式的程式設計語言:它只提供了很小的一個特性集合來滿足不同編程範式的需要,而不是為某種特定的編程範式提供繁雜的特性支援。例如,Lua並不提供繼承這個特性,但是你可以用元表格來類比它。諸如命名空間這些概念都沒有在語言基本特性中實現,但是我們可以用表格結構(Lua唯一提供的複雜資料結構)輕易類比。Lua可以在執行時隨時構造出一個函式,並把它看作一個物件(正所謂頭等函式),這個特性可以很好的滿足函式語言程式設計的需要。正是提供了這些基本的元特性,我們可以任意的對語言進行自需的改造。

語法[編輯]

經典的Hello World!程式可以寫為如下[8]

print("Hello World!")

或者如下:

print 'Hello World!'

在Lua中注釋可以於雙連字元並列至此行的結束,類似於AdaEiffelHaskellSQLVHDL。多行字串和注釋用雙方括號來裝飾。

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

function factorial(n)
  local x = 1
  for i = 2, n do
    x = x * i
  end
  return x
end

控制流[編輯]

Lua有四種類型的迴圈:the while迴圈repeat迴圈(類似於do while迴圈),數值for迴圈,和通用for迴圈,和條件語句if exp then ... {elseif exp then ...} [else ...] end

--condition = true

while condition do
  --statements
end

repeat
  --statements
until condition

for i = first, last, delta do  --delta可以是负数,允许计数增加或减少的循环
  --statements
  --example: print(i)
end

通用for迴圈:

for key, value in pairs(_G) do
  print(key, value)
end

將在表格_G上使用標準疊代器函式pairs進行疊代,直到它返回nil

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

local grid = {
  { 11, 12, 13 },
  { 21, 22, 23 },
  { 31, 32, 33 }
}

for y, row in ipairs(grid) do
  for x, value in ipairs(row) do
    print(x, y, grid[y][x])
  end
end

函式[編輯]

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

do
  local oldprint = print
  -- 存储当前的print函数为oldprint
  function print(s)
    --[[重新定义print函数。新函数只有一个实际参数。
      平常的print函数仍可以通过oldprint使用。]]
    oldprint(s == "foo" and "bar" or s)
  end
end

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

Lua還支援閉包,展示如下:

function addto(x)
  -- 返回一个把实际参数加到x上
  return function(y)
    --[=[ 在我们引用变量x的时候,它在当前作用域的外部,
      它的生命期会比这个匿名函数短,Lua建立一个闭包。]=]
    return x + y
  end
end
fourplus = addto(4)
print(fourplus(3))  -- 打印7

--这也可以通过如下方式调用这个函数来完成:
print(addto(4)(3))
--[[这是因为这直接用视件参数3调用了从addto(4)返回的函数。
    如果经常这么调用的话会减少数据消耗并提升性能。]]

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

function create_a_counter()
    local count = 0
    return function()
        count = count + 1
        return count
    end
end

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

變數類型[編輯]

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

有8種基本類型:nil、布林值(boolean)、數字型(number)、字串型(string)、用戶自訂類型(userdata)、函式(function)、執行緒(thread)和表(table)。

    print(type(nil))                    -- 输出 nil
    print(type(99.7+12*9))              -- 输出 number
    print(type(true))                   -- 输出 boolean
    print(type("Hello Wikipedia"))      -- 输出 string
    print(type(print))                  -- 输出 function
    print(type{1, 2, test = "test"})    -- 输出 table

表格[編輯]

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

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

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

a_table = {} -- 建立一个新的空表格

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

鍵(索引)可以是除了nilNaN的任何值,包括函式。

a_table = {x = 10}  -- 建立一个新表格,具有映射"x"到数10的一个表项。
print(a_table["x"]) -- 打印于这个字符串关联的值,这里是10。
b_table = a_table
b_table["x"] = 20   -- 在表格中的值变更为20。
print(b_table["x"]) -- 打印20。
print(a_table["x"]) -- 还是打印20,因为a_table和b_table都引用相同的表格。

通過使用字串作為鍵,表格經常用作結構英語object composition(或記錄)。由於這種用法太常見,Lua為存取這種欄位提供了特殊語法[9]

point = { x = 10, y = 20 }   -- 建立一个新表格
print(point["x"])            -- 打印10
print(point.x)               -- 同上一行完全相同含义。易读的点只是语法糖。

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

Point = {}

Point.new = function(x, y)
  return {x = x, y = y}  --  return {["x"] = x, ["y"] = y}
end

Point.set_x = function(point, x)
  point.x = x  --  point["x"] = x;
end

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

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

array = { "a", "b", "c", "d" }   -- 索引被自动赋予。
print(array[2])                  -- 打印"b"。在Lua中自动索引开始于1。
print(#array)                    -- 打印4。#是表格和字符串的长度算符。
array[0] = "z"                   -- 0是合法索引。
print(#array)                    -- 仍打印4,因为Lua数组是基于1的。

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

ExampleTable =
{
  {1, 2, 3, 4},
  {5, 6, 7, 8}
}
print(ExampleTable[1][3]) -- 打印"3"
print(ExampleTable[2][4]) -- 打印"8"

表格可以是物件的陣列。

function Point(x, y)        -- "Point"对象构造器
  return { x = x, y = y }   -- 建立并返回新对象(表格)
end
array = { Point(10, 20), Point(30, 40), Point(50, 60) }   -- 建立point的数组
                        -- array = { { x = 10, y = 20 }, { x = 30, y = 40 }, { x = 50, y = 60 } };
print(array[2].y)                                         -- 打印40

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

元表格[編輯]

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

fibs = { 1, 1 }                                -- 给fibs[1]和fibs[2]初始值。
setmetatable(fibs, {
  __index = function(values, n)                --[[__index是Lua预定义的函数, 
                                                   如果"n"不存在则调用它。]]
    values[n] = values[n - 1] + values[n - 2]  -- 计算并记忆化fibs[n]。
    return values[n]
  end
})

物件導向程式設計[編輯]

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

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

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

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

local Vector = {}
Vector.__index = Vector

function Vector:new(x, y, z)    -- 构造器
  return setmetatable({x = x, y = y, z = z}, Vector)
end

function Vector:magnitude()     -- 另一个方法
  -- 使用self引用隐蔽的对象
  return math.sqrt(self.x^2 + self.y^2 + self.z^2)
end

local vec = Vector:new(0, 1, 0) -- 建立一个向量
print(vec:magnitude())          -- 调用一个法方法 (输出: 1)
print(vec.x)                    -- 访问一个成员变量 (输出: 0)

實現[編輯]

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

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

下面的例子列出上面定義的階乘函式的位元組碼(通過luac 5.1編譯器來展示)[14]

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核心庫和輔助庫[15]。Lua API的設計消除了對用C代碼的手動參照管理的需要,不同於Python的API。API就像語言本身一樣是極簡主義的。進階功能通過輔助庫來提供,它們很大程度上構成自預處理器,用以幫助做複雜的表格操作。

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

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

#include <stdio.h>
#include <lua.h> // Lua主要库 (lua_*)
#include <lauxlib.h> // Lua辅助库 (luaL_*)

int main(void)
{
    // 建立一个Lua状态
    lua_State *L = luaL_newstate();

    // 装载并执行一个字符串
    if (luaL_dostring(L, "function foo (x,y) return x+y end")) {
        lua_close(L);
        return -1;
    }

    // 压入全局"foo"(上面定义的函数)的值
    // 到堆栈,跟随着整数5和3
    lua_getglobal(L, "foo");
    lua_pushinteger(L, 5);
    lua_pushinteger(L, 3);
    lua_call(L, 2, 1); // 调用有二个实际参数和一个返回值的函数
    printf("Result: %d\n", lua_tointeger(L, -1)); // 打印在栈顶的项目的整数值
    lua_pop(L, 1); // 返回堆栈至最初状态
    lua_close(L); // 关闭Lua状态
    return 0;
}

如下這樣執行這個例子:

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

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

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

應用[編輯]

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

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

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

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

Lua可以用於嵌入式硬體,不僅可以嵌入其他程式語言,而且可以嵌入微處理器中,例如NodeMCU開源硬體項目將Lua嵌入到Wi-Fi SoC中[25]

編譯成Lua的語言[編輯]

參考資料[編輯]

  1. ^ Ring Team. The Ring programming language and other languages. ring-lang.net. ring-lang. 5 December 2017. 
  2. ^ Yuri Takhteyev. From Brazil to Wikipedia. Foreign Affairs. 21 April 2013 [25 April 2013]. 
  3. ^ 3.0 3.1 Ierusalimschy, Roberto; de Figueiredo, Luiz Henrique; Filho, Waldemar Celes. Lua—An Extensible Extension Language. Software: Practice and Experience. June 1996, 26 (6): 635–652 [24 October 2015]. doi:10.1002/(SICI)1097-024X(199606)26:6<635::AID-SPE26>3.0.CO;2-P. 
  4. ^ The evolution of an extension language: a history of Lua. 2001 [2008-12-18]. 
  5. ^ 5.0 5.1 5.2 Ierusalimschy, R.; Figueiredo, L. H.; Celes, W. The evolution of Lua (PDF). Proc. of ACM HOPL III. 2007: 2–1–2–26. ISBN 978-1-59593-766-7. doi:10.1145/1238844.1238846. 
  6. ^ Figueiredo, L. H.; Ierusalimschy, R.; Celes, W. Lua: an Extensible Embedded Language. A few metamechanisms replace a host of features. Dr. Dobb's Journal 21 (12). December 1996: 26–33. 
  7. ^ 7.0 7.1 7.2 About Lua. Lua.org. [2011-08-11]. 
  8. ^ Programming in Lua : 1. 
  9. ^ Lua 5.1 Reference Manual. 2014 [2014-02-27]. 
  10. ^ Lua 5.1 Reference Manual. 2012 [2012-10-16]. 
  11. ^ Lua 5.1 Source Code. 2006 [2011-03-24]. 
  12. ^ Ierusalimschy, R.; Figueiredo, L. H.; Celes, W. The implementation of Lua 5.0. J. Of Universal Comp. Sci. 2005, 11 (7): 1159–1176. 
  13. ^ Texas Instruments. PC Scheme: Users Guide and Language Reference Manual, Trade Edition. 1990. ISBN 0-262-70040-9. 
  14. ^ Kein-Hong Man. A No-Frills Introduction to Lua 5.1 VM Instructions (PDF). 2006 [20 December 2008]. (原始內容 (PDF)存檔於19 June 2010). 
  15. ^ 15.0 15.1 Lua 5.2 Reference Manual. Lua.org. [2012-10-23]. 
  16. ^ Changes in the API. Lua 5.2 Reference Manual. Lua.org. [2014-05-09]. 
  17. ^ LuaRocks. LuaRocks wiki. [2009-05-24]. 
  18. ^ Binding Code To Lua. Lua-users wiki. [2009-05-24]. 
  19. ^ Why is Lua considered a game language?. [2017-04-22]. (原始內容存檔於20 August 2013). 
  20. ^ Poll Results. [2017-04-22]. (原始內容存檔於7 December 2003). 
  21. ^ Front Line Award Winners Announced. [2017-04-22]. (原始內容存檔於15 June 2013). 
  22. ^ Extension:Scribunto - MediaWiki. MediaWiki.org. [21 February 2019]. 
  23. ^ Wikipedia:Lua. [2018-12-19]. 
  24. ^ Wikidata:Infobox Tutorial - Wikidata. www.wikidata.org. [2018-12-21]. 
  25. ^ Huang R. NodeMCU devkit. Github. [3 April 2015]. 
  26. ^ Language Guide - MoonScript 0.5.0. moonscript.org. [2020-09-25]. 
  27. ^ leaf, leafo/moonscript, 2020-09-23 [2020-09-25] 
  28. ^ 28.0 28.1 28.2 28.3 Andre Alves Garzia. Languages that compile to Lua. AndreGarzia.com. [September 25, 2020]. 

延伸閱讀[編輯]

外部連結[編輯]