Lua语言是一种嵌入式语言被广泛的用于各个领域,而且与C语言的兼容性比较好.Openresty当中就可以使用Lua进行快速开发.Redis可以借助Lua实现事务.以及各大游戏引擎有很多都支持Lua脚本.以此可见Lua使用范围是很广泛的.
本篇将Lua语言和其它几种语言(PHP,JavaScript,Go,Shell,C)做对比,进行对比的记忆.
Lua的语法风格和shell语言有些相似之处,但不完全相同.
学习多种语言要比较的去记忆,防止弄混,否则可能最后学成一门四不像语言.
变量的声明很简单,命名规则和C语言无差别.
字母或下划线开头,由字母下划线数字组成.
默认声明的变量为全局变量
如果需要局部变量,必须显示的声明local
.
变量的作用域就是为了防止出现变量的污染而产生不可调和的错误.
JavaScript就有臭名昭著的变量作用域提升.(JavaScript的作用域分为全局作用域与函数作用域)导致不可调和的错误.
PHP中变量的作用域更是不清晰,列入for循环里定义的变量跳出了for循环还可以被访问.这个可能会造成一些错误.
a = 10
local b =10
局部变量的作用域:
声明变量的起始位置到所在语句块结束的位置.
语句块结束位置一般为if ,do,function等结束位置.
Lua中的流程控制和shell语言写法相似,但是条件是用圆括号包起来,还有语句结束是用end,这点和shell的fi,done,esac等语句结束符不一样.
if语句条件结束之后加then标志符合条件内容执行范围,其实本质是和{
一个意思.
elseif
是关键字,不能使用else if
否则会导致错误,在一些语言当中如PHP是两种语法都支持(但书写规范是用elseif).
if (a ~= 40 ) then
print(a)
elseif (a > 10) then
print(a)
end
while (a < 10) then
a--
end
Lua语言当中的repeat和其他语言当中的do while是一样的.只不过是名字起得奇怪点.
实际编程项目当中do while 一般用的少.
repeat
print(a)
until (a<10)
for var=expr,dest,step do
end
lua中的for表达式比较特别,var=expr进行赋值,dest表是var要变更到的目标值,step表是步长.
如果step变量缺失默认步长为1.
for i,v in ipairs(a) do
end
Lua当中的注释有些别扭.
单行注释以 --
开头.
多行注释很奇怪:
--[[
注释
注释
--]]
string,number,function,boolean,nil,userdata,table,thread.
函数的声明:
function test(...)
return 1, 2, 3
end
a, b, c = test({1,2,3}) -- a b c分别为1, 2, 3
函数特点:
...
,可变参数的本质是table.local
作用范围为定义的文件内,默认为全局函数.Lua中支持匿名函数,可以将匿名函数赋值给变量.
Lua当中的字符串既可以用单引号也可以用双引号声明字符串.
a = '123\'"' --123'"
b = "123'\"" --123'"
唯一的区别是单引号字符串需要转义单引号,双引号字符串需要转义单引号
Lua不能支持字符串和数字比较,因为这可能会引起歧义.字符串的比较的是ASCII顺序值,数字比较大小.
在其他解释形语言当中支持这种操作,一般是把字符串转换位数字,例如提供了==
和===
这两种操作符.
特别的如果将数字和一个不能转换为数字的字符串进行比较,会产生严重的歧义.PHP会把不能转换为数字的字符串的值作为false也就是0值再和数字进行比较.JS中这种情况永远不会反会true.
字符串声明支持多行字符串.
a = [[
123
456
789
]]
这个和PHP以及Shell当中使用的heredoc有些类似.
如果字符串内出现了[[
,需要在字符串起始的两个方括号之间加入等号,这样就可以包含[[
.
a = [==[
123
456
789
[[
]==]
输出
123
456
789
[[
字符串的拼接使用的是..
操作符.
Lua中的数组和PHP中的数组概念上很相似,而且都可以支持传统意义上的数组(也叫列表)也支持关联数组.
但是有一点数组的下标从1开始. 这点和其它语言不一致.
表永远是匿名的,目的是为了垃圾回收机制.
使用 #a
可以获取列表a的长度,无法获取真正的数组a的长度.
表构造器是指用于创建一个表.
这点和PHP当中不一样,PHP当中使用数组时直接创建使用即可,不需要使用构造器进行构造.
也就是说下述代码会在Lua当中产生错误.
a[1] = 100 --错误
--使用之前必须要调用构造器
a = {} -- 空构造器
a = {1, 2, 3} --非空构造
a= {x = 10, y = 20}
a = {["x"] = 10, ["y"] = 20}
a = {1, "abc"}
b = a
a[1] --1
b[1]=10
-- a[1] = 10
引用机制和PHP中的数组不一样,PHP中的数组是用了copy on write机制.当对引用的数组修改时不对原数组产生影响而是新copy一份.
对于关联数组而言可用pairs
进行遍历
t = {10, x=12, k = "123"}
for k,v pairs(t) do
print(k,v)
end
对于列表而言可用ipairs
进行遍历
t = {10, 12, "123"}
for i,v ipairs(t) do
print(i,v)
end
如何正确获取数组长度,#
只能获取列表的长度.
前例当中的t
,#t
实际输出是1.但与实际长度不符合.
#
等价于table.getn
因此二者返回结果一致.
t = {10, x=12, k = "123"}
--#t -> 1
原表的概念和PHP当中类的魔术方法大同小异.JavaScript也有类似的概念.
通过原表可以实现继承.
通过元表当中的一些函数,改变表的属性等.
使用方式:
t = {}
metat = {}
setmetatable(t, metat)
这与PHP当中的__get
方法,当访问一个table不存在的键值时会返回原表当中指定的数据或者函数调用.
metat = {__index =
function (table,key)
return key
end
}
metat = {
__index = { t = "123"}
}
设置表中不存在的元素,会调用这个函数.
metat = {__newindex =
function (table,key,val)
rawset(table, key, val .. "suffix") --rawset 函数直接对表赋值,跳过元表的__newindex操作,防止产生死循环
end
}
除了上述方法之外还有__call
,__tostring
方法.
定义一个文件可以其中包含可以导出的变量一以及函数.
--module.lua
module = {}
function module.e()
print(1)
end
--test.lua
reuqire("module")
module.e()
包的导入依赖环境变量LUA_PATH
.
export LUA_PATH="~/?.lua;;"# ;;是添加默认路径
可以打印package.path变量查看当前的默认导入路径.
Lua中的面向对象是基于table实现.
其实这样也有些误导,因为Lua本来就没有面向对象的概念.就如Go当中一样,Go语言当中结构体的方法有类似的概念.
:
操作符的函数,主要表示这个函数内可以访问self关键字.
function Account:new (o)
o = o or {} -- create object if user does not provide one
setmetatable(o, self)
self.__index = self
return o
end
A = {a = 10}
function A:new(o)
o = o or {}
self.__index = self --找不到的方法在self中找
setmetatable(o, self) -- self 具有new方法
return o
end
B = A:new({b = 20}) -- B继承了A的属性a,A的方法new,并有了新的属性b
C = B:new({c = 20}) --调用new方法时self指的是'B'这个类.
instanceC = C:new()
--[[
C类的结构看起来是这样的:
{
c = 20,
metatable = {
__index = {
b = 20
metatable = {
__index = {
a = 10
}
}
}
}
}
instanceC
{
metatable = {
__index = {
c = 20,
metatable = {
__index = {
b = 20
metatable = {
__index = {
a = 10
}
}
}
}
}
}
}
--]]
关键点在与容易混淆使用new方法,这种继承的方式实现的语义不够明确.
语法比较简单,没有super等关键字.难以实现方法的重写,protect,private,public语义,抽象类,接口等面向对象的高级特性.
总体而言,lua的语言比较适用于面向过程,而不太适合面向对象.