Lua学习笔记Day2-Lua语法、值与类型、表达式、语句、函数
目录
- Lua是什么?
- Lua语法
- Lua类型与值
- Lua表达式
- Lua语句
- Lua函数
- Lua深入函数
本文内容来自《Lua程序设计》
Lua是什么?
Lua是一门简单小巧的脚本语言,能与其他语言相辅相成。Lua依靠别的语言实现实现这些语言的功能,Lua的其他特性是别的语言不擅长的,为此Lua实现了一个安全的运行环境、一套自动内存管理机制、优秀的字符串处理能力和动态数据的处理能力。
Lua可以看做一门功能齐全的胶水语言。Lua的特性:
可扩展性。Lua可以集成到其他语言甚至脚本语言中,用来扩展程序,一个应用程序可以通过Lua反复修改。
简单小巧。Lua具有的概念并不多,但都很有用。
高效。Lua是解释型脚本语言中最快的。
可抑制性。Lua可以运行在任何平台上,因为Lua依赖于C标准。
Lua语法
Lua保留字:
and break do if else elseif
end false for function
in local nil not or
repeat return then true until while
Lua是有大小写之分的
全局变量
b = 10
b即是全局变量,没必要将全局变量删除,如果一定要删除,就b = nil
Lua类型与值
普通类型
Lua有八种基础类型:nil、boolean、number、string、userdata(自定义数据)、function、thread、table。
任何变量都可以包含任何类型的值。
Lua将0和空字符串也视为”真”。
number类型表示实数(双精度浮点数)。string
Lua完全采用8位编码,可以将任何二进制数据存储到一个字符串中。Lua也不能直接修改字符串,而是创建一个新的字符串。Lua字符串可有单引号或双引号包括。
Lua的字符串是由内存自动管理的,无需担心字符串的分配和释放。
Lua不会转译[[和]]之间的任何内容。
..是字符串连接操作符,print(10 ..20)–>1020。注意10后面有空格。
字符串转数字:tonumber(string)。
数字转字符串:tostring(int)或int ..”“。
‘#’号+string 代表字符串长度。table
table表实现了”关联数组”,关联数组是一种有特殊的索引方式的数组,不仅可以通过整数索引它还可以使用字符串或其他类型的值。table没有固定大小,可以动态地添加任何数量的元素。table是Lua仅有的数据结构机制。
table不是值也不是变量,是一种对象。程序仅持有一个对他们的引用,table的创建是通过”构造表达式”完成的,最简单的方式是{}。
a = {}
b = a
a = nil 只有b还在引用table
b = nil 再也没有table的引用了
当一个程序再也没有对table的引用时,Lua的垃圾回收器(GC)最终会删除该table,并复用其内存。全局变量就是存放在一个普通的表中。
print(a[#a]) 打印table a的最后一个值
a[#a] = nil 删除最后一个值
a = {}
a[100] = 1
print(#a) –> 0 因为#碰到nil就结束
print(table.maxn(a)) –> 100function
function函数
Lua的function也是一种类型,可以储存在变量中,可以通过变量传递给其他函数,也可以作为返回值。
Lua可以调用Lua编写的函数也可以调用C语言编写的函数。userdata
userdata类型可以将任何C语言数据存储到Lua变量中,userdata没有太多操作,只能进行赋值和相等性测试。
Lua表达式
表达式用于表示一个值。
算术操作符
算术操作符:+-*/^% -负号
关系操作符:< > <= >= == ~=。如果两个值类型不相同就一定不相等。nil只于nil相等。对于table、userdata、函数,只有他们的引用相同时才相等。逻辑操作符
and or not,所有逻辑操作符将false和nil视为假,其余视为真。
and:第一个操作数为假返回第一个,否则返回第二个。
or:第一个操作数为真返回第一个,否则返回第二个。
x = x or v x为假或空时对x赋一个v
max = (x>y) and x or y 求x y的最大值。
操作符not永远只返回true或false优先级
^ 右结合
not # - 其余都是左结合
乘除求余
加减
.. 右结合
< > <= >= ~= ==
and
ortable构造式:a = {}
a = {x=10, y=20} 等价于 a = {};a.x = 10;a.y = 20
Lua语句
语句包括赋值、控制结构和过程调用。
多重赋值
Lua允许多重赋值:a,b = 10,2*x。x,y = y,x交换x,y的值。
多重赋值还用于接收多个函数返回值。局部变量
局部变量:i = 10是全局变量,local j = 10是局部变量。
局部变量仅作用于声明它的块。块是一个控制结构的执行体、函数的执行体、程序块。
显式的定义一个块:do end。尽可能的用局部变量,它可以避免将一些无用的名称引入全局环境,而且访问的更快,最后随着作用域的结束而消失。
local foo = foo。用局部变量foo保存全局变量foo,可以用来加速当前作用域对foo的访问。控制结构
if 条件 then
elseif then
else
end
Lua不支持switch
while 条件 do
end
repeat
until 条件
for var = exp1,exp2,exp3 do
break
end
var是局部变量,exp1是起始值,exp2是结束值,exp3是步长
for i,v in ipairs(array) do
break
end
i是索引值,v是对应于该索引的数组元素值,array是数组
for k in pairs(table) do
break
end
k是控制变量,table是表。
for line in io.lines() do
end
do return end用于显式的跳过一段代码
Lua函数
Lua函数是对语句和表达式进行抽象的主要机制。当函数的参数只有一个并且参数是字符串或table表时,函数的括号可以不写。
多重返回值
多个参数:
function f(a,b) return a or b end f(3) --> a = 3,b = nil f(3,4) --> a = 3,b = 4 f(3,4,5) --> a = 3,b = 4
多返回值:return m1,m2即可
function foo0() end 无返回值
function foo1() return”a” end 返回一个
function foo2() return”a”,”b” end 返回两个
print(foo2(),1) 结果是a 1,Lua会将foo2返回值数量调到1个(print()函数的参数个数)。
t = {foo2()} –> t = {“a”, “b”}
不过t = {foo(),foo2(),4} –> t = {nil,”a”,4} foo2()仍只有一个返回值。即函数调用不是表达式最后一个元素时只产生一个值。
return foo2()也会返回foo2()的所有返回值。但return (foo2()) –>只返回一个值。unpack函数,接收一个数组从下标1开始返回该数组的所有元素。比如a = {“hello”,”ll”} string.find(unpack(a))
变长参数
function add(…) …就代表不同数量的实参,这个参数就叫变长参数,访问时仍用…。
…的行为类似具有多重返回值的函数,返回的是当前函数所有的变长参数,如local a,b = …。
访问变长参数就像访问table一样,但有可能其中有nil,所以要用到select:for i=1,select('#',...) do local arg = select(i,...) end
select(‘#’,…) 得到…的长度,包括nil。select(i,…)得到第i个参数。
具名参数
Lua中的参数传递是具有位置性的。我们可以将参数组织到一个table中,把table作为唯一实参传递给函数。
function rename(arg) rueturn os.rename(arg.old,arg.new) end
如果一个函数拥有大量的参数,而且大部分参数可选的情况下,这种风格很有用。函数内部会检查必填参数,或为其他参数添加默认值。
Lua深入函数
函数是第一值类型,即函数和其他传统变量具有相同的权利。函数可以储存到局部变量或者全局变量或table。在Lua中讨论函数时,实际在讨论持有某函数的变量。
定义一个函数其实就是一条赋值语句:
function foo(x) return x end
foo = function(x) return x end
匿名函数:function(x)<body>end,不用变量接收就是一个匿名函数。应用:比如table.sort()函数:
table.sort(network,function(a,b) return(a.name < b.name) end) 将表network按name从小到大排序。
像sort()这种接收一个函数作为参数就是一个高级函数。
词法域
词法域:将一个函数写在另一个函数之内,那么内部函数可以访问外部函数中的局部变量。
闭合函数:一个闭合函数就是一个函数加上所要访问的所有非局部变量。普通函数本身就是一个闭合函数。function cc() local i = 0 return function() i = i + 1 --匿名函数内的变量既不是全局又不是局部 return i end end c = cc() c1 = cc() print(c()) --> 1 print(c()) --> 2 print(c1()) --> 1
这种情况应用在回调函数中十分方便,也可以用来重定义函数。这种技术可以创建一个安全的运行环境,即”沙盒”,来执行不受信任的代码(网上接收到的代码)。
比如把math.sin参数变为角度local oldSin = math.sin local k = math.pi/180 math.sin = function(x) return oldSin(x*k) --调用原来的sin end end
非全局的函数
将函数储存在table中。将函数储存到局部table中就得到了一个局部函数,这个函数只能在某个作用域内使用。因为Lua将程序块作为一个函数来处理的,所以一个程序块声明的函数就是局部函数,局部函数在程序块中可见,词法域确保程序包中其他函数可以使用这些局部函数。
储存到表中:Lib = { foo = function(x,y) return x+y end goo = function(x,y) return x-y end } 也可以: Lib = {} function Lib.foo(x,y) return x+y end function Lib.goo(x,y) return x-y end 局部函数的定义: Local function f(<参数>) <函数体> end
尾调用
当一个函数调用是另一个函数的最后一个动作,如:function f(x) return g(x) end。Lua的尾调用不会消耗任何栈空间。
function foo(n)
if n>0 then return foo(n-1) end
end
调用这个函数时,传入任何参数都不会造成栈溢出。
尾调用的用途就是编写状态机,一个函数就是一个状态,改变状态就是goto到另一个函数。迭代器与泛型for
所谓的迭代器就是一种可以遍历一种集合中所有元素的机制。Lua把迭代器表示为函数,每次调用函数就返回集合的下一个元素。闭合函数为这种情况提供了支持。
function value(t) local i = 0 return function() i=i+1 return t[i] end end 调用: t = {10,20,30} iter = value(t) while true do local ele = iter() if(ele == nil) then break end print(ele) end
大部分情况下我们不需要写value(t)这种迭代器工厂。因为上述迭代器每次循环都会创建新的闭合函数。
泛型for
for var-list in exp-list do <函数体> end
var-list是变量名列表,第一个元素是控制变量,exp-list是表达式即对迭代器工厂的调用。for先对in后的表达式求值,返回3个值供for保存:迭代器函数、恒定状态、控制变量的初始值。这类似于多重赋值。然后,for会以恒定状态和控制变量来调用迭代器函数,然后将迭代器函数返回值赋予变量列表中的变量,如果返回值为nil则结束循环。
无状态迭代器
无状态迭代器不保存任何状态的迭代器,我们可以在多个循环中使用同一个无状态迭代器,避免创建闭合函数的开销。典型应用是ipairs和pairs。
具有复杂状态的迭代器
通常迭代器需要保存许多状态,简单的方法是使用闭合函数,或者把迭代器所需的所有状态打包为table,保存在恒定状态中。这样一个迭代器可以通过table保存任意个数据了。但闭合函数实现迭代器更加高效。我们可以用协同实现迭代器,功能最强但有一定开销。