Lua技术总结
来源:互联网 发布:淘宝怎么优化宝贝排名靠前 编辑:程序博客网 时间:2024/06/05 15:15
最近的工作中,用到Lua脚本来完成网管、CLI命令的下发或回显。系统框架C代码调用Lua完成命令参数解析,在Lua中调用C码完成数据库(DB)读写等,感觉有点意思,于是稍微花点时间琢磨了一番!
Lua是一个小巧的脚本语言,由巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)的一个研究小组,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo于1993年开发,设计目的是在嵌入式系统中,为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,因此,基本上在所有操作系统和平台上都可以编译、运行。
Lua脚本可以被C/C++代码调用,反过来也可以调用C/C++的函数,这使得Lua在应用程序中被广泛应用。不仅仅作为扩展脚本,亦可以作为普通的配置文件,代替XML、ini等配置文件,并且更容易理解和维护。Lua并没有提供强大的库,故Lua不适合作为独立应用程序的开发语言。但这却是由它的定位决定的。一个完整的Lua解释器不过200K,在目前所有脚本引擎中,Lua的速度最快。这一切就决定了Lua是作为嵌入式脚本的最佳选择。
一、数据交换
Lua可以与C/C++代码相互调用,那它们是如何进行数据交换的呢?
要理解Lua和C/C++交互,首先要理解Lua堆栈。Lua和C/C++程序通过一个栈交换数据: struct lua_State。栈的特点是先进后出(FILO)。栈的序号可以从栈顶和栈底计数:从栈底计数,则栈底是1,向栈顶方向递增;从栈顶计数,则栈顶是-1,向栈底方向递减。一般都用从栈底计数的方式。栈的默认大小是20,可以用lua_checkstack修改,可以用lua_gettop获得栈里的元素数目。如图:
lua的栈类似于以下的定义,它是在创建lua_State的时候创建的:
TValuestack[max_stack_len] //可查 lstate.c 的stack_init函数了解详情
存入栈的数据类型包括数值、字符串、指针、table、闭包等,下面是一个栈的例子:
依次执行下面的代码,就可以让你的lua栈呈现上图中的情况:
lua_pushcclosure(L, func, 0) //创建并压入一个闭包
lua_createtable(L, 0,0) //新建并压入一个表
lua_pushnumber(L, 123) //压入一个数字
lua_pushstring(L,“mystr”) //压入一个字符串
这里要说明的是,压入的类型有数值、字符串、表和闭包(在c中看来是不同类型的值),但是最后都是统一用TValue这种数据结构来保存,下面用图简单的说明一下这种数据结构:
TValue结构对应于lua中的所有数据类型,是一个{值,类型}结构,为了lua中动态类型的实现,它把值和类型绑在一起,用tt记录value的类型,value是一个联合体,由Value定义,可以看到这个联合体有四个域:
gc --其他诸如table、thread、closure、string,需要内存管理&垃圾回收的类型都存在这里
p --可以存一个指针,实际上是lua中的light userdata结构
n --所有的数值存在这里,可以是int,或是float
b -- Boolean值存这里,注意:lua_pushinteger不是存在这里,而存在n中,b只存布尔值
gc是一个指针,它指向的类型由联合体GCObject定义。从图中可以看出,有string、userdata、closure、table、proto、upvalue、thread。
由上可得出如下结论:
lua中,number、boolean、light userdata、nil四种类型的值是直接存在栈上元素里的,和垃圾回收无关。
lua中,string、table、closure、userdata、thread存在栈元素里的只是指针,它们均会在生命周期结束后被垃圾回收。
二、堆栈的操作
因为Lua与C/C++是通过栈来通信,Lua提供了C API对栈进行操作。
一些常用的栈操作:
lua_State *L = luaL_newstate(); //luaL_newstate返回一个指向堆栈的指针
lua_pushnumber(L, 123) //压入一个数字
lua_pushstring(L,“mystr”) //压入一个字符串
int lua_gettop (lua_State *L); //返回栈顶索引(即栈长度)
void lua_settop (lua_State *L, int idx); //
void lua_pushvalue (lua_State *L, int idx); //将idx索引上的值的副本压入栈顶
void lua_remove (lua_State *L, int idx); //移除idx索引上的值
void lua_insert (lua_State *L, int idx); //弹出栈顶元素,并插入索引idx位置
void lua_replace (lua_State *L, int idx); //弹出栈顶元素,并替换索引idx位置的值
… …
其中,lua_settop将栈顶设置为一个指定的位置,即修改栈中元素的数量。如果值比原栈顶高,则高的部分nil补足,如果值比原栈低,则原栈高出的部分舍弃,所以可以用lua_settop(0)来清空栈。
三、Lua开源软件安装
Lua开源软件安装,可参考《Lua脚本:开源软件的安装方法》
四、C调用Lua
实现方法可参考《Lua脚本:C调用Lua实现方法》
五、Lua调用C
实现方法可参考《Lua脚本:Lua调用C实现方法》
六、总结
Lua和C/C++是通过一个虚拟栈来实现数据交换的。
C/C++调用Lua实际上是:由C/C++先把数据放入栈中,由Lua去栈中取数据,然后返回数据对应的值到栈顶,再由栈顶返回C/C++。
Lua调C/C++也一样:先编写自己的C/C++模块,然后注册函数到Lua解释器中,然后由Lua去调用这个模块的函数。
最后附上:Lua语言15分钟快速入门
原文出处:http://tylerneylon.com/a/learn-lua/
--单行注释
--[[
[多行注释]
--]]
----------
- 1.变量 &控制流
----------
num = 23 --数字都是双精度,类似于C中的double
str = 'aspythonstring' --像 Python一样不可变
str = "aspythonuse" --可以双引号
str = [[
像 Python的多行注释可用于
表示多行字符串一样
方便
]]
bol = nil --未
定义;支持垃圾回收
--缩进只为易读性,像 Matlab一样以end结尾
while num < 50 do
num = num + 1 --没有++、--或 +=、-=、*=、/=等操作符号
end
-- IF条件开关
if num > 40 then
print('> 40')
elseif s ~= 'aspython' then -- ~=表示不等于,类似于C中的!=,注意elseif中间没有空格
io.write('s isnot aspython') --标准输入输出
else
thisIsGlobal = 5 --驼峰式命名
--显示声明局部变量(像Javascript一样)
local line =io.read()
-- ..作为字符串连接符
print('凛冬将至'.. line)
end
--引用未定义变量(默认视为全局变量)将返回nil,这并非错误!
foo = anUnknownVariable --等价于foo = nil
aBoolValue = false
--只有 nil与 false为逻辑假;数字0与空字串''为真!
if not aBoolValue then print('false')end
--像 Python一样运用'or'和'and'
--得到 C语言中a ? b : c的效果;需注意b = false或nil的情况
ans = aBoolValue and 'yes' or 'no'
karlSum = 0
for i = 1, 100 do --像Matlab的递增语法,如同数学中[1,100]
karlSum =karlSum + i
end
-- Step为 2递减的方式'100, 1, -2'
for j = 100, 1, -2 then print(j) end
--综上,范围可表示为"begin, end [, step]"
--另一种循环控制
num = 23
repeat
print('不甘于现状')
num = num - 1
until num == 0
----------
- 2.函数
----------
function fib(n)
if n < 2 thenreturn 1 end
return fib(n -2) + fib(n - 1)
end
-- Javascript一样的匿名函数与闭包
function adder(x)
--返回一个函数
--闭包内封存x值
return function(y) return x + y end
end
a1 = adder(9)
a2 = adder(36)
print(a1(16)) --> 25
print(a2(64)) --> 100
--遇到不匹配的列表长度时
--过长的变量将被赋予 nil
--过长的值将被忽略
x, y, z = 1, 2, 3, 4 -- 4将被忽略
function bar(a, b, c)
print(a, b, c)
return 4, 8, 15,16, 23, 42
end
x, y = bar('zaphod') -->"zaphod nil nil"
-- x = 4, y = 8,其余值被忽略
--函数与其他类型一样为一等公民
--同样有local/global之分
function f(x) return x * x end
f = function (x) return x * x end
print 'Hello World!' --只有一个`字符串`参数时可省略括号
----------
- 3.表(Table)
----------
--表是 Lua中唯一的复合类型
--像 PHP中的数组或Javascript中的Object一样
--可用作list/dict/map
--默认以字符串作为 key
t = {key1 = 'value1', key2 = false}
--像Javascript一样以.取值
print(t.key1) --> "value1"
t.key3 = {} --加入新的键值对
t.key2 = nil --销毁一组键值对
--理论上任何非 nil的变量都可以作为key
u = {['@!#'] = 'qbert', [{}] = 1729,[6.28] = 'tau'}
print(u[6.28]) -->"tau"
a = u['@!#'] -- a = 'qbert'
b = u[{}] -- b = nil,像Javascript一样{}会创建新的对象
--同字符串一样,只有一个表作为函数的参数时可以省略括号
--为了一个括号增加阅读难度,得不偿失
function h(x) print(x.key1) end
h{key1 = 'Sonmi~451'} -->"Sonmi~451"
for key, val in pairs(u)do --像Python 一样的键值迭代
print(key, val)
end
--像Javascript一样的全局作用域_G
print(_G['_G'] == _G) --> true
--省略 key之后即可变身为list
--实际上是以递增自然数为 key
v = {'value1', 'value2', 1.21,'gigawatts'}
for i = 1, #v do --像 Bash一样,#v表示列表长度
print(v[i]) --像 Matlab一样,列表索引从1开始
end
----------
- 3.1 Metatables & metamethods
----------
--元表(metatable)就是表的表,像Javascript的原型(prototype)一样
--为表重载一些元方法(metamethods)
f1 = {a = 1, b = 2}
f2 = {a = 2, b = 3}
-- s = f1 + f2为错
mm = {}
function mm.__add(x, y)
sum = {}
sum.a = x.a + y.a
sum.b = x.b + y.b
return sum
end
setmetatable(f1, mm)
setmetatable(f2, mm)
--实际调用 f1的metatable中的__add(f1, f2)
--只为 f1设置元表也可以
s = f1 + f2 -- s = {a = 3, b = 5}
-- s2 = s + s为错,s未定义元表
-- __index元方法重载表中key的提取符号`.`
defaultFavs = {animal = 'gru', food ='donuts'}
myFavs = {food = 'pizza'}
setmetatable(myFavs, {__index =defaultFavs})
food = myFavs.food
-- Lua中的值都具有元方法,只有 Table可以重载
--所有元方法如下
-- __add(a,b) for a + b
-- __sub(a,b) for a - b
-- __mul(a,b) for a * b
-- __div(a,b) for a / b
-- __mod(a,b) for a % b
-- __pow(a,b) for a ^ b
--__unm(a) for-a
-- __concat(a,b) fora .. b
--__len(a) for#a
-- __eq(a, b) fora == b
-- __lt(a,b) fora < b
-- __le(a,b) fora <= b
-- __index(a, b) <fn or atable> for a.b
-- __newindex(a, b,c) fora.b = c
-- __call(a,...) fora(...)
----------
- 3.2类风格的 Table与继承
----------
--像Javascript一样并没有内置Class
--但可以通过 Table`{}`实现
Dog ={} -- 1.
functionDog:new() --2.
newObj = {sound= 'woof'} -- 3.
self.__index =self -- 4.
returnsetmetatable(newObj, self) -- 5.
end
functionDog:makeSound() --6.
print('I say '.. self.sound)
end
mrDog =Dog:new() -- 7.
mrDog:makeSound() --> "I saywoof"
-- 1. Dog像类但实际是 Table
-- 2. Dog:new(...) := Dog.new(self, ...)
-- 3. newObj作 Dog的实例
-- 4. self是 Lua中默认的参数,在这里self = Dog
-- 继承的时候可以改变
-- self.__index与 self的元方法__index不是一回事
-- self ={__index = self, metatable = {__index = ...}}
-- 5. setmetatable(newObj, self)相当于setmetatable(newObj, {__index = self})
-- 赋予实例所有类方法
-- 6.同 2.
-- 7. mrDog = Dog.new(Dog)
--继承
LoudDog = Dog:new()
function LoudDog:makeSound()
s = self.sound.. ' '
print(s .. s ..s)
end
seymour = LoudDog:new()
seymour:makeSound() --> "woofwoof woof"
----------
- 4.模块
----------
--以下来自文件mod.lua
local M = {}
local function sayMyName()
print('Hrunkner')
end
function M.sayHello()
print('Why hellothere')
sayMyName()
end
return M
--以上
--回到主文件
local mod = require('mod') --运行mod.lua中的代码
--操作同下
local mod = (function()
--像Javascript一样
--[[
mod.lua中的代码
]]--
end)()
mod.sayHello() --> "Why hellothere"
mod.sayMyName() -->错!sayMyName()是mod.lua中的局部变量
-- require返回的值将被缓存
--即使多次调用require被调用文件也只运行一次
-- mod2.lua包含print("mod2")
local a = require("mod2")--> "mod2"
local b = require("mod2") --不输出,实际为 b = a
-- dofile是不缓存的版本的require
dofile("mod2") -->"mod2"
dofile("mod2") -->"mod2"
-- loadfile读取文件但不执行
--勘误:f =loadfile('mod2'),需加后缀名,否则找不到文件
f = loadfile('mod2.lua')
f() --> "mod2"
-- loadstring读取代码字符串
f = loadstring("print('Lua iscool!')")
f() --> "Lua is cool!"
----------
- 5.参考,略
----------
- Lua技术总结
- Lua总结
- lua总结
- lua总结
- Lua总结
- 【Lua】lua的解析技术有哪些?
- lua迭代总结
- LUA学习总结
- lua编译问题总结
- Lua词法分析总结
- Lua基础总结
- lua错误总结
- Lua基本语法总结
- Lua 字符串库函数总结
- Lua 字符串库函数总结
- lua学习总结
- Lua入门总结
- lua基本库函数总结
- 火狐浏览器 selenium 'geckodriver' executable needs to be in PATH
- 脑咋了?脑炸了
- bzoj 4869: [Shoi2017]相逢是问候 数论+线段树
- 安卓recycleView添加viewheader
- 用记事本编写第一个Java程序运行时 出现报错:错误: 类helloworld是公共的, 应在名为 helloworld.java 的文件中声明 public class helloworld {
- Lua技术总结
- 从零开始的JAVA之路.第二章
- 深入浅出MFC学习笔记(第6章 :MFC程序的生死因果)
- 工厂模式
- 练习1 Shortest Unsorted Continuous Subarray
- JAVA网络编程
- HTML学习笔记(Day4)
- 热修复框架Tinker最完整讲解(02)——加入Walle多渠道打包
- 润乾V5行式填报表自动插入空行