参考Programming in Lua,学习Lua的第一部分语言总结
一、chunks:一组放在一起的语句组。
二、全局变量:与nil相关。一个全局变量不是nil的时候即存在,是nil的时候即不存在。所以全局变量本身不需声明,与C++相比灵活。
三、八种变量:
1、nil:表示这个值没有
2、booleans:false和nil表示假,其余所有都是真
3、numbers:任意实数
4、string:单双引号都可以表示字符串,习惯使用双引号。字符串不可修改,即将a="one string"的one改为another,需要重新为a赋值,b=string.grusb(a,"one","another")。但是a不会改变。
string和number之间用是自动转换的
5、function:声明函数。可以认为任何一个函数都是一个变量。
6、userdata:后补充
7、thread:后补充
8:table:
(1)构造函数仅仅用于初始化,之后可以添加或删除
(2)无法调用的使用索引调用,且这一部分数据自己从1开始计算索引。
<span style="font-size:18px;">a={"xxx",x=1,y=2,"yyy"}
print(a[1])
print(a.x)
print(a.y)
print(a[2])
print(a[3])</span>
输出:
xxx
1
2
yyy
nil
四、运算符:
1、a==b:a和b类型不同则必不相同;Lua的比较通过引用比较,赋值也是通过引用赋值。所以,a和b声明内容相同(table,如果声明为一个普通内置变量则不成立),==时却不同。用a给c赋值,则a==c为真。
2、二元逻辑运算符,通过前一个元判断整体。若前一个元可以决定整体就返回前一个元,否则返回后一个元:
a and b:a为false则返回a,否则返回b
a or b:a为true则返回a,否则返回b
C++中的三元运算:a?b:c
lua中的三元运算:(a and b) or c
五、局部变量与代码块
local:局部变量,只在代码块中有效;在某些地方,使用类似于C++中的static
代码块:一个chunk,类似于C++中的{}。用do...end可以表示{}。
局部变量的使用主要要和注意交互模式,在交互模式中可以使用do...end可以表示{}。
六、控制语句
1、与C++相比,用repeat...until代替了do...while
2、for有两类
(1)数值for循环:
for i=var1(开始),var2(终止),var3(步长,默认1,可省略) do
end
以上声明方式导致var都是for的local,出了for就没用了
三个var只在循环开始前做一次计算,不再修改
(2)泛型for循环:遍历迭代子函数返回的每一个值,后补充
七、函数
1、可以多个返回值。当函数调用是参数中的唯一参数或者最后一个参数的时候,尽量多的返回返回值;其余情况下只能返回第一个值。
<span style="font-size:18px;">function foo()
return 1,2;
end
x,y=foo(),3
print(x)
print(y)</span>
打印1,3
<span style="font-size:18px;">function foo()
return 1,2;
end
a={foo(),3}
print(a[1])
print(a[2])
print(a[3])</span>
打印 1,3,nil
2、函数的命名参数的使用:简而言之就是将多个参数放到一个表中,在函数中,通过表调用函数
<span style="font-size:18px;">nametable={oldname="old",newname="new"}
function rename(arg)
return os.rename(arg.oldname,arg.newname)
end</span>
3、高级函数:函数的参数中有另一个函数即称为是高级函数
4、关于闭包:一个嵌套在函数的函数和其外部函数中的local变量共同构成
<span style="font-size:18px;">function counter()
local i=0; --<span style="color:#ff0000;">类似于C++中的static</span>
return function() --匿名函数
i=i+1;
return i;
end
end
c1=counter();
c2=counter();
print(c1()); --c1作为一个闭包
print(c1()); --同一个c1闭包
print(c2()); --另一个闭包c2
</span>
输出:
1
2
1
对比:
<span style="font-size:18px;">function add(i)
i=i+1;
return i;
end
function counter()
local i=0;
return add(i);
end
c1=counter();
c2=counter();
print(c1); --c1作为一个闭包
print(c1); --同一个c1闭包
print(c2); --<span style="font-family: KaiTi_GB2312;">同</span>一个闭包
</span>
输出:
1
1
1
对比:
<span style="font-size:18px;"><span style="background-color: rgb(255, 255, 255); font-family: KaiTi_GB2312; font-size: 18px;"></span><pre name="code" class="plain">function counter()
i=0; --类似于C++中的static
return function() --匿名函数
i=i+1;
return i;
end
end
c1=counter();
c2=counter();
print(c1()); --c1作为一个闭包
print(c1()); --同一个c1闭包
print(c2()); --另一个闭包c2</span>
输出:
123
5、局部函数递归定义的时候需要先声明再使用,否则在调用递归的时候会先寻找全局函数中相同名字的函数寻求调用:<span style="font-size:18px;">function fact(n)
return n;
end
local fact=function(n)
if n==0 then
return 1;
else
return n*fact(n-1);
end
end
print(fact(5))
</span>
输出是20。表示第一次调用的是local,递归的第一次调用是全局的fact
6、尾调用:函数的最后一个动作是调用其他函数,像是goto一样
<span style="font-size:18px;">function f()
return g();
end</span>
调用g()后,不需要再栈中保留f的相关信息,所以尾调用用于递归可以无限制。
八、迭代器
迭代器支持指针类型的结构,遍历集合所有元素,使用函数描述迭代器,每次调用迭代器函数都要返回集合的下一个元素。迭代器保留上一次成功调用的状态和下一次成功调用的状态。
闭包:闭包本身+创建闭包的工厂
迭代器要和闭包结合起来,代码如下:
<span style="font-size:18px;">function list_liter(t) --创建闭包的工厂
local i=0;
local n=table.getn(t);
return function() --和以上的两个local构成一个闭包,即通过i保存了每上一次和下一次的所有调用状态
i=i+1;
if i<=n then
return t[i];
end
end
end
t={1,2,3};
itor=list_liter(t);
while true do
local element=itor();
if element==nil then
break;
end
print(element);
end
</span>
迭代器可以分为无状态的迭代器和多状态的迭代器,但是尽量写无状态迭代器;其次使用闭包。尽量不用使用table的多状态迭代器。
上一个代码例子可以用泛型for遍历
<span style="font-size:18px;">function list_liter(t)
local i=0;
local n=table.getn(t);
return function() --和以上的两个local构成一个闭包,即通过i保存了每上一次和下一次的所有调用状态
i=i+1;
if i<=n then
return t[i];
end
end
end</span>
<span style="font-size:18px;">t={1,2,3};
for elemment in list_liter(t) do
print(elemment)
end</span>
(1)泛型for的表达式:
for <var-list> in <exp-list> do<body>
end
<var-list>:变量列表名列表,第一个变量是控制变量,为nil的时候循环结束
<exp-list>:表达式列表,一般只有一个值,就是迭代工厂
(2)运行步骤:
[1]计算表达式,返回三个值,迭代函数,状态常量,控制变量
[2]调用迭代函数,参数是状态常量和控制变量
[3]将迭代函数的返回值赋值给变量列表,检查控制变量
<span style="font-size:18px;">for var1,var2,...,varn in explist --var1是控制变量
do block
end</span>
以上代码等价于
<span style="font-size:18px;">do
local f,s,var=explist --f表示迭代函数,s表示状态常量,var控制变量
while true do
local var1,var2,...,varn=f(s,var);
var=var1
if var==nil then
break;
end
end
end</span>
九、dofile,loadfile,loadstring,require区别:
Lua将代码预编译为中间码再执行。解释型语言的特征是编译器是语言运行时的一部分。
dofile:编译+执行。可以认为是loadfile+错误保护机制。每次编译每次执行。
loadfile:编译但不执行,本身就可以返回错误信息。一次编译多次运行。
loadstring:功能强大但陷阱多。读入一个字符串当做一个函数做处理
require:类似于dofile,但是会搜索目录加载文件且可以避免重复加载同一文件,尽量使用这个。
loadfile:编译但不执行的理解用例
-- file `foo.lua'
function foo (x)
print(x)
end
如果只执行f=loadfile("foo.lua"),则foo仅仅被编译,没有定义
f() -- defines `foo'
foo("ok") --> ok
loadstring:
调用loadstring时,只寻找全局变量做操作。对local变量无法找到