Lua5.1编程1:Lua语言基础

Lua的设计目的是依赖C的高效,并提供C所没有动态结构、高层抽象、内存回收、高阶函数等功能。

Lua本身很小,功能有限,大部分功能来源于其标准库。但本身提供的简单、高效、高扩展、可移植等特性,使得在特定场景中Lua有用武之地。

1 语言基础
1.1 词法约定

Lua语言是一种区分大小写的语言。变量命名和C一样,只能使用字母数字和下划线,数字不能位于开始。以下划线开始的变量默认为Lua自己使用的变量。

Lua中有20+个关键字,不能作为变量使用。诸如for while do end之类的。

Lua中的语句最好以;进行分隔。

1.2 注释
-- 这是行注释
--[[ 这是块
     注释 
  ]]

1.3 变量
变量不需要声明,无修饰的变量默认为全局变量,变量未初始化时值为nil。如果需要删除一个变量,将其置为nil即可。

系统初始化的全局变量 : _VERSION lua的版本号;_PROMPT  解释器的提示符样式

局部变量使用local来修饰,如local x = 0; 局部变量的作用域只限于声明它们的块。

lua的变量为词法变量,在局部作用域中,局部变量会覆盖全局作用域中的同名变量。

1.4 lua解析器程序

lua代码有几种方式执行:


lua prog.lua args 
lua -e "lua code;"
如果传递了脚本参数,则会创建一个名为arg的表,脚本名位于索引0上,其后的参数索引从1开始,其前的参数则位于负数索引上。

2 类型与值

Lua是动态类型语言,类型不在变量名上绑定,而在值中存储。

Lua中有8种基础类型,nil bool number string function thread table userdata。所有的类型均为FirstClass的。type()可以返回一个变量的类型。

number 表示的为双精度浮点数,取值范围大约在1.79e-308,1.79e+308之间,计算时不会出现四舍五入的错误。
string 表示一个字符序列,采用8位编码,可以包含C的转义序列。其为不可修改的值对象。类似于python的doc string 可以将使用[[]]将一大块字符串包含在其中,而避免其中的特殊字符被转义。 #"hello" = 5
   tonumber()  将字符串转换为数
   tostring()      将数字转换为字符串
   ..                     字符串连接符
table  类似于python中的dict,表示关联数组。不仅可用整数来索引,也可用字符串等类型来索引。索引从1开始。
   初始化 t = {1,2,3}; t = {x=1,y=2}; t ={['key']=value, }; 
   取值   t[1] = 10; t['key'] = value; t.key = value; 
userdata 用于存储C语言传递的数据结构。

3 表达式与语法

3.1 运算符

算法运算符  + - * / ^ % 
关系运算符  < <= > >= ~= ==
逻辑运算符 and or not 

赋值 x=1; x,y = y,x; t[1] = 0;  如果右侧值过少,则多余的变量赋值为nil

3.2 控制结构
Lua不支持switch,if..else结构会比较常见

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. if a < 0 then  
  2.     t = -1;  
  3. elseif a = 0 then  
  4.     t = 0  
  5. else  
  6.     t = 1  
  7. end  

while结构
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. while i < 10 do  
  2.     print(i*2);  
  3.     i = i + 1;  
  4. end  

repeat结构

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. repeat  
  2.     print(2*i);  
  3.     i = i + 1;  
  4. until  
  5.     i < 10;  

for结构

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. for i = 1, 10, 1 do  
  2.    print(2*i)  
  3. end  

泛型for
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. for k,v in pairs(t) do  
  2.     print(k,v)  
  3. end  

支持break退出当前循环,return用于从当前函数返回。

5 函数

函数调用时需要将参数放在括号中,但如果只有一个参数且参数为字符串或table构造表达式时,可不使用括号。
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. function foo(str,key)  
  2.     local start,end = string.find(string,key);  
  3.     return start,end  
  4. end  

传递参数时和多重赋值类似,如果形参少,则未能赋值的值为nil,多余的参数将丢弃。此外函数可以返回多重值。

unpack可以将数组中的元素解出来,作为多重值返回。

...可以作为变长参考来进行传递和使用。...的行为类似于一个具有多重返回值的函数,返回所有参数。

[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. function add(...)  
  2.     local s = 0;  
  3.     for i,v in ipairs(...)  
  4.         s = s + v  
  5.     end  
  6.     return s  
  7. end  

利用table,可以使用键值参数,如
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. fun(arg)  
  2.     return arg.old,arg.new;  
  3. end  
  4.   
  5. fun({old="1",new="2"});  

6 闭包
Lua支持词法作用域,同时函数又是第一类的。因此可以实现函数式编程的范式。高阶函数可以使用函数处理逻辑。
这样内部定义的函数可以访问外层函数的局部变量。并将外层的变量闭包在自己的对象中。从而可以脱离外层函数来单独调用。

Lua中的模块即使用在了table结合函数指针。
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. Lib = {}  
  2. Lib.fun = function() ... end  
  3. Lib.foo = function() ... end  

对于递归调用,支持尾递归。但需要实现尾调用。即函数在调用完另一个函数之后,是否无其他事情需要做了。
只有形式上是return <fun>(args); 这样的调用才算是尾调用。只要args在调用前能求出值即可。


7 迭代器与泛型for
和ipairs等价的迭代器
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. function values(t)  
  2.     local i = 0;  
  3.     return function() i=i+1 return t[i] end  
  4. end  

for的实现
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. for var_1,...,var_n in <explist> do  
  2.     <block>  
  3. end  
  4. --等价于下面的代码  
  5. do  
  6.     local _func, _state,_var = <explist>  
  7.     while true do  
  8.         local var1,...,var_n = _func(_state,_var)  
  9.         _var = var1  
  10.         if _var == nil then  
  11.             break  
  12.         end  
  13.         <block>  
  14.     end  
  15. end  

8 文件加载与错误处理
8.1 加载文件

loadfile只加载文件,但不执行。dofile()则不仅仅会加载而且会执行。loadstring()则加载一段代码的字符串。需要注意的是对于字符串,其在运行时编译时,会在全局变量环境中查找变量。调用会返回一个函数闭包。
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. function dofile(file)  
  2.     local f = assert(loadfile(file))  
  3.     f()  
  4. end  

只有执行了f(),文件中的定义才能生效,接下来才能使用文件中定义的函数等。

8.2 加载C库
Lua提供的底层关于动态链接的功能聚集在package.loadlib函数上。
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. local f = package.loadlib("/usr/lib/socket.so","luaopen_socket").  

通常我们并不需要直接使用它,只需要require("socket")即可。

8.3 错误与异常处理
assert(expr,msg) 当expr表达式为true时返回expr,为假时触发错误,并打印msg信息。
error("msg")     抛出异常并输出msg,error可以返回任何类型的数据,如返回table
pcall(func,fun_args)      以try..catch类似的方式,捕获func调用中出现的error异常,其返回两个参数,第一个参数表示是否出现异常,第二个异常或func返回值。
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. if pcall (func) then  
  2.    <block> --成功执行func  
  3. else then  
  4.     <block> --异常处理  
  5. end  

xpcall(func,errHandler)  当出现异常时,在异常现场调用errHandler
debug.traceback()  打印当前的调用栈

9 协同例程(coroutine)
coroutine类似于一个独立的执行线程逻辑,但与线程的区别是,多个coroutine并不是同时执行的,而互相协调,串行执行的。不执行的将让出CPU,以让其他的coroutine来执行。
coroutine.create(func)             创建一个协程,返回协程的id,此时co处于suspended状态
coroutine.resume(co,args)    执行创建的协程,
coroutine.status(co)                返回协程的状态
coroutine.yield(args)               当前运行的协程,通过调用yield挂起自己,使得之前的resume调用可以返回yield传递的参数。只有再次调用resume时,yield()才返回resume调用传 入的参数,使得协程可以向下运行,并交换数据。

运行中协程处于running状态,运行结束则处于dead状态。对于一个死亡状态的协程,调用resume将返回false和一个错误消息。如果协程A在运行中resume了协程B,则A处于normal状态。

示例:使用协程实现生产者和消费者
[plain]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. function producer(consumer)  
  2.     while(true) do  
  3.         x=io.read("*number");  
  4.         send(x,consumer)  
  5.     end  
  6. end  
  7.   
  8. function filter(consumer)  
  9.     while(true) do  
  10.         x = receive();  
  11.         x = x*x ;  
  12.         send(x,consumer)  
  13.     end  
  14. end  
  15.   
  16. function consumer()  
  17.     while(true) do  
  18.         x = receive()  
  19.         print("In consumer use ",x)  
  20.     end  
  21. end  
  22.   
  23. function send(x,consumer)  
  24.     coroutine.resume(consumer,x)  
  25. end  
  26.   
  27. function receive()  
  28.     value = coroutine.yield();  
  29.     return value;  
  30. end  
  31.   
  32. coc = coroutine.create(consumer);  
  33. cof = coroutine.create(filter);  
  34. cop = coroutine.create(producer);  
  35.   
  36. coroutine.resume(coc)  
  37. coroutine.resume(cof,coc);  
  38. coroutine.resume(cop,cof)  


参考文献

Lua程序设计第二版
Lua Reference 翻译 http://www.photoneray.com/Lua-5.2-Reference-Manual-ZH_CN/

993 年在巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro in Brazil)诞生了一门编程语言,发明者是该校的三位研究人员,他们给这门语言取了个浪漫的名字——Lua,在葡萄牙语里代表美丽的月亮。事实证明她没有糟蹋这个优美的单词,Lua语言正如它名字所预示的那样成长为一门简洁、优雅且富有乐趣的语言。 Lua从一开始就是作为一门方便嵌入(其它应用程序)并可扩展的轻量级脚本语言来设计的,因此她一直遵从着简单、小巧、可移植、快速的原则,官方实现完全采用ANSI C编写,能以C程序库的形式嵌入到宿主程序中。Lua的每个版本都保持着开放源码的传统,不过各版采用的许可协议并不相同,自5.0版(最新版是5.1) 开始她采用的是著名的MIT许可协议。正由于上述特点,所以Lua在游戏开发、机器人控制、分布式应用、图像处理、生物信息学等各种各样的领域中得到了越来越广泛的应用。其中尤以游戏开发为最,许多著名的游戏,比如Escape from Monkey Island、World of Warcraft、大话西游,都采用了Lua来配合引擎完成数据描述、配置管理和逻辑控制等任务。 作为一门过程型动态语言,Lua有着如下的特性:1、变量名没有类型,值才有类型,变量名在运行时可与任何类型的值绑定;2、语言只提供唯一一种数据结构,称为表(table),它类似key-value关联数组,可以用任何类型的值作为key和value。提供了一致且富有表达力的表构造语法,使得Lua很适合描述复杂的数据;3、函数是一等类型,支持匿名函数和正则尾递归(proper tail recursion);4、支持词法定界(lexical scoping)和闭包(closure);5、提供thread类型和结构化的协程(coroutine)机制,在此基础上可方便实现协作式多任务;6、运行期能编译字符串形式的程序文本并载入虚拟机执行;7、通过元表(metatable)和元方法(metamethod)提供动态元机制(dynamic meta-mechanism),从而允许程序运行时根据需要改变或扩充语法设施的内定语义;8、能方便地利用表和动态元机制实现基于原型(prototype-based)的面向对象模型;9、从5.1版开始提供了完善的模块机制,从而更好地支持开发大型的应用程序; Lua 的语法类似PASCAL和Modula但更加简洁,所有的语法产生式规则(EBNF)不过才60几个。熟悉C和ASCAL的程序员一般只需半个小时便可将其完全掌握。而在语义上Lua则与Scheme极为相似,她们完全共享上述的1、3、4、6点特性,Scheme的continuation与协程也基本相同只是自由度更高。最引人注目的是,两种语言都只提供唯一一种数据结构:Lua的表和Scheme的列表(list)。正因为如此,有人甚至称Lua为“只用表的Scheme”。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值