在代码文件中第一行输入:#!User/local/bin/lua,系统将直接用lua来解析这个脚本文件。
Lua解析器的用法:lua [选项参数] [脚本[参数]],常用参数有
在脚本代码中,可以通过全局变量arg来检索脚本的启动参数。脚本名称索引为0(前一个索引为-1),第一个参数为1,依此类推…
如:lua -e “sin=math.sin”script a b,则arg[0]=script,arg[1]=a,arg[-1]=“sin=math.sin”。
Lua中有8中基本类型:nil(空)、boolean(布尔)、number(数字)、string(字符串)、userdata(自定义类型)、function(函数)、table(表)、thread(线程)。函数type(val)可返回val的类型名。
nil是一种类型,只有一个值nil,在lua中也叫无效值。将nil赋给一个变量,可删除变量。
boolean类型只有2个值:false和true。在条件表达式中,只有值为false和nil为“假”,其它值均为“真”。
number用于表示实数,lua中没有整数类型。数字常量有2中表示法,分别为普通表示法和科学计数法,如:
4 0.4 4.57e-3 0.3e12 5e+20
string通常用于表示“一个字符序列”,其表示方式有4中:
在lua中,“…”用于连接字符串,特殊地:纯数字之间用空格隔一下。tonumber用于将字符串转成数字,tostring则可将数字转成字符串。
在lua中,函数作为“第一类值”看待。这表示函数可以存储在变量中,可以通过参数传递给其它函数,还可以作为其它函数的返回值。lua既可以调用以自身lua语言编写的函数,又可以调用以C语言编写的函数。
Table类型实现了“关联数组”,可以用特殊索引访问它。为整数索引时,可理解为数组;为字符串索引时,可理解为记录。基于table,还可实现一些较复杂的数据结构,如:队列、栈、集合、记录等。
Lua仅持有一个对它们的引用,不会产生新的副本或创建table,可理解为两层含义:
删除一个表中元素,可直接赋值nil。Lua中将nil作为数组的结尾标志,#t表示数组长度,若要处理中间元素为nil的“空隙”数组,可使用table.maxn(t)获取最大索引。
Lua中支持的算术操作符有:二元的+(加)、-(减)、*(乘)、/(除)、^(指数)、%(取模),一元的“-”(负号)。
特别地,x(1/2)表示平方根,x(1/3)表示立方根。
取模操作符的定义:a % b = a - math.floor( a / b ) * b。对于实数有这特殊用途,x % 1表示x的小数部分,x - x % 1表示x的整数部分,x - x % 0.01表示x精确到小数点后两位的结果。
angle % (2 * math.pi)表示将任意角度angle限制在[0.2Π]范围内。
关系运算符的结果只有true和false两种。
对于基本类型,只对其值作比较;而对于table、函数、userdata类型,lua只作引用比较,只有引用同一个对象时才判定为相等。
逻辑操作符有and、or、not。x=x or {} <> if not x then x={} end;C语言中的a?b:c <> (a and b)or c,其前提条件是:b不为假,否则无意义。
示例1:比较大小
max=(x>y) and x or y;
Lua中连接2个字符串,用…(两点)表示。连接操作符只会创建一个新串,不会修改原值(字符串为常量)。
构造式是用于创建和初始化table的表达式。最简单的构造式是{},用于创建一个空表。
两种常用的构造式:
table作为数组时,下表默认从1开始,方便与#t表长对应,但也可以显示申明将第一个元素写成t[0]。table结尾的逗号是可选的,中间的分号可用来表示不同的成分(分类)。
示例1:创建链表
List = nil
For line in io.lines() do
List={next=List,str=line}
End
Lua不同于其它语言,允许“多重赋值”。在多重赋值中,lua先对等号右边的所有元素求值,然后才执行赋值,利用这项特性可快速交换元素,如:x,y=y,x,也常用于函数的返回值。
赋值原则:若值的个数少于变量的个数,那么多余的变量湖北赋为nil;若值的个数更多的话,那么多余的值会被“静悄悄地”丢弃掉。
全局变量一般直接使用,局部变量用local作限定,其作用域仅限于声明它们的那个块。将外部变量赋值给局部变量,可加速在作用域内对其的访问,如:local fn = fn。一般在需要的地方声明局部变量,这样可以缩短变量的作用域,且有助于提高代码的可读性。
一个块是一个控制结构的执行体、或者是一个函数的执行体,再或者是一个程序块。显示声明一个块,只需将内容放在do … end之间。
一个分支都是以end为结尾,不能出现多个end。lua中没有switch语句,若要实现复杂的嵌套,可使用if - then - elseif - then - else - end结构。
while和repeat最大的区别在于,前者先判断条件,可能一次都不执行;后者先执行再判断条件,至少可执行一次,repeat循环需要以until结尾。与其它语言不同的是,在lua中,一个声明在循环体中的局部变量的作用域包括了条件测试。
数字型for基本语法:for var=exp1,exp2,exp3 do…end。Var从exp1变化到exp3,每次变化以exp3为步长递增,exp3是可选的,如果不指定默认为1。
此外,控制变量会自动地声明为for的局部变量,如需跳出循环体,使用bread语句。
泛型for通过一个迭代器(iterator)函数来遍历所有值。数字型for和泛型for,有两个共同的特点:①循环变量是循环体的局部变量②决不应该对局部变量作任何赋值。
lua标准库中常用的几种迭代器为:迭代文件中每行的(io.lines)、迭代数组元素的(ipairs)、迭代table元素的(pairs)、迭代字符串中单词的(gmatch)。具体语法如下:
Lua语言中是没有switch和continue语句的,但是我们可以模拟出其功能。
<1> 模拟switch,用函数和table来实现
Local switch = {
[case1]=function(arg)...end,
[case2]=function(arg)...end,etc
}
Switch[iCase](arg)
<2> 模拟contionue,用do…end包住整个for块
For k,v in pairs(t) do
Do
语句1
Break; --相当于continue的功能
语句2
End
End
在lua中,函数是一种对语句和表达式进行抽象的主要机制。
在面向对象式的函数调用中,提供:操作,可隐式传入self参数,表达式o.foo(o,x)可改写成o:foo(x)。
在lua中,函数声明时不能指定默认参数,但可以在函数体内通过“n = n or 1”或“t = t or {}”方式实现。
Lua具有一项非常与众不同的特征,允许函数返回多个结果,只需在return关键字后列出所有的返回值即可。
函数虽然能返回多个返回值,但某些情况下只能获取到部分值。将函数作为单独一条语句时,丢弃所有返回值;如果函数调用不是一系列表达式的最后一个元素,那么将只产生一个值。“一系列表达式”在Lua中表现为4种情况:多重赋值、函数调用时传入的实参列表、table的构造式和return语句。详细实例如下(假定函数f返回2个值):
如果将函数调用放在一对圆括号内,可强制其只返回一个结果,如(f())。
关于多重返回值,有一个特殊的函数——unpack,它接收一个数组作为参数。其功能是展开数组中的所有元素(下标从1开始),unpack的一项重要用途体现在“泛型调用”,通用语法:f(unpack(a)),f为任意函数,只要数组a取对应的参数值即可。
比如:
f = string.find;a={“hello”,“ll”}。
示例1:用lua实现unpack函数
Function unpack(t,i)
i = i or 1
if t[i] then
Return t[i],unpack(t,i+1)
End
end
在参数列表中,用3个点…表示变长参数,可当作table来解析(或先保存)。具有变长的实参,函数调用实参对形参作初始化时,跟“多重赋值”类似。
对于变长参数,如需获取指定位置的元素,可使用select函数。此函数会处理值为nil的参数,原型为select(n,…),当n为数字时,返回第n个可变实参;当为字符串“#”,则返回变长参数的长度。
Lua中的参数传递机制是具有“位置性”的,也就是在调用一个函数时,实参是通过参数表中的位置取匹配形参的。
具名参数可防止参数书写顺序问题。当一个函数拥有大量的参数时,很容易把参数位置搞错,将其参数列表打包table作为唯一参数传递是很有用的。
实例1:文件改名
Function rename(arg)
Return os.rename(arg.old,arg.new)
End
Local arg={old=“1.lua”,new=“1.lua”}
Rename(arg)
在设计一些接口的时候,可能有很多参数,但只有部分参数特别重要,此时可对原有函数进行封装,内部调用时指定部分默认值,关键参数作校验。这样,用户调用时只需关注部分参数,构造一个table即可,这也是一个很多的设计思路。
Lua中有一个容易混淆的概念,当讨论一个函数名时,实际上是在讨论一个持有某函数的变量,因为函数是匿名的。
函数在Lua中属于“第一类值”,不仅可以存储在全局变量中,还可以存储在局部变量甚至table的字段中。所以,可以将函数应用于回调、参数、table存储等方面。
Lua词法域:一个函数可以嵌套在另一个函数中,内部函数可以访问外部函数中的变量。
按变量的作用域来分,除全局变量和局部变量外,还有一种“非局部变量(外部变量)”,也就是closure中的局部变量。一个closure就是一个函数加上该函数所需访问的所有“非局部的变量”。
示例1:简单计数
Function newCount()
Local i=0
Return function()
i=i+1
return i
End
End
c1=newCount()
Print(c1) --结果1
Print(c1) --结果2
c2=newCount()
Print(c2) --新的closure,结果1
Print(c1) --持续累加,结果3
示例2:函数重定义
Do
Local sin=math.sin
Math.sin=function(x)
Reutrn sin(x*math.pi/180)
End
End
示例2中的奇妙之处在于,将老版本的sin保存在一个私有变量中,并使用do…end程序块限制sin的作用域。此后,只有新版本的sin才能访问到它,外部调用math.sin只能访问新版本的函数。
使用这种技术,可以创建一个安全的运行环境,即所谓的“沙盒(sandbox)”。
函数不仅可以作为全局变量,还可以存储在table的字段和局部变量中。典型的应用:面向对象和递归,如下:
示例1:面向对象
Lib={}
Lib.foo=function(x,y) return x+y end
Lib.goo=function(x,y) return x-y end
示例2:递归求阶层
Local function fact(n)
If n == 0 then return 1
Else return n*fact(n-1) end
End
对于一些间接的递归调用,需要提前将函数申明为局部变量,如:
Local f,g
Function g() ... f() ... end
Function f() ... g() ... end
Lua中函数中还有一个特性:尾调用消除,类似于goto语句,直接展开代码,不耗费任何栈空间。当一个函数调用另一个函数的最后一个动作时,该调用是一条“尾调用”,准确地说是“return ()”(也适用于复杂的表达式)。
由于“尾调用”不会耗费栈空间,所以一个程序可以拥有无数嵌套的“尾调用”,且不会造成栈溢出。
示例1:迷宫游戏
Function room1()
Local move = io.read()
If move == “south” then return room2()
Elseif move == “north” then return room3()
Else
print(“invaild romm”)
Return room1()
End
End
-- room2和room3实现类似
Function room4()
Print(“congratulations”)
end