• Lua 基础知识
    • (1) 变量
      • 赋值
      • 局部变量
      • 代码块(block)
    • (2) 类型
    • (3) 表达式
      • 操作符
      • 优先级:
      • 表的构造:
      • 多重赋值和多返回值
    • (4) 控制流
      • if
      • for
      • while
      • repeat-until
      • break 和 return
    • (5) C/C++ 中的 Lua
    • 导航

    Lua 基础知识

    (1) 变量

    赋值

    赋值是改变一个变量的值和改变表域的最基本的方法。Lua 中的变量没有类型,只管赋值即可。比如在 Lua 命令行下输入:

    1. end_of_world = "death"
    2. print(end_of_world)
    3. end_of_world = 2012
    4. print(end_of_world)

    上面这四行代码 Lua 不会报错,而会输出:

    1. death
    2. 2012

    局部变量

    使用 local 创建一个局部变量,与全局变量不同,局部变量只在被声明的那个代码块内有效

    1. x = 10
    2. local i = 1 -- 局部变量
    3. while i<=x do
    4. local x = i*2 -- while 中的局部变量
    5. print(x) --> 2, 4, 6, 8, ...
    6. i = i + 1
    7. end

    应该尽可能的使用局部变量,有两个好处:

    1. 避免命名冲突
    2. 访问局部变量的速度比全局变量更快

    代码块(block)

    代码块指一个控制结构内,一个函数体,或者一个chunk(变量被声明的那个文件或者文本串)。

    我们给block划定一个明确的界限:do..end内的部分。当你想更好的控制局部变量的作用范围的时候这是很有用的。

    1. do
    2. local a2 = 2*a
    3. local d = sqrt(b^2 - 4*a*c)
    4. x1 = (-b + d)/a2
    5. x2 = (-b - d)/a2
    6. end -- scope of 'a2' and 'd' ends here
    7. print(x1, x2)

    (2) 类型

    虽说变量没有类型,但并不是说数据不分类型。Lua 基本数据类型共有八个:nil、boolean、number、string、function、userdata、thread、table。

    • Nil Lua中特殊的类型,他只有一个值:nil;一个全局变量没有被赋值以前默认值为nil;给全局变量赋nil可以删除该变量。
    • Booleans 两个取值false和true。但要注意Lua中所有的值都可以作为条件。在控制结构的条件中除了false和nil为假,其他值都为真。所以Lua认为0和空串都是真。
    • Numbers 即实数,Lua 中的所有数都用双精度浮点数表示。
    • Strings 字符串类型,指字符的序列,Lua中字符串是不可以修改的,你可以创建一个新的变量存放你要的字符串。
    • Table 是很强大的数据结构,也是 Lua 中唯一的数据结构。可以看作是数组或者字典。
    • Function 函数是第一类值(和其他变量相同),意味着函数可以存储在变量中,可以作为函数的参数,也可以作为函数的返回值。
    • Userdata userdata可以将C数据存放在Lua变量中,userdata在Lua中除了赋值和相等比较外没有预定义的操作。userdata用来描述应用程序或者使用C实现的库创建的新类型。例如:用标准I/O库来描述文件。
    • Thread 线程会在其它章节来介绍。

    可以用 type 函数取得表达式的数据类型:

    1. print(type(undefined_var))
    2. print(type(true))
    3. print(type(3.14))
    4. print(type('Hello World'))
    5. print(type(type))
    6. print(type({}))

    (3) 表达式

    操作符

    1. 算术运算符:+ - * / ^ (加减乘除幂)
    2. 关系运算符:< > <= >= == ~=
    3. 逻辑运算符:and or not
    4. 连接运算符:..

    有几个操作符跟C语言不一样的:

    • a ~= b 即 a 不等于 b
    • a ^ b 即 a 的 b 次方
    • a .. b 将 a 和 b 作为字符串连接

    优先级:

    1. ^
    2. not -(负号)
    3. * /
    4. + -
    5. ..
    6. < > <= >= ~= ==
    7. and
    8. or

    表的构造:

    最简单的构造函数是{},用来创建一个空表。可以直接初始化数组:

    1. days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}

    Lua将”Sunday”初始化days[1](第一个元素索引为1),不推荐数组下标以0开始,否则很多标准库不能使用。

    在同一个构造函数中可以数组风格和字典风格进行初始化:

    1. polyline = {color="blue", thickness=2, npoints=4,
    2. {x=0, y=0},
    3. {x=-10, y=0},
    4. {x=-10, y=1},
    5. {x=0, y=1}
    6. }

    多重赋值和多返回值

    另外 Lua 还支持多重赋值(还支持函数返回多个值)。也就是说:等号右边的值依次赋值给等号左边的变量。比如:

    1. year, month, day = 2011, 3, 12
    2. print(year, month, day)
    3. reutrn year, month, day -- 多返回值
    4. a, b = f()

    于是,交换两个变量值的操作也变得非常简单:

    1. a, b = b, a

    (4) 控制流

    if

    1. name = "peach"
    2. if name == "apple" then
    3. -- body
    4. elseif name == "banana" then
    5. -- body
    6. else
    7. -- body
    8. end

    for

    1. -- 初始值, 终止值, 步长
    2. for i=1, 10, 2 do
    3. print(i)
    4. end
    5. -- 数组
    6. for k, v in ipairs(table) do
    7. print(k, v)
    8. end
    9. -- 字典
    10. for k, v in pairs(table) do
    11. print(k, v)
    12. end

    反向表构造实例:

    1. revDays = {}
    2. for i,v in ipairs(days) do
    3. revDays[v] = i
    4. end

    while

    1. while i<10 do
    2. print(i)
    3. i = i + 1
    4. end

    repeat-until

    1. repeat
    2. print(i)
    3. i = i + 1
    4. until i < 10

    break 和 return

    break 语句可用来退出当前循环(for, repeat, while),循环外部不可以使用。

    return 用来从函数返回结果,当一个函数自然结束,结尾会有一个默认的return。

    Lua语法要求break和return只能出现在block的结尾一句(也就是说:作为chunk的最后一句,或者在end之前,或者else前,或者until前):

    1. local i = 1
    2. while a[i] do
    3. if a[i] == v then break end
    4. i = i + 1
    5. end

    (5) C/C++ 中的 Lua

    首先是最简单的 Lua 为 C/C++ 程序变量赋值,类似史前的 INI 配置文件。

    1. width = 640
    2. height = 480

    这样的赋值即设置全局变量,本质上就是在全局表中添加字段。

    在 C/C++ 中,Lua 其实并不是直接去改变变量的值,而是宿主程序通过「读取脚本中设置的全局变量到栈、类型检查、从栈上取值」几步去主动查询。

    1. int w, h;
    2. if (luaL_loadfile(L, fname) || // 读取文件,将内容作为一个函数压栈
    3. lua_pcall(L, 0, 0, 0)) // 执行栈顶函数,0个参数、0个返回值、无出错处理函数(出错时直接把错误信息压栈)
    4. error();
    5. lua_getglobal(L, "width"); // 将全局变量 width 压栈
    6. lua_getglobal(L, "height"); // 将全局变量 height 压栈
    7. if (!lua_isnumber(L, -2)) // 自顶向下第二个元素是否为数字
    8. error();
    9. if (!lua_isnumber(L, -1)) // 自顶向下第一个元素是否为数字
    10. error();
    11. w = lua_tointeger(L, -2); // 自顶向下第二个元素转为整型返回
    12. h = lua_tointeger(L, -1); // 自顶向下第一个元素转为整型返回

    读取表的字段的操作也是类似,只不过细节上比较麻烦,有点让我想起在汇编里调戏各种寄存器:

    1. score = { chinese=80, english=85 }
    2. int chinese, english;
    3. if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0, 0))
    4. error();
    5. lua_getglobal(L, "score"); // 全局变量 score 压栈
    6. lua_pushstring(L, "chinese"); // 字符串 math 压栈
    7. lua_gettable(L, -2); // 以自顶向下第二个元素为表、第一个元素为索引取值,弹栈,将该值压栈
    8. if (!lua_isnumber(L, -1)) // 栈顶元素是否为数字
    9. error();
    10. chinese = lua_tointeger(L, -2);
    11. lua_pop(L, 1); // 弹出一个元素 (此时栈顶为 score 变量)
    12. lua_getfield(L, -1, "english"); // Lua5.1开始提供该函数简化七八两行
    13. if (!lua_isnumber(L, -1))
    14. error();
    15. english = lua_tointeger(L, -2);
    16. lua_pop(L, 1); // 如果就此结束,这一行弹不弹都无所谓了

    前面说过,设置全局变量本质就是在全局表中添加字段,所以 lua_getglobal 函数本质是从全局表中读取字段。没错,lua_getglobal 本身就是一个宏:

    1. #define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, s)

    宏 LUA_GLOBALSINDEX 指明的就是全局表的索引。

    导航

    • 目录
    • 下一章:环境与模块