Lua 是一种轻量级的、可扩展的脚本语言,它可以用于创建动态网页、游戏开发、应用程序开发等。学习 Lua 并不难,但要想从入门到精通,需要一定的时间和耐心。
首先,学习 Lua 需要具备一定的编程基础,包括对数据类型、流程控制、函数、对象和其他常用概念的理解。如果你还不了解这些概念,可以先学习一门其他语言,例如 C 语言或者 Python 语言。
然后,你需要下载并安装 Lua 的开发工具包。Lua 有很多不同的版本,你可以根据你的需要选择合适的版本。安装好之后,就可以开始学习 Lua 了。
Lua 的语法很直观易懂,因此在学习过程中不会遇到太大困难。但是在使用 Lua 时还是会遇到一些问题,例如函数使用方法、数据处理方法、内存占用情况等。为了避免出错,你应该尝试多看看文档或者在 Stack Overflow 上寻找帮助。
-- 定义一个函数 function hello() print("Hello World!") end -- 调用函数 hello()
此外,Lua 还有很多高级特性,例如闭包、协同程序、表格库和 Meta-Table 等。这些特性能帮助你写出优雅而高效的代码。当然要想真正理解这些特性并把它们应用到实际开发中去就需要大量的实践和思考了。
1.1 实例代码
function createCountdownTimer(second)
local ms = second * 1000 --ms为countDown的Upvalue
local function countDown()
ms = ms -1
return ms
end
return countDown
end
local timer1 = createCountdownTimer(1)
for i = 1, 3 do
print(timer1())
end
输出结果:
999
998
997
1.2 关于函数闭包描述
Upvalue
一个函数所使用的定义在它的函数体之外的局部变量(external local variable)称为这个函数的upvalue。 在前面的代码中,函数countDown使用的定义在函数createCountdownTimer 中的局部变量ms就是countDown的upvalue,但ms对createCountdownTimer而 言只是一个局部变量,不是upvalue。 Upvalue是Lua不同于C/C++的特有属性,需要结合代码仔细体会。
一个函数和它所使用的所有upvalue构成了一个函数闭包。
Lua函数闭包使函数具有保持它自己的状态的能力,从这个意义上说,可以 与带静态局部变量的C函数相类比。但二者有显著的不同:对Lua来说,函数 是一种基本数据类型——代表一种(可执行)对象,可以有自己的状态;但 是对带静态局部变量的C函数来说,它并不是C的一种数据类型,更不会产生 什么对象实例,它只是一个静态地址的符号名称。
2.2 实例代码
local function create(name ,id )
local data = {name = name ,id = id} --data为obj.SetName,obj.GetName,obj.SetId,obj.GetId的Upvalue
local obj = {} --把需要隐藏的成员放在一张表里,把该表作为成员函数的upvalue。
function obj.SetName(name)
data.name = name
end
function obj.GetName()
return data.name
end
function obj.SetId(id)
data.id = id
end
function obj.GetId()
return data.id
end
return obj
end
输出结果:
mycreate"s id:1mycreate"s name:Sam
mycreate"s id:1mycreate"s name:Lucy
2.2 有关对象实现的描述
实现方式: 把需要隐藏的成员放在一张表里,把该表作为成员函数的upvalue。
局限性: 基于对象的实现不涉及继承及多态。但另一方面,脚本编程是否需要继承和多态要视情况而定。
3.1 实例代码(1):
local t = {}
local m = {a = "and",b = "Li Lei", c = "Han Meimei"}
setmetatable(t,{__index = m}) --表{ __index=m }作为表t的元表
for k,v in pairs(t) do --穷举表t
print("有值吗?")
print(k,"=>",v)
end
print("-------------")
print(t.b, t.a, t.c)
输出结果:
Li LeiandHan Meimei
3.2 实例代码(2):
local function add(t1,t2)
--‘#’运算符取表长度
assert(#t1 == #t2)
local length = #t1
for i = 1,length do
t1[i] = t1[i] + t2[i]
end
return t1
end
--setmetatable返回被设置的表
t1 = setmetatable({1,2,3},{__add = add})
t2 = setmetatable({10,20,30},{__add = add})
for k,v in pairs(t1) do
print(k,"=>",v)
end
for k,v in pairs(t2) do
print(k,"=>",v)
end
print("---------两元表相加--------------")
t1 = t1 + t2
for i = 1 ,#t1 do
print(t1[i])
end
输出结果:
1=>1
2=>2
3=>3
1=>10
2=>20
3=>30
---------两元表相加--------------
11
22
33
3.3 有关元表的描述:
定义 :
元表本身只是一个普通的表,通过特定的方法(比如setmetatable)设置到某个对象上,进而影响这个对象的行为;一个对象有哪些行为受到元表影响以及这些行为按照何种方式受到影响是受Lua语言约束的。比如在前面的代码里,两个表对象的加法运算,如果没有元表的干预,就是一种错误;但是Lua规定了元表可以“重载”对象的加法运算符,因此若把定义了加法运算的元表设置到那两个表上,它们就可以做加法了。元表是Lua最关键的概念之一,内容也很丰富,请参考Lua文档了解详情。
元表与C++虚表的比较:
如果把表比作对象,元表就是可以改变对象行为的“元”对象。在某种程度上,元表可以与C++的虚表做一类比。但二者还是迥然不同的:元表可以动态的改变,C++虚表是静态不变的;元表可以影响表(以及其他类型的对象)的很多方面的行为,虚表主要是为了定位对象的虚方法(最多再带上一点点RTTI)。
4.1 实例代码
local Robot = { name = "Sam", id = 001 }
function Robot:New(extension)
local t = setmetatable(extension or { }, self)
self.__index = self
return t
end
function Robot:SetName(name)
self.name = name
end
function Robot:GetName()
return self.name
end
function Robot:SetId(id)
self.id = id end
function Robot:GetId()
return self.id
end
robot = Robot:New()
print("robot"s name:", robot:GetName())
print("robot"s id:", robot:GetId())
print("-----------------")
local FootballRobot = Robot:New({position = "right back"})
function FootballRobot:SetPosition(p)
self.position = p
end
function FootballRobot:GetPosition()
return self.position
end
fr = FootballRobot:New()
print("fr"s position:", fr:GetPosition())
print("fr"s name:", fr:GetName())
print("fr"s id:", fr:GetId())
print("-----------------")
fr:SetName("Bob")
print("fr"s name:", fr:GetName())
print("robot"s name:", robot:GetName())
输出结果:
robot"s name:Sam
robot"s id:1
fr"s position:right back
fr"s name:Sam
fr"s id:1
fr"s name:Bob
robot"s name:Sam
4.2 相关描述:
prototype模式一个对象既是一个普通的对象,同时也可以作为创建其他对象的原型的对象(即类对象,class object);动态的改变原型对象的属性就可以动态的影响所有基于此原型的对象;另外,基于一个原型被创建出来的对象可以重载任何属于这个原型对象的方法、属性而不影响原型对象;同时,基于原型被创建出来的对象还可以作为原型来创建其他对象。
5.1 实例代码:
hello.lua
local pack = require "mypack" --导入包[注:包的名字与定义包的文件的名字相同(除去文件名后缀,在前面的代码中,就是“mypack”)]
print(ver or "No ver defined!")
print(pack.ver)
pack.aFunInMyPack()
print(aFunInMyPack or "No aFunInMyPack defined!")
aFuncFromMyPack()
mypack.lua
module(..., package.seeall) --定义包
ver = "0.1 alpha"
function aFunInMyPack()
print("Hello!")
end
_G.aFuncFromMyPack = aFunInMyPack
输出结果:
No ver defined!
0.1 alpha
Hello!
No aFunInMyPack defined!
Hello!
5.2有关包的描述:
包是一种组织代码的方式。
一般在一个Lua文件内以module函数开始定义一个包。module同时定义了一个新的包的函数环境,以使在此包中定义的全局变量都在这个环境中,而非使用包的函数的环境中。理解这一点非常关键。以前面的代码为例, “module(..., package.seeall)”的意思是定义一个包,包的名字与定义包的文件的名字相同(除去文件名后缀,在前面的代码中,就是“mypack”),并且在包的函数环境里可以访问使用包的函数环境(比如,包的实现使用了print,这个变量没有在包里定义,而是定义在使用包的外部环境中)。
一般用require函数来导入一个包,要导入的包必须被置于包路径(packagepath)上。包路径可以通过package.path或者环境变量来设定。一般来说,当前工作路径总是在包路径中。
现在官网已经不提倡使用module了,官方给出了两个理由(以
model(... , package ,seeall)
为例):package.seeall 这种方式破坏了模块的高内聚,原本引入 "filename" 模块只想调用它的 foobar() 函数,但是它却可以读写全局属性,例如 "filename.os"。
module 函数压栈操作引发的副作用,污染了全局环境变量。例如 module("filename") 会创建一个 filename 的 table,并将这个 table 注入全局环境变量中,这样使得没有引用它的文件也能调用 filename 模块的方法。
请参考Lua手册进一步了解包的详细说明。
参考文献《C/C++程序员的Lua快速入门》
枚举类最基本的用法是实现一个类型安全的枚举。枚举常量用逗号分隔,每个枚举常量都是一个对象。enum class Color{RED,BLACK,BLUE...
Go 语言支持以下系统:LinuxFreeBSDMac OS X(也称为 Darwin)Window安装包下载地址为:https://golang.google.cn/dl/。各个系统...
介绍创建一个遮罩层,用于强调特定的页面元素,并阻止用户进行其他操作。实例演示引入通过以下方式来全局注册组件,更多注册方式...
常用于主动操作后的反馈提示。与 Notification 的区别是后者更多用于系统级通知的被动提醒。基础用法从顶部出现,3 秒后自动消失...
Collapse 折叠面板通过折叠面板收纳内容区域基础用法可同时展开多个面板,面板之间不影响templateel-collapse v-model="activeNa...
创建第一个 Bootstrap 4 页面当你成功安装好 Bootstrap4 后,就可以开始创建 Bootstrap4 的第一个页面了。添加HTML5doctypeBoots...
什么是Axios?Axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中。特性从浏览器中创建XMLHttpRequests从 node....