Lua5.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结构会比较常见

if a < 0 then
    t = -1;
elseif a = 0 then
    t = 0
else
    t = 1
end

while结构
while i < 10 do
    print(i*2);
    i = i + 1;
end

repeat结构

repeat
    print(2*i);
    i = i + 1;
until
    i < 10;

for结构

for i = 1, 10, 1 do
   print(2*i)
end

泛型for
for k,v in pairs(t) do
    print(k,v)
end

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

5 函数

函数调用时需要将参数放在括号中,但如果只有一个参数且参数为字符串或table构造表达式时,可不使用括号。
function foo(str,key)
    local start,end = string.find(string,key);
    return start,end
end

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

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

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

function add(...)
    local s = 0;
    for i,v in ipairs(...)
        s = s + v
    end
    return s
end

利用table,可以使用键值参数,如
fun(arg)
    return arg.old,arg.new;
end

fun({old="1",new="2"});

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

Lua中的模块即使用在了table结合函数指针。
Lib = {}
Lib.fun = function() ... end
Lib.foo = function() ... end

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


7 迭代器与泛型for
和ipairs等价的迭代器
function values(t)
    local i = 0;
    return function() i=i+1 return t[i] end
end

for的实现
for var_1,...,var_n in <explist> do
    <block>
end
--等价于下面的代码
do
    local _func, _state,_var = <explist>
    while true do
        local var1,...,var_n = _func(_state,_var)
        _var = var1
        if _var == nil then
            break
        end
        <block>
    end
end

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

loadfile只加载文件,但不执行。dofile()则不仅仅会加载而且会执行。loadstring()则加载一段代码的字符串。需要注意的是对于字符串,其在运行时编译时,会在全局变量环境中查找变量。调用会返回一个函数闭包。
function dofile(file)
    local f = assert(loadfile(file))
    f()
end

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

8.2 加载C库
Lua提供的底层关于动态链接的功能聚集在package.loadlib函数上。
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返回值。
if pcall (func) then
   <block> --成功执行func
else then
    <block> --异常处理
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状态。

示例:使用协程实现生产者和消费者
function producer(consumer)
    while(true) do
        x=io.read("*number");
        send(x,consumer)
    end
end

function filter(consumer)
    while(true) do
        x = receive();
        x = x*x ;
        send(x,consumer)
    end
end

function consumer()
    while(true) do
        x = receive()
        print("In consumer use ",x)
    end
end

function send(x,consumer)
    coroutine.resume(consumer,x)
end

function receive()
    value = coroutine.yield();
    return value;
end

coc = coroutine.create(consumer);
cof = coroutine.create(filter);
cop = coroutine.create(producer);

coroutine.resume(coc)
coroutine.resume(cof,coc);
coroutine.resume(cop,cof)


参考文献

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


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值