lua程序设计

简介:

1.基本
(1)a=10   --定义一个全局变量
   a=nil  --删除一个全局变量
(2)print("hello","world")       --中间有空格,后面有换行
   io.write("hello","world")    --中间无空格,后面无换行
   a=io.read("input a number:") --从标准输入读入一个数据
(3)Chunk 是指lua的一系列语句,lua每一个语句后面的";"是可选的。
   os.exit()退出当前程序的运行
   lua -l file1 -l file2 --将2个文件连接(拼接在一起)在一起运行,先运行file1在运行file2,file1中的变量对file2可见
   dofile("file.lua")  --在内存中运行file.lua文件,并在内存中保存所以变量。可以以此方式加载函数库。
(4)lua的所有保留字:and,break,do,else,else,ifend,false,for,function,if,in,local,nil,not,or,repeat,return,then,true,until,while.
   --[[  ...  --]]  :表示多行注释
(5)lua [options] [script [args]]     lua的命令运行方式
   -e:直接将命令传入Lua       lua -e "print(math.sin(12))"  可以通过-e选项向脚本传递全局变量值
   -l:链接加载运行一个文件.   lua -l file1   file   相当于把file1作为file的库使用
   -i:进入交互模式.
   系统默认的全局变量arg通过数组的形式存放命令行参数,arg[1]为第一个参数,命令行参数以空格为分隔,且命令行参数都是字符串形式的。
(6)lua的数据类型:lua是动态类型的语言,变量不需要指定类型,lua会自动根据变量的值判断变量的类型。
   lua中有8个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和table。
   boolean类型有2个取值true和false。在lua条件判断中,false和nil为假,其它都为真(包括0和空串),存在即为真。
   type可以测试给定变量或者值的类型。
   print(type("Hello world")) --> string
   print(type(print)) --> function
   不同数据类型的值可能可以做数值运算(+-*/)因为数学运算符有隐形的数据转换,但是切记不要做关系和逻辑运算。tostring(),tonumber()函数可以转换number和string类型。
   .. 连接运算符,不管连接的哪种数据类型,最后生成的数据都是string类型。注意“ .. ”使用时前后都要加空格。
   print(type(10 .. 20)) --> string
   function类型:函数和其他变量相同是一种数据类型,意味着函数可以存储在变量中,可以作为函数的参数和返回值。这个特性给了语言很大的灵活性。
   lua的5大标准库包括string库、table库、I/O库、OS库、算术库、debug库。
   userdata和thread类型:userdata可以将C数据存放在Lua 变量中。userdata在Lua中除了赋值和相等比较外没有预定义的操作。
(7)lua的表达式:Lua 中的表达式包括数字常量、字符串常量、变量、一元和二元运算符、函数调用以及表构造。
   关系运算符:< > <= >= == ~=  在做关系运算的时候,最好确保使用的数据类型是相同的。
   逻辑运算符:and or not
   .. :连接运算符,总是返回string类型的值。
   表的构造:表构造器是指初始化表的表达式。(3种表:下标表,键值表,成员表)
   默认下标索引构造法:days = {"Sunday", "Monday", "Tuesday", "Wednesday","Thursday", "Friday", "Saturday"}
   成员索引构造法:a = {x=0, y=0}  a = {}; a.x=0; a.y=0;a.y=nil    --可以删除表元素。
   关键字索引构造法:a = {["lk"]="red",["lm"]="green",["ln"]="blue"}  关键字必须加引号,除非关键字是数字
   混合索引构造法:a = {"red",x=3,"green","blue"}相当于a = {[1]="red",x=3,[2]="green",[3]="blue"}   print(a[3]) -->blue
   表的引用:下标、关键字和成员索引。默认下标是从1开始的,有关键字的元素不能用关键字索引,构造表中没有关键字的那么默认关键字从[1]开始。
   构造表中的分割号既可以是","也可以是";"
   [[]]支持以可换行的方式创建表,便于阅读。
(8)lua的赋值
   多值赋值:
   a, b = 10, 2*x <--> a=10; b=2*x
   a, b, c = 0,1;print(a,b,c) --> 0 1 nil
   多值赋值可以用在函数的返回值中。
(9)lua的局部变量
   用local申明一个局部变量,局部变量只能在申明的那个代码块内使用。
   代码块指:一个控制结构(控制结构的范围是do...end;then...end之间);一个函数体;一个chunk(变量被申明的那个文件或者文本串)
  local x;
   local y=1;
(10)lua的控制结构
 if语句:
 if conditions then
       then-part
   elseif conditions then
       elseif-part
   else
       else-part
   end;
 while语句:
 while condition do
   statements;
 end;
repeat语句:
 repeat
   statements;
 until conditions;
数值for循环:
 for var=forstart,forend,step do
   loop-part
 end
范型for循环:
for i,v in ipairs(t) do print(v) end  -->打印表中所有的值
for k in pairs(t) do print(k) end     -->打印表中所有的键

(11)break和return语句
break 语句用来退出当前循环(for、repeat、while)
return 用来从函数返回结果,当一个函数自然结束时,结尾会有一个默认的return。
Lua 语法要求break 和return 只能出现在block 的结尾一句(也就是说:作为chunk的最后一句,或者在end 之前,或者else 前,或者until 前),
有时候为了调试或者其他目的需要在block 的中间使用return 或者break,可以显式的使用do..end 来实现:
function foo ()
return --<< SYNTAX ERROR  'return' is the last statement in the next block
do return end -- OK
... -- statements not reached
end
2.lua函数
(1).定义

function func_name (arguments-list)
  statements-list;
end;

(2)函数的可变参数列表
function print(...)
--对下标索引表arg进行操作
end
可以使用"..."表示函数的可变参数列表,函数的可变参数放在"下标索引"表arg中。

string.find("hello hello", " hel")在字符串中查找子串,返回子串起始位置和结束位置。
string.format("first is :%d;%f", y, x))
unpack(t) 能解包t表中以小标为索引的元素。 t={["k"]=1,2,3} print(unpack(t)) -->2 3

(3)函数的高级特性
a.函数实际是一个普通的变量,变量类型为function类型
function foo (x) return 2*x end
等价于
foo = function (x) return 2*x end
b.闭包
特定:返回一个函数;外部函数为内部函数提供了参数保存(保存内部函数的状态),使得内部函数的调用具有了"记忆"功能。
可以用来实现迭代器以及泛型for
function list_iter (t)
local i = 0
local n = table.getn(t)
return function ()
i = i + 1
if i <= n then return t[i] end
end
end
使用迭代器:
t = {10, 20, 30}
iter = list_iter(t) -- creates the iterator
while true do
local element = iter() -- calls the iterator
if element == nil then break end
print(element)  
end

迭代器用于范性for:
t = {10, 20, 30}
for element in list_iter(t) do
print(element)
end

c.非全局函数(表的成员函数)
Lib = {}
Lib.foo = function (x,y) return x + y end

Lib = {}
function Lib.foo (x,y)
return x + y
end
d.正确的尾部调用(尾部递归)
function f(x)
return g(x)
end
f调用g后不会再做任何事情,这种情况下当被调用函数g 结束时程序不需要返回到调用者f;所以尾调用之后程序不需要在栈中保留关于调用者的任何信息。一些编译器比如Lua 解释器利用这种特性在处理尾调用时不使用额外的栈,我们称这种语言支持正确的尾调用。节省了栈开销。

3.编译·运行·错误信息
a.加载文件和字符串表达式(编译)
dofile("文件路径")   加载并全局运行该文件
f=loadfile("文件路径") 加载但并不运行文件,返回一个文件内容的全局调用地址,可以像函数一样调用。
f=loadstring("表达式") 加载表达式,但不调用。

b.例子
b.1 
dofile的真正实现为:由loadfile实现
function dofile (filename)
  local f = assert(loadfile(filename))
  return f()
end
b.2
f = loadstring("i = i + 1")
i = 0
f(); print(i) --> 1
f(); print(i) --> 2  将字符串当作表达式来执行。
b.3
Lua 把每一个chunk都作为一个匿名函数处理。例如:chunk "a = 1",loadstring 返回与其等价的function() a = 1 end
与其他函数一样,chunks 可以定义局部变量也可以返回值:
f = loadstring("local a = 10; return a + 20")
print(f()) --> 30
c.require加载
Lua 提供高级的require 函数来加载运行库。粗略的说require 和dofile 完成同样的功能但有两点不同:
1) require 会搜索目录加载文件
2) require 会判断是否文件已经加载避免重复加载同一文件。由于上述特征,require在Lua 中是加载库的更好的函数。
require指定lua的加载路径。
4.lua中使用table来实现其他数据结构
table是Lua中唯一的数据结构,其他语言所提供的数据结构,如:arrays、records、lists、queues、sets 等,Lua 都是通过table 来实现,并且在lua 中table 很好的实现了这些数据结构。
5.lua dofile调用时产生的事件,及其回调
文件 data.lua
Entry{"Donald E. Knuth",       --记住Entry{...}与Entry({...})等价,他是一个以表作为唯一参数的函数调用。
      "Literate Programming",  --指示Entry()函数的入参可能的取值
      "CSLI",                  --这其实是一个无名表
      1992}
Entry{"Jon Bentley",
      "More Programming Pearls",
      "Addison-Wesley",
      1990}
主文件
local authors = {} -- a set to collect authors
function Entry (b) authors[b[1]] = true end
dofile("data")   --当每加载一个Entry数据项时,就调用一次该文件中的Entry()函数。
for name in pairs(authors) do print(name) end
在这些程序段中使用事件驱动的方法:Entry 函数作为回调函数,dofile 处理数据文件中的每一记录都回调用它。

6.lua的6大标准库
math:
sin,cos,tan,asin,acos,exp,log,log10,floor,ceil,max,min,random
table:getn(有n成员时,返回n的值;没有n成员时,返回元素个数),setn(设置n成员的值)
      insert(t, a)向表t中尾部插入一个元素a;table.remove(t)尾部删除一个元素;
      table.insert(t,1,a)和table.remove(t,1) 首部插入和首部删除
string:string.len(s);string.rep(s,n)使用s生成一个重复n次的字符串;string.lower(s);string.upper(s)
       string.sub(s,i,j)切片(i,j=-1指向最后一个字符,-2 指向倒数第二个)
       string.char(97) string.byte("a") ASCII数字与字符相互转换
       string.format("pi = %.4f", PI))格式化字符串
       string.find(s,"ab",0)  第一个子字符串查找 。第二个参数可以是一个模式,比如"%d"查找一个数字,%a一个字母
       string.gsub(s,"a","b") 全局字符串替换       第二个参数可以是一个模式,比如"%d"查找一个数字,%a一个字母
       string.gfind           全局字符串查找       第二个参数可以是一个模式,比如"%d"查找一个数字,%a一个字母
IO:   lua使用当前文件的概念,io.read,io.write,print都是从当前文件中读写,默认当前文件为stdin和stdout
       io.input("filename")   改变当前输入文件 
       io.output("filename")  改变当前输出文件 
       io.read("*all") 读取整个文件;"*line":读取下一行;  "*number":从串中转换出一个数值;   num:读取num个字符
       io.lines()读下一行(是一个迭代器)
       f=io.open("filename", "w")  -> f:read("*all")
OS:    os.time{year=1970, month=1, day=1, hour=0}) 返回时间戳
       os.date()
       os.getenv("HOME")
       os.execute("mkdir  filename")
Debug:
       debug.getinfo(funcname)  返回函数的所有属性

7.lua中的栈
C API包括读写Lua全局变量的函数,调用Lua函数的函数,运行Lua 代码片断的函数,注册C函数然后可以在Lua中被调用的函数等。在C 和Lua 之间通信关键内容在于一个虚拟的栈。栈的使用解决了C 和Lua 之间两个不协调的问题:第一,Lua 会自动进行垃圾收集,而C 要求显示的分配存储单元,两者引起的矛盾。第二,Lua 中的动态类型和C 中的静态类型不一致引起的混乱。

lua的栈

            -------------------------

栈顶 indx=-1-> | Dn | ... | D2 | D1->   栈底  indx=1  

            -------------------------

lua的栈类似于以下的定义, 它是在创建lua_State的时候创建的:  

TValue stack[max_stack_len] #max_stack_len默认=20

存入栈的数据类型包括数值, 字符串, 指针, talbe, 闭包等, 下面是一个栈的例子:

                        wKiom1Yq9UyxZ6T5AACd4pk1iII420.jpg

执行下面的代码就可以让你的lua栈上呈现图中的情况

lua_pushcclosure(L, func, 0) // 创建并压入一个闭包   

lua_createtable(L, 0, 0)        // 新建并压入一个表   

lua_pushnumber(L, 234)      // 压入一个数字   

lua_pushstring(L, “mystr”)   // 压入一个字符串  

压入的类型有数值, 字符串, 表和闭包在c中看来是不同类型的值, 但是最后都是统一用TValue这种数据结构来保存的. 

TValue结构对应于lua中的所有数据类型, 是一个{值, 类型} 结构,保存在栈的的数据根据8大数据类型不同保存的方式不一样,分2种:值保存和指针保存。

  a. lua中, number, boolean, nil, light userdata四种类型的值是直接存在栈上元素里的, 和垃圾回收无关.

  b. lua中, string, table, closure, userdata, thread存在栈上元素里的只是指针, 他们都会在生命周期结束后被垃圾回收.


8.lua与C语言交互的函数集-C API

主要的CAPI库函数
<lua.h>、<lauxlib.h>、<lualib.h>
lua_State *L = lua_open()  --打开lua运行环境,返回lua_State *运行环境指针。并没有加载任何库
luaopen_base(L);           --打开基本库
luaopen_table(L);          --打开表库
luaopen_io(L);             --打开io库
luaopen_string(L);         --打开字符串库
luaopen_math(L);           --打开数学库
error = luaL_loadbuffer(L, "a=a+1", strlen("a=a+1"),"line")   

编译字符串,如果成功,则将其放入栈中;否则返回错误信息,并压入栈中。
error = luaL_loadfile(L, "filename")

编译文件,如果成功,则将其放入栈中;否则返回错误信息,并压入栈中。
lua_close(L); --关闭lua环境

void lua_call(lua_State* L, int nargs, int nresults)非保护下调用一个lua函数。调用它之前, 需要布局一下栈, 第一, 要把要call的函数压入栈; 第二, call要用的参数正序压入栈中; 然后才能调用lua_call, 调用完了, 自己去取返回值, 它都给你压栈上了.

error = lua_pcall(L, int nargs,int nresults, int ferror);  出栈指定位置的数据,并在lua环境中运行,运行后保存所以全局变量。如果出错,则将错误信息压栈。正确调用将返回0


C语言中调用的压栈函数 
a.将C环境的数据类型压入栈的函数
void lua_pushnil (lua_State *L);
void lua_pushboolean (lua_State *L, int bool);
void lua_pushnumber (lua_State *L, double n);
void lua_pushlstring (lua_State *L, const char *s,size_t length);
void lua_pushstring (lua_State *L, const char *s);
int lua_checkstack (lua_State *L, int sz); 检测栈上是否有足够你需要的空间

void lua_getfield (lua_State *L, int index, const char *k)  

取由index指向的栈上的一个表的键为k的元素的值,并压入栈顶

void lua_setfield (lua_State *L, int index, const char *k) 

给由index指向的栈上的一个表的键为k的元素赋值,被赋的值是栈顶的值。

void lua_gettable (lua_State *L, int index)

根据index指定取到相应的表; 取栈顶元素为key, 并弹出栈; 获取表中key的值压入栈顶.

void lua_settable (lua_State *L, int index)

根据index指定取到相应的表; 取栈顶元素做value, 弹出之; 再取当前栈顶元素做key, 亦弹出之; 然后将表的键为key的元素赋值为value

b.将lua环境中的变量压入栈中
lua_getglobal(L, "变量名"); --变量名可以是:普通变量,表,函数等任何全局变量
lua_gettable(L, inx);       --获取栈中inx元素(表),以inx-1为键对应表中的值,并将值放在inx-1位置。(所以用户要获得表中某成员的值,应该先将该成员名压入到栈中)
lua_setglobal(L,"变量名");  --将栈中的某变量赋值给lua环境中的"变量名"变量。
c.判断栈中指定位置的元素数据类型的C函数
int lua_type(lua_State *L, int index); )返回lua 8大数据类型对应的类型码。在lua.h 头文件中,每种类型都被定义为一个常量:LUA_TNIL、LUA_TBOOLEAN 、LUA_TNUMBER 、LUA_TSTRING 、LUA_TTABLE 、LUA_TFUNCTION、LUA_TUSERDATA 以及LUA_TTHREAD。
int lua_is... (lua_State *L, int index);
lua_isnumber 和lua_isstring

d.转换栈中指定位置的元素数据类型的C函数
int lua_toboolean (lua_State *L, int index);
double lua_tonumber (lua_State *L, int index);
const char * lua_tostring (lua_State *L, int index);
size_t lua_strlen (lua_State *L, int index);

其他栈操作函数
int lua_gettop (lua_State *L);返回堆栈中的元素个数,它也是栈顶元素的索引。
void lua_settop (lua_State *L, int index);设置栈顶(也就是堆栈中的元素个数)为一个指定的值。如果开始的栈顶高于新的栈顶,顶部的值被丢弃。否则,为了得到指定的大小这个函数压入相应个数的空值(nil)到栈上。lua_settop(L,0)清空堆栈
void lua_pushvalue (lua_State *L, int index);压入堆栈上指定索引的一个拷贝到栈顶
void lua_remove (lua_State *L, int index);移除指定索引位置的元素,并将其上面所有的元素下移来填补这个位置的空白
void lua_insert (lua_State *L, int index);移动栈顶元素到指定索引的位置,并将这个索引位置上面的元素全部上移至栈顶被移动留下的空隔
void lua_replace (lua_State *L, int index);从栈顶弹出元素值并将其设置到指定索引位置,没有任何移动操作(使用时,先要将替代的数据压入栈顶)
C API 的错误处理
Lua 利用C 的setjmp 技巧构造了一个类似异常处理的机制。
C环境引用lua的普通全局变量
--lua配置文件 data.lua
if getenv("DISPLAY") == ":0.0" then
    width = 300; height = 300
else
    width = 200; height = 200
end
#c文件加载lua运行后,lua环境中的变量
main.c
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
void load (char *filename, int *width, int *height) {
    lua_State *L = lua_open();
    luaopen_base(L);
    luaopen_io(L);
    luaopen_string(L);
    luaopen_math(L);
    if (luaL_loadfile(L, filename) || lua_pcall(L, 0, 0, 0))
       error(L, "cannot run configuration file: %s",lua_tostring(L, -1));
    lua_getglobal(L, "width");
    lua_getglobal(L, "height");
    if (!lua_isnumber(L, -2))
       error(L, "`width' should be a number\n");
    if (!lua_isnumber(L, -1))
       error(L, "`height' should be a number\n");
    *width = (int)lua_tonumber(L, -2);
    *height = (int)lua_tonumber(L, -1);
    lua_close(L);
}
C环境引用lua的表
--lua中的配置文件data.lua
background = {r=0.30, g=0.10, b=0}
#c文件加载lua运行后,lua环境中的变量
main.c
#define MAX_COLOR 255
main(){
...
lua_getglobal(L, "background");
if (!lua_istable(L, -1))
    error(L, "`background' is not a valid color table");
red = getfield("r");
green = getfield("g");
blue = getfield("b");
...
}
int getfield (const char *key) {
int result;
lua_pushstring(L, key);
lua_gettable(L, -2);
if (!lua_isnumber(L, -1))
   error(L, "invalid component in background color");
result = (int)lua_tonumber(L, -1) * MAX_COLOR;
lua_pop(L, 1); /* remove number */
return result;
}#可以将lua中的表里面的值传递到c中来,也可以在C语言中为lua创建一个新表。
C语言向lua中生产一个表
void setfield (const char *index, int value) {
lua_pushstring(L, index);
lua_pushnumber(L, (double)value/MAX_COLOR);
lua_settable(L, -3);
}
void setcolor (struct ColorTable *ct) {
lua_newtable(L); /* creates a table */
setfield("r", ct->red); /* table.r = ct->r */
setfield("g", ct->green); /* table.g = ct->g */
setfield("b", ct->blue); /* table.b = ct->b */
lua_setglobal(ct->name); /* 'name' = table */
}
c调用setcolor()后在lua中生成后的表
WHITE = {r=1, g=1, b=1}
RED = {r=1, g=0, b=0}

C环境引用lua的函数
--lua文件中的函数data.lua
function f (x, y)
  return (x^2 * math.sin(y))/(1 - x)
end
#c文件加载lua运行后,调用lua中的函数
double f (double x, double y) {
  double z;
   /* push functions and arguments */
  lua_getglobal(L, "f"); /* function to be called */
  lua_pushnumber(L, x); /* push 1st argument */
  lua_pushnumber(L, y); /* push 2nd argument */
  /* do the call (2 arguments, 1 result) */
  if(lua_pcall(L, 2, 1, 0) != 0)
     error(L, "error running function `f': %s",
  lua_tostring(L, -1));
  /* retrieve result */
  if (!lua_isnumber(L, -1))
     error(L, "function `f' must return a number");
  z = lua_tonumber(L, -1);
  lua_pop(L, 1); /* pop returned value */
  return z;
}#可以编写一个通用的函数调用接口



本文转自 a_liujin 51CTO博客,原文链接:http://blog.51cto.com/a1liujin/1705525,如需转载请自行联系原作者

相关文章
|
数据采集 Linux C++
【Lua】《Lua 程序设计》摘录
【Lua】《Lua 程序设计》摘录
88 3
Lua程序设计(三)面向对象实现一个简单的类
1.Lua面向对象实现步骤 ①创建一个全局表(称之为元表) ②设置这个元表的__index值(值通常为元表自己,这样就能通过__index查找到对应的属性和方法)__index 赋值其实是一个function的语法糖,Sharp.
1235 0
Lua程序设计(四)面向对象类继承
1.类继承  ①代码 Sharp = { _val = 1} --① 父类 function Sharp:new() local new_sharp = { } self.__index = self --②,self == Sharp setmetatable(n...
826 0
|
索引
Lua程序设计(二)面向对象概念介绍
----------------------------------------------------------- Lua面向对象3 local smartMan = { name = "Tinywan", age = 26, money = 800000, ...
890 0
Lua程序设计(一)面向对象概念介绍
  完整代码 local mt = {} mt.__add = function(t1,t2) print("两个Table 相加的时候会调用我") end local t1 = {} local t2 = {} -- 给两个table 设置新的元表,一个元表...
967 0