1.脚本不要大写大写字母或者下划线开头做变量,因为它们被用来做保留字(环境变量啥的)。

2.变量的创建,直接使用一个变量即可,不存在就创建,没有赋值就认为变量的值为nil。删除一个全局变量,将其值赋值为nil,即一个变量是否存在看其值是否为nil。nil在用来作比较时,应该加上双引号”nil”如 type(x)==”nil”, type(type(X))==string

3.对一个数字字符串进行算数操作,lua会将其转化为数字

例:

>print("2" + 6)
8.0
> print("2 + 6")
2 + 6
4.数字和字符串可以使用连接符号“..”连接起来
例 > print("a" .. 'b')
ab
> print(157 .. 428)
157428
5.在字符串或者字符串变量前面使用#表示计算字符串长度,不包括\0。
例> len = "www.runoob.com"
> print(#len)
14
> print(#"www.runoob.com")
14

6.lua里面的数组没有固定长度,下标可以是数字也可以是字符串,当下标不存就直接在数组中开辟创建,没有赋值,其值为nil。7.for循环除开用来循环递增,还用来遍历数组。

8.lua里面有线程即协同程序(coroutine),和线程区别在于线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。

9.多值赋值:多值赋值经常用来交换变量,或将函数调用返回给变量:

①遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作,所以我们可以这样进行交换变量的值:例子将x,y的值互换以及x[i]与y[i]的值互换。

例:x, y = y, x                     -- swap 'x' for 'y'
a[i], a[j] = a[j], a[i]         -- swap 'a[i]' for 'a[j]'
②下面例子将函数f()的返回值以此赋给a,b,函数返回值假设有两个。
a, b = f()
10.对于表格的索引有三种方法:表在使用之前需要创建,不像基本变量使用即创建。表其实是数组和结构体的混合体,下标为数字的空间相当于是连续的与下标时字符串时分开来存储的,数字和字符下标可以混合使用  字符下标和数字下标存储位置不是连续的,是分隔开的,虽然表格变量名一样,但是无法通过数字下标来访问字符下标的内容
t[i] --i的值可以为数字,也可以为字符串,不过i本身为字符串时要加双引号
t.i  《==》 t[“i”]          -- 当索引i本身为字符串时的一种简化写法
gettable_event(t,i) -- 采用索引访问本质上是一个类似这样的函数调用
字符串当索引下标时,在非方括号内,则可以不用加双引号
fruits={“apple”, key=”mango”, “grape”},这个是带元素的表,其中元素apple,grape的索引是默认的1和2,我们可以使用print(fruits[1], fruits[2])打印输出,mango是通过print(fruits[“key2”])或者print(fruits.key2)输出

11.函数名的赋值 


myprint = function(param)
print("这是打印函数 -   ##",param,"##")
end

myprint相当于函数名,之后正常使用就可以,可以赋值给其他变量回调和直接调用。当去=号左边的部分时,剩余部分可以做一个函数调用的参数(函数指针),即匿名函数。

12.当函数定义时,参数列表为...时,表明参数为一系列,那么传入参数任意,后面使用...就表示传入的一系列参数值

通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:

select('#', …) 或者 select(n, …)
select('#', …) 返回可变参数的长度
select(n, …) 用于访问第n个参数
  1. string.format()用来打印信息,类似于C语言的printf函数

13.在%号后添加参数. 参数将以如下的顺序读入:

(1) 符号: 一个+号表示其后的数字转义符将让正数显示正号. 默认情况下只有负数显示符号.

(2) 占位符: 一个0, 在后面指定了字串宽度时占位用. 不填时的默认占位符是空格.

(3) 对齐标识: 在指定了字串宽度时, 默认为右对齐, 增加-号可以改为左对齐.

(4) 宽度数值

(5) 小数位数/字串裁切: 在宽度数值后增加的小数部分n, 若后接f(浮点数转义符, 如%6.3f)则设定该浮点数的小数只保留n位, 若后接s(字符串转义符, 如%5.3s)则设定该字符串只显示前n位.

14. 当我们为 table a 并设置元素,然后将 a 赋值给 b,则 a 与 b 都指向同一个内存。如果 a 设置为 nil ,则 b 同样能访问 table 的元素。如果没有指定的变量指向a,Lua的垃圾回收机制会清理相对应的内存。

15.lua调用lua模块和C库:

 

Lua模块的调用:

模块的命名  xxx.lua

模块的路径的包含:在使用require函数来加载模块,需要将模块的路径添加到 LUA_PATH 中,如果变量不存在则去用户根目录的.profile文件,没有则创建,打开 .bashrc 文件也可以,例如把 "~/lua/" 路径加入 LUA_PATH 环境变量里,

export LUA_PATH="~/lua/?.lua;;" 文件路径以 ";" 号分隔,最后的 2 个 ";;" 表示新加的路径后面加上原来的默认路径
source ~/.profile     接着,更新环境变量参数,使之立即生效。

静态调用lua包模块:调用lua模块:向上面一样设置路径,然后调用 require("XXX")来声明模块,执行 require 后会返回一个由模块常量或函数组成的 table(例如:m= require("XXX"),然后将m当做表XXX来使用),并且还会定义一个包含该 table 的全局变量(5.2以后的版本不在创建,需要使用返回值的形式)(就可以把把外部模块XXX的返回值m当一个已经定义的内部表(类似于c++里面的类,分私有和公有函数,对应模块的局部和全局函数)一样来使用)

 

Lua模块调用示例:

编写模块
-- 文件名为 module.lua
-- 定义一个名为 module 的模块
module = {}
-- 定义一个常量
module.constant = "这是一个常量"
 
-- 定义一个函数
function module.func1()
    io.write("这是一个公有函数!\n")
end
 
local function func2()
print("这是一x

调用结果:

这是一个常量

这是一个私有函数!

 

Lua调用C语言库:动态调用,库与lua在同一个目录,或者设置路径环境变量

C语言库函数.so的编写方式:

#include <stdio.h>
#include <math.h>
#include <stdarg.h>
#include <stdlib.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
/* 所有注册给Lua的C函数具有
 * "typedef int (*lua_CFunction) (lua_State *L);"的原型。
 */
static int l_sin(lua_State *L)
{ 
 // 如果给定虚拟栈中索引处的元素可以转换为数字,则返回转换后的数字,否则报错。
 double d = luaL_checknumber(L, 1);/*获取lua的参数*/
 lua_pushnumber(L, sin(d)); /* push result ,将调用函数sin的结果过入栈给lua作为函数调用的返回值*/
 
 /* 这里可以看出,C可以返回给Lua多个结果,
  * 通过多次调用lua_push*(),之后return返回结果的数量。
  */
 return 1; /* number of results */
}
/* 需要一个"luaL_Reg"类型的结构体,其中每一个元素对应一个提供给Lua的函数。
 * 每一个元素中包含此函数在Lua中的名字,以及该函数在C库中的函数指针。
 * 最后一个元素为“哨兵元素”(两个"NULL"),用于告诉Lua没有其他的函数需要注册。
 */
static const struct luaL_Reg mylib[] = {
 {"mysin", l_sin},
 {NULL, NULL}
};
/* 此函数为C库中的“特殊函数”。
 * 通过调用它注册所有C库中的函数,并将它们存储在适当的位置。
 * 此函数的命名规则应遵循:
 * 1、使用"luaopen_"作为前缀。
 * 2、前缀之后的名字将作为"require"的参数。
 */
extern int luaopen_mylib(lua_State* L)
{
 /* void luaL_newlib (lua_State *L, const luaL_Reg l[]);
  * 创建一个新的"table",并将"l"中所列出的函数注册为"table"的域。
  */ 
 luaL_newlib(L, mylib);
 
 return 1;
}

使用gcc -o mylib.so -fPIC -shared mylib.c -llua -ldl编译成so

然后创建一个lua文件,把我们编译出来的c库引入进来

调用方式:1

--[[ 这里"require"的参数对应C库中"luaopen_mylib()"中的"mylib"。
  C库就放在"a.lua"的同级目录,"require"可以找到。]]
local mylib = require "mylib"
print(mylib.mysin(3.14 / 2)) --> 0.99999968293183

执行a.lua文件,后报错,说Lua存在多个虚拟机!

为什么呢?查了一些资料发现因为lua默认编译的是静态链接库,这样会导致链接多个VM冲突。

那么我们自己再编译个lua解释器动态链接一下。

mylua.c
#include <stdio.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
int main() {
 lua_State *L = luaL_newstate();
 luaL_openlibs(L);
if (luaL_loadfile(L, "a.lua") || lua_pcall(L, 0, 0, 0)) {
  printf("%s", lua_tostring(L, -1));
 }
}
gcc -o mylua mylua.c -llua -ldl -lm -Wall

这样就能编译出mylua可执行文件

在命令行./mylua执行,成功打印出0.99999968293183

 

 

Lua中调拥方式:2

local path = "/usr/local/lua/lib/libluasocket.so"
-- 或者 path = "C:\\windows\\luasocket.dll",这是 Window 平台下
local f = assert(loadlib(path, "luaopen_socket"))  -- luaopen_socket:C语言函数名
--assert作用:如果加载动态库或者查找初始化函数时出错,loadlib将返回nil和错误信息。
f()  -- 真正调用函数

 

15.给一个表设置元表,在元表里面设置元方法以后,对表的操作会更改方式,相当于规则重载。可以通过对表设置元表的元方法来单向访问元表中元方法设置的函数和表,表不能访问自己的元方法中的元素,也不能访问元表的元素:表设置元方法无作用,不能访问到 

给一个表设置元表:

mytable = {}                          -- 普通表
mymetatable = {}                      -- 元表
setmetatable(mytable,mymetatable)     -- 把 mymetatable 设为 mytable 的元表
--或者改写成一行  mytable = setmetatable({},{})
getmetatable(mytable)                 -- 这回返回mymetatable
 
设置表查询索引的元方法:__index  先在表中查询键,如果不存在,则继续元表的__index设置的元方法(隐匿函数或者表格)中查询
> other = { foo = 3 }  > t = setmetatable({}, { __index = other })
> t.foo 3
> t.bar nil

 

对表更新的元方法:__newindex 当对表中一个索引赋值时,当索引不存在时,它会对查__newindex对应的元方法(隐匿函数或者表格1(将新的索引值写入这个表中)),如果对元表设置的__index的元方法也是表格1,那么对表访问

这个新索引才能访问到,否则访问不到。如果索引存在则直接更改,但是不能通过元表的设置的元方法(表格)来访问这个索引

 

重载表的操作符:通过元方法(关键词)设置隐匿函数来执行相应算数运算符的操作

__add    对应的运算符 '+'.       __sub    对应的运算符 '-'.
__mul    对应的运算符 '*'.        __div    对应的运算符 '/'.
__mod    对应的运算符 '%'.     __unm    对应的运算符 '-'.
__concat   对应的运算符 '..'.    __eq    对应的运算符 '=='.
__lt    对应的运算符 '<'.        __le    对应的运算符 '<='.

以相加为例:

-- 两表相加操作
mytable = setmetatable({ 1, 2, 3 }, {
  __add = function(mytable, newtable)
for i = 1, 3 do
      table.insert(mytable, 3+,newtable[i])
end
return mytable
end
})
 
secondtable = {4,5,6}
 
mytable = mytable + secondtable
for k,v in ipairs(mytable) do
print(k,v)
end
 
将表当做函数使用元方法:__call
mytable = setmetatable({10}, {
__call = function(mytable, newtable)  函数体  })
newtable = {10,20,30}
print(mytable(newtable)) –此处将表当做函数,调用元方法
 
 
定义表的输出行为元方法:__tostring
mytable = setmetatable({ 10, 20, 30 }, {
__tostring = function(mytable) 函数体 })
print(mytable)     --此处调用

16.协同程序:

 

coroutine.create()

创建coroutine,参数为隐匿函数,返回coroutine的ID号,只是创建,不执行

coroutine.resume()

启动和重启coroutine,第一个参数为需要运行的协同程序的ID号, 后面参数个数没有规定,参数与创建协同隐匿函数的参数一一对应, 不能唤醒已经结束的

返回值格式为 ture/fale  返回值1 返回值2 … (返回值为挂起函数的参数)

coroutine.yield()

在协同程序中挂起coroutine,将coroutine设置为挂起状态,参数将作为 coroutine.resume()的返回值,返回值格式为  ture/false  参数1 参数2 …

coroutine.status()

查看coroutine的状态,参数为协同程序ID,返回值为下面三种状态。
注:coroutine的状态有三种:dead,suspend,running,具体什么时候有这样的状态请参考下面的程序

coroutine.wrap()

创建coroutine,返回一个函数地址,参数为隐匿函数,一旦你调用这个函数,就进入coroutine,和create功能重复

coroutine.running()

返回正在跑的coroutine地址,一个coroutine就是一个线程,当使用running的时候,就是返回一个corouting的线程号

 

17.文件I/O:分为简单模式和完全模式,简单模式在打开以后,需要指定输入还是输出以及其目标,完全模式可以在打开以后具体对某一个直接进行读写。

两种模式打开文件的方式一样:

io.open(“filename”,”mode”)mode与C语言一样,read函数的参数也一样有:“*n” 读取一个数字返回,“*a”从当前位置读取整个文件 
 “*I”读取下一行(默认) ,在文件末尾处(EOF)返回nil 例:io.read(“*I”)  数字 返回一个指定字符个数的字符串  例如:io.read(5)

简单模式:格式为 io.function_name

file = io.open("test.lua", "r")  --打开一个文件
io.input(file)            --设置需要操作的文件的输入输出格式,从文件file输入到缓冲区,相当于读文件
print(io.read())                           --读文件
io.close(file)
file = io.open("test.lua", "a")
io.output(file)
io.write("--  test.lua 文件末尾注释")
io.close(file)
提供的其他简单模式函数:io.flush(): 向文件写入缓冲中的所有数据,
 
io.lines(optional file name): 返回一个迭代函数,每次调用将获得文件中的一行内容,当到文件尾时,将返回nil,但不关闭文件 例:
for line in io.lines("main.lua") do
print(line)
end
 
完全模式:-- 以只读方式打开文件 流程与简单模式一样,read函数参数与简单模式一样
file = io.open("test.lua", "r")
print(file:read())
file:close()
file = io.open("test.lua", "a")
file:write("--test")
file:close()
• 完全模式的其他函数:file:seek(optional whence, optional offset): 设置和获取当前文件位置,成功则返回最终的文件位置(按字节),失败则返回nil加错误信息。参数 whence 值可以是:
"set": 从文件头开始 "cur": 从当前位置开始[默认]  "end": 从文件尾开始 offset:默认0
不带参数file:seek()则返回当前位置,file:seek("set")则定位到文件头,file:seek("end")则定位到文件尾并返回文件大小 offset设置定位好后的偏移量  file:seek("end",-25)
• file:flush(): 向文件写入缓冲中的所有数据
 
18.错误处理函数:assert error
assert:assert(表达式1,”输出字符串”) 当表达式的值为假,则打印错误的文件,代码行数,需要出的字符串。
 
error:error (message [, level])

功能:终止正在执行的函数,并返回message的内容作为错误信息(error函数永远都不会返回)

通常情况下,error会附加一些错误位置的信息到message头部。

Level参数指示获得错误的位置:

  • Level=1[默认]:为调用error位置(文件+行号)
  • Level=2:指出哪个调用error的函数的函数
  • Level=0:不添加错误位置信息

pcall(function_name,parameter..) 函数名,参数列表

pcall接收一个函数和要传递后者的参数,并执行,执行结果返回:有错误、无错误;返回值true或者或false, errorinfo。即就是看一个函数是否能执行完整返回,然后可以根据其返回值做一些判断和分支,函数返回前部分栈内容被销毁了

if pcall(function_name, ….) then
-- 没有错误
else
-- 一些错误
end
 
xpcall:xpcall接收第二个参数——一个错误处理函数,当错误发生时,Lua会在调用桟展看(unwind)前调用错误处理函数,之后接需要检测的函数的参数,错误处理函数无法传参
function myfunction (10)
   n = n/nil
end
 
function myerrorhandler( err )    --err为错误信息内容,不需要传参,
print( "ERROR:", err )
end
 
status = xpcall( myfunction, myerrorhandler ,10)
print( status)

19.内存管理:后台有自动运行,不过可以调用函数去更改其运行的速率和在你想启动的时候启动

垃圾收集器间歇率:

控制着收集器需要在开启新的循环前要等待多久。 增大这个值会减少收集器的积极性。 当这个值比 100 小的时候,收集器在开启新的循环前不会有等待。 设置这个值为 200 就会让收集器等到总内存使用量达到 之前的两倍时才开始新的循环。

垃圾收集器步进倍率:

控制着收集器运作速度相对于内存分配速度的倍率。 增大这个值不仅会让收集器更加积极,还会增加每个增量步骤的长度。 不要把这个值设得小于 100 , 那样的话收集器就工作的太慢了以至于永远都干不完一个循环。 默认值是 200 ,这表示收集器以内存分配的"两倍"速工作。

Lua 提供了以下函数collectgarbage ([opt [, arg]])用来控制自动内存管理:

  • collectgarbage("collect"): 做一次完整的垃圾收集循环。通过参数 opt 它提供了一组不同的功能:
  • collectgarbage("count"): 以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。
  • collectgarbage("restart"): 重启垃圾收集器的自动运行。
  • collectgarbage("setpause"): 将 arg 设为收集器的 间歇率 (参见 §2.5)。 返回 间歇率 的前一个值。
  • collectgarbage("setstepmul"): 返回 步进倍率 的前一个值。
  • collectgarbage("step"): 单步运行垃圾收集器。 步长"大小"由 arg 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 如果收集器结束一个循环将返回 true 。
  • collectgarbage("stop"): 停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。

 

20.面向对象:. 与 : 的区别在于使用 : 定义的函数隐含 self 参数,使用 : 调用函数会自动传入 table 至 self 参数

示例:创建一个类(基类),然后设置好类的属性和方法,然后在这个类中的一个方法中,将它自己通过self这个参数把它自己赋值给另外一个表(表1)的空元表的__index元方法,返回另外一个表(表1),需要使用这个基类的时候,只需要调用这个基类的方法,将返回值赋值给一个表(实际需要使用的表)就相当于创建了一个派生类:派生类可以访问基类中的属性,方法。

--[[
Lua 中使用":"实现面向对象方式的调用。":"只是语法糖,它同时在方法的声明与实现中增加了一个
名为 self 的隐藏参数,这个参数就是对象本身。
]]
 
--实例:
Account = {balance = 0};
 
--生成对象
function Account:new(o)
    o = o or {}; --如果用户没有提供对象,则创建一个。
    setmetatable(o, self); --将 Account 作为新创建的对象元表
self.__index = self; --将新对象元表的 __index 指向为 Account(这样新对象就可以通过索引来访问 Account 的值了)
    
return o; --将新对象返回
end
 
--存款
function Account:deposit(v)
self.balance = self.balance + v;
end
 
--取款
function Account:withdraw(v)
self.balance = self.balance - v;
end
 
--查询
function Account:demand()
print(self.balance);
end
 
--创建对象
myAccount = Account:new();
--通过索引访问
print(myAccount.balance);
--调用函数
myAccount:deposit(100);
myAccount:withdraw(50);
myAccount:demand();
 
打印结果
0
50
[Finished in 0.0s]
 
21. C语言调用lua: 对应的lua文件和C语言在同一个文件目录下
//你需要include这几个lua头文件
#include  <stdio.h>
#include  "lua.h"
#include  "lualib.h"
#include  "lauxlib.h"
lua_State* L;
int
luaadd(int x, int y)
{
 int sum;
 /*函数名*/
 lua_getglobal(L,"add");
 /*参数入栈*/
 lua_pushnumber(L, x);
 /*参数入栈*/
 lua_pushnumber(L, y);
 /*开始调用函数,有2个参数,1个返回值*/
 lua_call(L, 2, 1);
 /*取出返回值*/
 sum = (int)lua_tonumber(L, -1);
 /*清除返回值的栈*/
 lua_pop(L,1);
 return sum;
}
int
main(int argc, char *argv[])
{
 int sum;
 L = luaL_newstate(); /* 创建lua状态机 */
 luaL_openlibs(L); /* 打开Lua状态机中所有Lua标准库 */
 /*加载lua脚本*/
 luaL_dofile(L, "add.lua");
 /*调用C函数,这个里面会调用lua函数*/
 sum = luaadd(99, 10);
 printf("The sum is %d \n",sum);
 /*清除Lua*/
 lua_close(L);
 return 0;
}
 
对应Lua文件的内容:
function add(x,y)
  return x + y
end