Lua语言基础

语言特点

  1. 动态数据类型
  2. 唯一一种数据结构 —— 表
  3. 函数是一等类型,支持匿名函数和正则尾递归
  4. 支持词法定界和闭包
  5. 提供 thread 类型和结构化的协程机制
  6. 运行期能编译字符串形式的程序文本并载入虚拟机执行

参考: https://www.gitbook.com/book/moonbingbing/openresty-best-practices/details

LuaJIT

LuaJIT 是采用 C 和汇编语言编写的 Lua 解释器与即时编译器。LuaJIT 被设计成全兼容标准的 Lua 5.1 语言,同时可选地支持 Lua 5.2 和 Lua 5.3 中的一些不破坏向后兼容性的有用特性。因此,标准 Lua 语言的代码可以不加修改地运行在 LuaJIT 之上。可以说 LuaJIT 是一个高效的 Lua 实现。

利用即时编译(Just-in Time )技术把 Lua 代码编译成本地机器码后交由 CPU 直接执行。在数值运算、循环与函数调用、协程切换、字符串操作等许多方面它的加速效果都很显著。

数据类型

类型 示例
nil nil
boolean true,false
number 3.99
string “hello world”
table {web = “www.google.com”}

字符串还可以用一种长括号(即 [[]],[=[]=],[==[]==],[===[]===],[====[]====])。这种方式描述的字符串可以包含任何东西,当然本级别的反长括号除外。例:[[abc\nbc]] ,里面的 “\n” 不会被转义。

Lua 字符串一般都会经历一个 “ 内化 ”(intern )的过程,即两个完全一样的 Lua 字符串在 Lua 虚拟机中只会存储一份。每一个 Lua 字符串在创建时都会插入到 Lua 虚拟机内部的一个全局的哈希表中。

运算符

算术运算符 说明
+ 加法
- 减法
* 乘法
/ 除法
^ 指数
% 取模
关系运算符 说明
< 小于
> 大于
<= 小于等于
>= 大于等于
== 等于
~= 不等于
逻辑运算符 说明
and 逻辑与
or 逻辑或
not 逻辑非

特殊操作符

  • # 取长度操作符
print(#"hello world") -- 输出 11

对于常规的数组,里面从 1 到 n 放着一些非空的值的时候,它的长度就精确的为 n,即最后一个值的下标。如果数组有一个 “ 空洞 ”(就是说,nil 值被夹在非空值之间),那么 #t 可能是指向任何一个是 nil 值的前一个位置的下标(就是说,任何一个 nil 值都有可能被当成数组的结束)。这也就说明对于有 “ 空洞 ” 的情况,table 的长度存在一定的 不可确定性。

  • .. 字符串连接符
print("Hello " .. "World")

所有逻辑操作符将 false 和 nil 视作假,其他任何值视作真,对于 and 和 or,“ 短路求值 ”,对于 not,永远只返回 true 或者 false。

字符串连接

在 Lua 中连接两个字符串,可以使用操作符 “..”(两个点)。如果其任意一个操作数是数字的话,Lua 会将这个数字转换成字符串。注意,连接操作符只会创建一个新字符串,而不会改变原操作数。也可以使用 string 库函数 string.format 连接字符串。

print("Hello " .. "World")    -->打印 Hello World

print(string.format("%s-%s","hello","world")) -->打印 hello-world

控制结构

选择判断

  • if then end
  • if then else end
  • if then elseif then else end
if score == 100 then
print("Very good!Your score is 100")
elseif score >= 60 then
print("Congratulations, you have passed it,your score greater or equal to 60")
--此处可以添加多个elseif
else
print("Sorry, you do not pass the exam! ")
end

循环

while do end

while x <= 5 do
sum = sum + x
x = x + 1
end

repeat until

repeat
print(x)
until false

for do

for var = begin, finish, step do
--body
end

for in do

for i, v in ipairs(a) do
print("index:", i, " value:", v)
end

-- 打印table t中所有的key
for k in pairs(t) do
print(k)
end

-- print value
for k,v in pairs(revDays) do
print("k:", k, " v:", v)
end

ipairs() 内建函数是可以被 JIT 编译的,而 pairs() 则只能被解释执行。因此在性能敏感的场景,应当合理安排数据结构,避免对哈希表进行遍历。事实上,即使未来 pairs 可以被 JIT 编译,哈希表的遍历本身也不会有数组遍历那么高效,毕竟哈希表就不是为遍历而设计的数据结构。

没有 continue 只有 break

变量

Lua 中的局部变量要用 local 关键字来显式定义

不使用 local 显式定义的变量就是全局变量

g_var = 1         -- global var
local l_var = 2 -- local var

作用域

局部变量的生命周期是有限的,它的作用域仅限于声明它的块(block )。

一个块是一个控制结构的执行体、或者是一个函数的执行体再或者是一个程序块(chunk )。

函数

Lua 使用关键字 function 定义函数

-- 全局方法
function function_name (arc) -- arc 表示参数列表,函数的参数列表可以为空
-- body
end

-- 局部方法
local function function_name (arc)
-- body
end

函数定义等价于变量赋值 , 也可以把函数名替换为某个 Lua 表的某个字段

function foo.bar(a, b, c)
-- body ...
end
-- 等价于
foo.bar = function (a, b, c)
print(a, b, c)
end

变长参数

local function func( ... )                -- 形参为 ... ,表示函数采用变长参数

local temp = {...} -- 访问的时候也要使用 ...
local ans = table.concat(temp, " ") -- 使用 table.concat 库函数对数
-- 组内容使用 " " 拼接成字符串。
print(ans)
end

具名参数 及 引用传递

当函数参数是 table 类型时,传递进来的是 实际参数的引用,此时在函数内部对该 table 所做的修改,会直接对调用者所传递的实际参数生效

function change(arg) --change函数,改变长方形的长和宽,使其各增长一倍
arg.width = arg.width * 2 --表arg不是表rectangle的拷贝,他们是同一个表
arg.height = arg.height * 2
end -- 没有return语句了

函数返回多值

local function swap(a, b)   -- 定义函数 swap,实现两个变量交换值
return b, a -- 按相反顺序返回变量的值
end
x, y = swap(x, y) -- 调用 swap 函数

调整规则: 若返回值个数大于接收变量的个数,多余的返回值会被忽略掉; 若返回值个数小于参数个数,从左向右,没有被返回值初始化的变量会被初始化为 nil。

当一个函数有一个以上返回值,且函数调用不是一个列表表达式的最后一个元素,那么函数调用只会产生一个返回值 , 也就是第一个返回值。

local function init()       -- init 函数 返回两个值 1 和 "lua"
return 1, "lua"
end

local x, y, z = init(), 2 -- init 函数的位置不在最后,此时只返回 1
print(x, y, z) -->output 1 2 nil

local a, b, c = 2, init() -- init 函数的位置在最后,此时返回 1 和 "lua"
print(a, b, c) -->output 2 1 lua

确保只取函数返回值的第一个值,可以使用括号运算符

local function init()
return 1, "lua"
end

print(2, (init())) -->output 2 1

值得一提的是,如果实参列表中某个函数会返回多个值,同时调用者又没有显式地使用括号运算符来筛选和过滤,则这样的表达式是不能被 LuaJIT 2 所 JIT 编译的,而只能被解释执行。

函数动态调用

unpack

local args = {...} or {}
method_name(unpack(args, 1, table.maxn(args)))

unpack 内建函数还不能为 LuaJIT 所 JIT 编译,因此这种用法总是会被解释执行。对性能敏感的代码路径应避免这种用法。

注释

  1. 单行注释 -- 功能等同于 C++ 中的 //
  2. 多行注释 --[[ 注释的内容 ]] 功能等同于 C++ 中的 /**/
  3. 多行注释 --[====[ 注释和内容 ]====], 也等同于 C++ 中的 /**/, 这个主要用于注释的内容里面有像 arr[arr2[idx]] 这种文本 , 如果使用 –[[ ]] 遇到 arr[arr2[idx]] 后面的 ]] 注释就结束 , 这显示与我们的意图不相符合 . 所以使用 –[===[ ]===] 来注释内容 , 就不用担心了 , 两边的等号数量要相同 , 多少个没有关系

模块

Lua 提供了一个名为 require 的函数用来加载模块。模块加载后的结果通过是一个 Lua table,这个表就像是一个命名空间,其内容就是模块中导出的所有东西,比如函数和变量。

要加载一个模块,只需要简单地调用 require ("file") 就可以了,file 指模块所在的文件名。

local fp = require("my")
fp.greeting()

对于需要导出给外部使用的公共模块,处于安全考虑,是要避免全局变量的出现。

Table

在 Lua 中,数组下标从 1 开始计数。

在初始化一个数组的时候,若不显式地用键值对方式赋值,则会默认用数字作为下标,从 1 开始。由于在 Lua 内部实际采用哈希表和数组分别保存键值对、普通值,所以不推荐混合使用这两种赋值方式。

追加到 table 的末尾用的是 s[#s+1] = something

不要在 Lua 的 table 中使用 nil 值,如果一个元素要删除,直接 remove,不要用 nil 去代替。

所有索引值都需要用 “[]” 括起来;如果是字符串,还可以去掉引号和中括号; 即如果没有 [] 括起,则认为是字符串索引

s={a=1,b=2}
print(s.a)
  • 获取长度: table.getn(table)
s={1,2,3}
print(table.getn(s))
-- 或者
print(#s)
  • table 连接成字符串:table.concat( tablename, sep, start_index, end_index )
local a = {1, 3, 5, "hello" }
print(table.concat(a)) -- output: 135hello
print(table.concat(a, "|")) -- output: 1|3|5|hello
  • 插入值:table.insert (table, [pos 位置 ,] value)
local a = {1, 8}             --a[1] = 1,a[2] = 8
table.insert(a, 1, 3) --在表索引为1处插入3
  • 最大索引编号:table.maxn (table) 返回(数组型)表 table 的最大索引编号;如果此表没有正的索引编号,返回 0。
  • 删除值:table.remove (table [, pos]) 在表 table 中删除索引为 pos(pos 只能是 number 型)的元素,并返回这个被删除的元素,它后面所有元素的索引值都会减一。pos 的默认值是表的长度,即默认是删除表的最后一个元素。
local a = { 1, 2, 3, 4}
print(table.remove(a, 1)) --删除速索引为1的元素
print(a[1], a[2], a[3], a[4])
  • 表排序:table.sort (table [, comp]) 按照给定的比较函数 comp 给表 table 排序,也就是从 table[1] 到 table[n],这里 n 表示 table 的长度。 比较函数有两个参数,如果希望第一个参数排在第二个的前面,就应该返回 true,否则返回 false。 如果比较函数 comp 没有给出,默认从小到大排序。
local function compare(x, y) --从大到小排序
return x > y --如果第一个参数大于第二个就返回true,否则返回false
end

local a = { 1, 7, 3, 4, 25}
table.sort(a, compare) --使用比较函数进行排序
0%