LuaLua入门


备注

Lua标志

Lua是简约,轻量级和可嵌入的脚本语言。它由巴西里约热内卢罗马天主教大学PUC-Rio团队设计,实施和维护。 邮件列表是开放的参与。

Lua的常见用例包括脚本视频游戏,使用插件和配置扩展应用程序,包装一些高级业务逻辑或仅嵌入到电视,汽车等设备中。

对于高性能任务,使用称为LuaJIT的即时编译器进行独立实现。

版本

笔记发布日期
1.0 最初的非公开发布。 1993年7月28日
1.1 首次公开发布。 会议论文描述它。 1994年7月8日
2.1 从Lua 2.1开始,Lua可以免费用于各种用途,包括商业用途。 期刊论文描述它。 1995年2月7日
2.2 长字符串,调试接口,更好的堆栈回溯 1995年11月28日
2.4 外部luac编译器 1996年5月14日
2.5 模式匹配和vararg函数。 1996-11-19
3.0 引入了auxlib,一个帮助编写Lua库的库 1997-07-01
3.1 通过“upvalues”匿名函数和函数闭包。 1998年7月11日
3.2 调试库和新表函数 1999年7月8日
3.2.2 2000年2月22日
4 多个状态,“for”语句,API改进。 2000年11月6日
4.0.1 2002年7月4日
5 协同程序,元数据,完整词汇范围,尾部调用,布尔值转移到麻省理工学院许可证。 2003-04-11
5.0.3 2006-06-26
5.1 模块系统改造,增量垃圾收集器,所有类型的元表,luaconf.h改造,完全可重入的解析器,可变参数。 2006-02-21
5.1.5 2012-02-17
5.2 紧急垃圾收集器,goto,表的终结器。 2011-12-16
5.2.4 2015年3月7日
5.3 基本的UTF-8支持,按位运算,32/64位整数。 2015-01-12
5.3.4 最新版本。 2017年1月12日

评论

Lua中的单行注释从-- 开始直到行尾:

-- this is single line comment
-- need another line
-- huh?
 

阻止评论以--[[ 和结束]] 开头:

--[[
    This is block comment.
    So, it can go on...
    and on...
    and on....
]]
 

块注释使用与长字符串相同的分隔符样式;括号之间可以添加任意数量的等号以界定注释:

--[=[
    This is also a block comment
    We can include "]]" inside this comment
--]=]

--[==[
    This is also a block comment
    We can include "]=]" inside this comment
--]==]
 

注释掉代码块的一个巧妙方法是用--[[--]] 包围它:

--[[
    print'Lua is lovely'
--]]
 

要重新激活块,只需在注释开始序列中附加-

---[[
    print'Lua is lovely'
--]]
 

这样,序列-- 在第一行开始单行注释,就像最后一行一样,并且没有注释掉print 语句。

更进一步,可以设置两个代码块,如果第一个块被注释掉,第二个块将不会,反之亦然:

---[[
  print 'Lua is love'
--[=[]]
  print 'Lua is life'
--]=]
 

要在禁用第一个块时激活第二个块,请删除第一行的前导-

--[[
  print 'Lua is love'
--[=[]]
  print 'Lua is life'
--]=]
 

执行Lua程序

通常,Lua附带两个二进制文件:

  • lua - 独立解释器和交互式shell
  • luac - 字节码编译器

假设我们有一个示例程序( bottles_of_mate.lua ),如下所示:

local string = require "string"    

function bottle_take(bottles_available)

    local count_str = "%d bottles of mate on the wall."
    local take_str = "Take one down, pass it around, " .. count_str
    local end_str = "Oh noes, " .. count_str
    local buy_str = "Get some from the store, " .. count_str
    local bottles_left = 0

    if bottles_available > 0 then
         print(string.format(count_str, bottles_available))
         bottles_left = bottles_available - 1
         print(string.format(take_str, bottles_left))
    else
        print(string.format(end_str, bottles_available))
        bottles_left = 99
        print(string.format(buy_str, bottles_left))
    end

    return bottles_left
end

local bottle_count = 99

while true do
    bottle_count = bottle_take(bottle_count)
end
 

可以通过在shell上执行以下命令来运行程序本身:

$ lua bottles_of_mate.lua
 

输出应该如下所示,在无限循环中运行:

Get some from the store, 99 bottles of mate on the wall.
99 bottles of mate on the wall.
Take one down, pass it around, 98 bottles of mate on the wall.
98 bottles of mate on the wall.
Take one down, pass it around, 97 bottles of mate on the wall.
97 bottles of mate on the wall.
...
...
3 bottles of mate on the wall.
Take one down, pass it around, 2 bottles of mate on the wall.
2 bottles of mate on the wall.
Take one down, pass it around, 1 bottles of mate on the wall.
1 bottles of mate on the wall.
Take one down, pass it around, 0 bottles of mate on the wall.
Oh noes, 0 bottles of mate on the wall.
Get some from the store, 99 bottles of mate on the wall.
99 bottles of mate on the wall.
Take one down, pass it around, 98 bottles of mate on the wall.
...
 

您可以通过在您的shell上执行以下命令将程序编译为Lua的字节码:

$ luac bottles_of_mate.lua -o bottles_of_mate.luac
 

字节码列表也可通过以下方式执行:

$ luac -l bottles_of_mate.lua


main <./bottles.lua:0,0> (13 instructions, 52 bytes at 0x101d530)
0+ params, 4 slots, 0 upvalues, 2 locals, 4 constants, 1 function
    1    [1]    GETGLOBAL    0 -1    ; require
    2    [1]    LOADK        1 -2    ; "string"
    3    [1]    CALL         0 2 2
    4    [22]    CLOSURE      1 0    ; 0x101d710
    5    [22]    MOVE         0 0
    6    [3]    SETGLOBAL    1 -3    ; bottle_take
    7    [24]    LOADK        1 -4    ; 99
    8    [27]    GETGLOBAL    2 -3    ; bottle_take
    9    [27]    MOVE         3 1
    10    [27]    CALL         2 2 2
    11    [27]    MOVE         1 2
    12    [27]    JMP          -5    ; to 8
    13    [28]    RETURN       0 1

function <./bottles.lua:3,22> (46 instructions, 184 bytes at 0x101d710)
1 param, 10 slots, 1 upvalue, 6 locals, 9 constants, 0 functions
    1    [5]    LOADK        1 -1    ; "%d bottles of mate on the wall."
    2    [6]    LOADK        2 -2    ; "Take one down, pass it around, "
    3    [6]    MOVE         3 1
    4    [6]    CONCAT       2 2 3
    5    [7]    LOADK        3 -3    ; "Oh noes, "
    6    [7]    MOVE         4 1
    7    [7]    CONCAT       3 3 4
    8    [8]    LOADK        4 -4    ; "Get some from the store, "
    9    [8]    MOVE         5 1
    10    [8]    CONCAT       4 4 5
    11    [9]    LOADK        5 -5    ; 0
    12    [11]    EQ           1 0 -5    ; - 0
    13    [11]    JMP          16    ; to 30
    14    [12]    GETGLOBAL    6 -6    ; print
    15    [12]    GETUPVAL     7 0    ; string
    16    [12]    GETTABLE     7 7 -7    ; "format"
    17    [12]    MOVE         8 1
    18    [12]    MOVE         9 0
    19    [12]    CALL         7 3 0
    20    [12]    CALL         6 0 1
    21    [13]    SUB          5 0 -8    ; - 1
    22    [14]    GETGLOBAL    6 -6    ; print
    23    [14]    GETUPVAL     7 0    ; string
    24    [14]    GETTABLE     7 7 -7    ; "format"
    25    [14]    MOVE         8 2
    26    [14]    MOVE         9 5
    27    [14]    CALL         7 3 0
    28    [14]    CALL         6 0 1
    29    [14]    JMP          15    ; to 45
    30    [16]    GETGLOBAL    6 -6    ; print
    31    [16]    GETUPVAL     7 0    ; string
    32    [16]    GETTABLE     7 7 -7    ; "format"
    33    [16]    MOVE         8 3
    34    [16]    MOVE         9 0
    35    [16]    CALL         7 3 0
    36    [16]    CALL         6 0 1
    37    [17]    LOADK        5 -9    ; 99
    38    [18]    GETGLOBAL    6 -6    ; print
    39    [18]    GETUPVAL     7 0    ; string
    40    [18]    GETTABLE     7 7 -7    ; "format"
    41    [18]    MOVE         8 4
    42    [18]    MOVE         9 5
    43    [18]    CALL         7 3 0
    44    [18]    CALL         6 0 1
    45    [21]    RETURN       5 2
    46    [22]    RETURN       0 1
 

入门

变量

var = 50 -- a global variable
print(var) --> 50
do
  local var = 100 -- a local variable
  print(var) --> 100
end
print(var) --> 50
-- The global var (50) still exists 
-- The local var (100) has gone out of scope and can't be accessed any longer.
 

类型

num = 20 -- a number
num = 20.001 -- still a number
str = "zaldrizes buzdari iksos daor" -- a string
tab = {1, 2, 3} -- a table (these have their own category)
bool = true -- a boolean value
bool = false -- the only other boolean value
print(type(num)) --> 'number'
print(type(str)) --> 'string'
print(type(bool)) --> 'boolean'
type(type(num)) --> 'string'

-- Functions are a type too, and first-class values in Lua.
print(type(print)) --> prints 'function'
old_print = print
print = function (x) old_print "I'm ignoring the param you passed me!" end
old_print(type(print)) --> Still prints 'function' since it's still a function.
-- But we've (unhelpfully) redefined the behavior of print.
print("Hello, world!") --> prints "I'm ignoring the param you passed me!"
 

特殊类型nil

Lua的另一种类型是nilnil 类型中唯一的值是nilnil 存在与Lua中的所有其他值不同。这是一种非价值的价值。

print(foo) -- This prints nil since there's nothing stored in the variable 'foo'.
foo = 20
print(foo) -- Now this prints 20 since we've assigned 'foo' a value of 20.

-- We can also use `nil` to undefine a variable
foo = nil -- Here we set 'foo' to nil so that it can be garbage-collected.

if nil then print "nil" end --> (prints nothing)
-- Only false and nil are considered false; every other value is true.
if 0 then print "0" end --> 0
if "" then print "Empty string!" --> Empty string!
 

表达式

a = 3
b = a + 20 a = 2 print(b, a) -- hard to read, can also be written as
b = a + 20; a = 2; print(a, b) -- easier to read, ; are optional though
true and true --> returns true
true and 20 --> 20
false and 20 --> false
false or 20 --> 20
true or 20 --> true
tab or {}
  --> returns tab if it is defined
  --> returns {} if tab is undefined
  -- This is useful when we don't know if a variable exists
tab = tab or {} -- tab stays unchanged if it exists; tab becomes {} if it was previously nil.

a, b = 20, 30 -- this also works
a, b = b, a -- switches values
 

定义功能

function name(parameter)
    return parameter
end
print(name(20)) --> 20
-- see function category for more information
name = function(parameter) return parameter end -- Same as above
 

布尔

只有falsenil 评估为false,其他一切,包括0 和空字符串计算为true。

垃圾收集

tab = {"lots", "of", "data"}
tab = nil; collectgarbage()
-- tab does no longer exist, and doesn't take up memory anymore.
 

tab1 = {"a", "b", "c"}
tab2 = tab1
tab2[1] = "d"
print(tab1[1]) --> 'd' -- table values only store references.
--> assigning tables does not copy its content, only the reference.

tab2 = nil; collectgarbage()
print(tab1) --> (prints table address) -- tab1 still exists; it didn't get garbage-collected.

tab1 = nil; collectgarbage()
-- No more references. Now it should actually be gone from memory.
 

这些是基础知识,但是有一个关于具有更多信息的表的部分。

条件

if (condition) then
    -- do something
elseif (other_condition) then
    -- do something else
else
    -- do something
end
 

for循环

Lua中有两种类型的for 循环:数字for 循环和泛型for 循环。

  • 数字for 循环具有以下形式:

    for a=1, 10, 2 do -- for a starting at 1, ending at 10, in steps of 2
      print(a) --> 1, 3, 5, 7, 9
    end
     

    数字for 循环中的第三个表达式是循环递增的步骤。这使得反向循环变得容易:

     for a=10, 1, -1 do
       print(a) --> 10, 9, 8, 7, 6, etc.
     end
     

    如果省略步骤表达式,Lua假定默认步长为1。

     for a=1, 10 do
       print(a) --> 1, 2, 3, 4, 5, etc.
     end
     

    另请注意,循环变量是for 循环的本地变量。循环结束后它将不存在。

  • Generic for 循环遍历迭代器函数返回的所有值:

    for key, value in pairs({"some", "table"}) do
      print(key, value)
      --> 1 some
      --> 2 table
    end
     

    Lua提供了几个内置的迭代器(例如, pairsipairs ),用户可以定义自己的自定义迭代器以及与泛型for 循环一起使用。

做块

local a = 10
do
    print(a) --> 10
    local a = 20
    print(a) --> 20
end
print(a) --> 10
 

你好,世界

这是hello world代码:

print("Hello World!")
 

这个怎么运作?这很简单! Lua执行print() 函数并使用"Hello World" 字符串作为参数。

安装

二进制

Lua二进制文件由大多数GNU / Linux发行版提供。

例如,在Debian,Ubuntu及其衍生产品上,可以通过以下方式获取:

sudo apt-get install lua50
 
sudo apt-get install lua51
 
sudo apt-get install lua52
 

为Windows,MacOS和SourceForge上托管的其他一些操作系统提供了一些半官方版本。

Apple用户也可以使用Homebrew轻松安装Lua:

brew install lua
 

(目前Homebrew有5.2.4,5.3见Homebrew /版本 。)

资源

来源可在官方页面上找到 。获取资源和建立自己应该是​​微不足道的。在Linux系统上,以下内容应该足够了:

$ wget http://lua.org/ftp/lua-5.3.3.tar.gz
$ echo "a0341bc3d1415b814cc738b2ec01ae56045d64ef ./lua-5.3.3.tar.gz" | sha1sum -c -
$ tar -xvf ./lua-5.3.3.tar.gz
$ make -C ./lua-5.3.3/ linux
 

在上面的例子中,我们基本上从官方站点下载源tarball ,验证其校验和,以及提取和执行make 。 (仔细检查官方页面的校验和。)

注意:您必须指定所需的构建目标。在这个例子中,我们指定了linux 。其他可用的构建目标包括solarisaixbsdfreebsdmacosxmingw 等。有关详细信息,请查看源代码中包含的doc/readme.html 。 (您也可以在线找到最新版本的README 。)

模块

标准库仅限于原语:

  • coroutine - 协程管理功能
  • debug - 调试钩子和工具
  • io - 基本IO原语
  • package - 模块管理功能
  • string - 字符串和Lua特定模式匹配功能
  • table - 用于处理基本但复杂的Lua类型的基元 - 表
  • os - 基本的OS操作
  • utf8 - 基本的UTF-8原语(自Lua 5.3起)

可以针对特定构建禁用所有这些库,也可以在运行时加载这些库。

用于分发模块的第三方Lua库和基础架构很少,但正在改进。像LuaRocksLua ToolboxLuaDist这样的项目正在改善这种状况。在较旧的Lua Wiki上可以找到许多信息和许多建议,但请注意,其中一些信息已经过时且已过时。

一些棘手的事情

有时候,Lua在阅读文档后的行为并不像人们想象的那样。其中一些案例是:

没有,没有什么不一样(常见的PITFALL!)

正如所料, table.insert(my_table, 20) 将值20 添加到表中, table.insert(my_table, 5, 20) 在第5个位置添加值20。 table.insert(my_table, 5, nil) 做了什么?有人可能会认为它将nil 视为无参数,并在表的末尾插入值5 ,但它实际上在表的第5个位置添加了值nil 。什么时候出现这个问题?

(function(tab, value, position)
    table.insert(tab, position or value, position and value)
end)({}, 20)
-- This ends up calling table.insert({}, 20, nil)
-- and this doesn't do what it should (insert 20 at the end)
 

tostring() 发生类似的事情:

print (tostring(nil)) -- this prints "nil"
table.insert({}, 20) -- this returns nothing
-- (not nil, but actually nothing (yes, I know, in lua those two SHOULD
-- be the same thing, but they aren't))

-- wrong:
print (tostring( table.insert({}, 20) ))
-- throws error because nothing ~= nil

--right:
local _tmp = table.insert({}, 20) -- after this _tmp contains nil
print(tostring(_tmp)) -- prints "nil" because suddenly nothing == nil
 

使用第三方代码时,这也可能导致错误。例如,如果一些函数的文档说“如果幸运则返回甜甜圈,否则返回nil”,实现可能看起来有点像这样

function func(lucky)
    if lucky then
        return "donuts"
    end
end
 

这个实现起初可能看似合理;它必须返回甜甜圈,当你输入result = func(false) 结果将包含值nil

但是,如果要编写print(tostring(func(false))) lua会抛出一个看起来有点像这个stdin:1: bad argument #1 to 'tostring' (value expected) 的错误stdin:1: bad argument #1 to 'tostring' (value expected)

这是为什么? tostring 明显得到一个论点,即使它是nil 。错误。 func根本不返回任何内容,因此tostring(func(false))tostring() 相同,与tostring(nil)

说“预期价值”的错误强烈表明这可能是问题的根源。

留下数组中的空白

如果你是lua的新手,这是一个巨大的陷阱, 表格类别中有很多信息