Lua总结一

值和类型(Values and Types)

Lua是一门动态类型语言,这意味着变量没有类型,只有值有类型。语言没有类型定义,所有值携带自己的类型。

Lua中所有的值都是一等公民,这意味着所有的值都可以存储在变量中,作为参数传递给其他函数,作为函数的结果返回。

值得注意的是这点对函数也成立,在Java中函数是没这个待遇的。比如对一个列表排序,需要一个比较函数,Lua可以直接传递比较函数,而Java需要为这个比较函数创建一个类型,然后传递这个类型的实例。

来看一个例子,将一个列表从大到小排列:

Lua

list = {1, 2, 3, 4, 5}

function comp(i, j)
    return i > j
end

table.sort(list, comp)

Java

List<Integer> list = new ArrayList<Integer>(Arrays.asList(new Integer[] { 1, 2, 3, 4, 5 }));

Comparator<Integer> comp = new Comparator<Integer>()
{
    @Override
    public int compare(Integer o1, Integer o2)
    {
        return o1 < o2 ? 1 : -1;
    }
};

Collections.sort(list, comp);

Lua提供了八种基础数据类型:nil、boolean、number、string、function、userdata、thread、table。

nil:表示空。

boolean:布尔类型,只有nil和false表示false,其他都为true。0和1都表示true。

number:数值类型,两种内部表示,integer和float,默认为64位整型和双精度浮点型。

string:字符串类型,值是不可变的,每次修改都会创建一个新的string对象。同Java的StringBuilder一样,Lua提供了table.concat来拼接字符串。

function:函数类型。他是一等公民。

userdata:允许C中的数据保存在Lua中。这有什么作用呢?Lua作为一种嵌入式语言,自身的API是很少的,但是可以很方便的通过C来扩展功能。比如为Lua提供socket功能,使它能够像如下方式使用:

connection = socket.connect(host, port)
connection.write(msg)

这个connection是一个连接对象,它是由CAPI创建的一个userdata,可以在Lua中使用。

thread:独立的执行线程,用于实现协程。Lua的thread不同于操作系统的thread,即时操作系统不支持thread,Lua能在这种操作系统上支持协程。(协程后面介绍)

table:表,Lua仅有的数据结构。不过可以用于表示数组,集合,链表,树,图等数据结构。

环境与全局环境(Environmets and the Global Environment)

当引用一个未被声明的变量var,句法上表示_ENV.var。_ENV的值是一个table,这个值被称为环境。Lua中每个被编译的chunk都有一个额外的local变量,称为_ENV。 当访问一个非local变量,会从_ENV中查找,当定义一个非local变量,会存储在_ENV中。

Lua中有个一个特殊的环境叫做全局环境(_G),_ENV的默认值就是全局环境。所以当你定义一个非local的变量,默认为全局变量。

如果我们定义自己的环境_ENV,那么非local变量就会存储在自定义的环境中,来看一个例子(lua5.2以上版本):

employee1 = {_G = _G}  -->保留全局环境的引用
_ENV = employee1
id = 1              -->不会作为全局变量
name = "xiaoming"
salary = 3000

_ENV = _G
employee2 = {_G = _G}
_ENV = employee2
id = 2
name = "daming"
salary = 20000

_ENV = _G
print(employee1.id, employee1.name, employee1.salary)
print(employee2.id, employee2.name, employee2.salary)

错误处理(Error Handing)

Lua作为一门嵌入式扩展语言,所有的行为都是从宿主程序的一次C函数调用开始的。当Lua编译或者运行发生错误时,会把控制权交给宿主程序,由宿主程序处理错误。

Lua可以通过error函数来抛出一个错误。如果Lua想捕获错误,则需要在保护模式下运行代码,即通过pcall或xpcall来执行方法。

当使用xpcall时,需要提供一个错误处理函数,他可以在错误发生且栈还未展开时执行。如果栈还未展开,这意味你可以获得调用上下文的所有信息,比如当前环境中的一个本地变量。不过不能错误处理函数中直接使用上下文中信息,需要借助debug库,来看一个例子:

function get1()
    local i = 1
    get2()
end

function get2()
    local j = 2
    get3()
end

function get3()
    local k = 3
    error("throw error")
end

function handler(msg)
    --print(debug.traceback())            -->通过打印栈回溯信息确定方法所在的level
    name, value = debug.getlocal(3, 1)     -->获取get3方法的本地变量k
    print(name, value)
    name, value = debug.getlocal(4, 1)     -->获取get2方法的本地变量j
    print(name, value)
    name, value = debug.getlocal(5, 1)     -->获取get1方法的本地变量i
    print(name, value)
    return "warp "..msg
end

xpcall(get1, handler)

输出:

k   3
j   2
i   1

元表和元方法(Metatables and Metamethods)

Lua为每一个值都提供了元表,元表就是一个table,它定义了原始值在特定操作下的行为。行为由方法提供,这种方法称为元方法。元表可以控制很多操作,比如加法,取长度,比较,索引等等。

table和userdata类型的值有独立的元表,其他类型的值分别共享一张元表。

比如我们的变量可以存储在三个地方,global域,session域,page域,查找顺序为page,session,global。用元表可以这样来实现:

global = {a = 1}
session = {b = 2}
session_mt = {__index = global}     -->定义元表session_mt和元方法__index
setmetatable(session, session_mt)   -->session中查找不到的key会去global中查找
page = {c = 3}
page_mt = {__index = session}
setmetatable(page, page_mt)

print(page.c, page.b, page.a)

输出:

3   2   1

协程(Coroutines)

Lua中的协程又叫协同式多线程,协程之间不会抢占执行,只有在协程内部调用yield才会让出执行权。

Lua中的每个协程都是独立的执行序列,协程之间不共享内存。

Lua协程很多特点,比如yield之后调用resume继续执行,会从上次yield的地方继续执行,并且可以访问到yield之前的那些局部变量。

闭包(Closure)

当函数A调用函数B时,函数B可以操作函数A中的局部变量,这种形式叫做闭包。看一个例子:

function retfoo()
    local i = 0
    return function ()
        i = i + 1
        return i
    end
end

foo = retfoo()
print(foo(), foo())

输出:

1   2

闭包的作用在于可以保存状态,让多次调用之间产生关系。

For语句

Lua中提供了两种形式的for语句,numeric for 和 generic for。

  1. numeric for 的语法如下
stat ::= for Name ‘=’ exp ‘,’ exp [ ‘,’ exp ] do block end 

block把Name作为循环变量,起始值为第一个exp,直到第二个exp为止,步长为第三个exp。

一个numeric for像这样:

for v = e1, e2, e3 do block end

它等价于下面的代码

do
    local var, limit, step = tonumber(e1), tonumber(e2), tonumber(e3)
    if not (var and limit and step) then error() end
    var = var - step
    while true do
        var = var + step
        if (step >= 0 and var > limit) or (step < 0 and var < limit) then
            break
        end
        local v = var
        block
    end
end
  1. generic for 的语法如下
stat ::= for namelist in explist do block end
namelist ::= Name {‘,’ Name}

一个generic for像这样:

for var_1, … , var_n in explist do block end

它等价于下面的代码

do
    local f, s, var = explist
    while true do
        local var_1, … , var_n = f(s, var)
        if var_1 == nil then break end
        var = var_1
        block
    end
end

f, s, var 可以看作迭代函数,迭代不变量,迭代变量。

如此Lua的迭代器可以分成两种,一种是有状态的,一种是无状态的。

有状态的迭代器采用闭包来实现,比如编写一个迭代器来遍历table数组。

function ipairs(table)      -->有状态的迭代器
    i = 0
    return function()
        i = i + 1
        if i > #table then return nil end
        return i, table[i]
    end
end

table = {"a", "b", "c", "d", "e"}
for i, v in ipairs(table) do
    print(i, v)
end

有状态的迭代器的缺点在于每遍历一次都需要创建一个迭代器,无状态的迭代器则不需要。

function ipairs0(table, i)   -->无状态的迭代器
    i = i + 1
    local v = table[i]
    if not v then return nil end
    return i, v
end

function ipairs(table)
    return ipairs0, table, 0
end

table = {"a", "b", "c", "d", "e"}
for i, v in ipairs(table) do
    print(i, v)
end

在这个例子中,ipairs0, table, i 分别对应f, s, var。在上下文中,i既是迭代变量,也是table数组的当前索引。

模块与包(Module and Package)

从用户的观点来看,一个模块就是一个程序库,可以通过require来加载。然后便得到一个全局变量,表示一个table。这个table就像一个名称空间,其内容就是模块中导出的所有东西,如函数和常量。

加载一个模块

require “mod”
mod.foo()

编写一个模块

把模块中要导出的内容放在一个table中,用模块名作为全局变量引用这个table,模块的最后返回这个table。

modulename = …       -->模块名由require函数传入
local moduletable = {}   -->模块table,存放要导出的内容
_G[modulename] = moduletable    -->用模块名作为全局变量引用模块table

-- 定义模块中要导出的函数和常量

return moduletable     -->相当于package.loaded[modulename] = moduletable

如果一个模块被加载过,require函数会返回已经加载的模块。如果模块需要热更新,可以这样实现:

package.loaded[modulename] = nil
require “modulename”

当然在实际的代码中,还有更多的问题需要考虑,比如闭包中的upvalue的更新。

面向对象

一个对象要有变量和方法,Lua可以用table来表示对象。

Girl = {age = 18}
function Girl.grow(self, v)
    self.age = self.age + v
end

Girl.grow(Girl, 1)
print(Girl.age)

Girl是一个对象,age是Girl的变量,grow是Girl的方法。grow方法第一个参数为self,示例中Girl.grow(Girl, 1)调用grow方法时,传入了Girl。这个调用存在另外一种语法糖:

Girl:grow(1)

grow方法可以这样来定义:

function Girl:grow(v)
    self.age = self.age + v
end

在Java、C#这样的面向对象语言中,有类和对象的区分。类是对象的抽象,对象通过类来创建,比如 Girl g = new Girl()。Lua中没有类的概念,如果需要表达类,可以采用原型语言的做法。对象a拥有一个原型b,对象a会在原型b上查找不存在的操作,原型b也是一个普通对象。

function Girl:new(o)
    o = o or {}
    setmetatable(o, self)
    self.__index = self
    return o
end

function Girl:grow(v)
    self.age = self.age + v
end

g = Girl:new({age = 18})
g:grow(1)
print(g.age)

利用元表,Lua也可以表达出继承,多态的概念。不过我觉得Lua拥有更灵活的语法,没必要像面向对象那样死板的编程。比如如果有girl1和girl2两个对象,它们的grow行为不同,那么在面向对象中需要创建Girl1和Girl2两个类,而在Lua中只需要简单的修改girl2的grow方法:

local oldGrow = g.grow
function g:grow(v)
    oldGrow(g, v)
    print("do other things...")
end

g:grow(1)
print(g.age)

参考资料

《Lua程序设计》第二版

Lua 5.3参考手册

Lua 5.3云风翻译版

时间: 2024-10-12 14:56:14

Lua总结一的相关文章

cocos2dx lua中异步加载网络图片,可用于显示微信头像

最近在做一个棋牌项目,脚本语言用的lua,登录需要使用微信登录,用户头像用微信账户的头像,微信接口返回的头像是一个url,那么遇到的一个问题就是如何在lua中异步加载这个头像,先在引擎源码里找了下可能会提供这个功能的地方,发现好像没有提供类似功能,那么只能自己动手写.所以我在ImageView这个类里面添加了一个成员方法,其实可以不写在ImageView里,而且我觉得非必需情况下还是不要修改引擎源码的好,因为如果源码改动比较多的话,将来引擎版本升级会比较麻烦.我写在ImageView里纯粹是想偷

Lua 第一天

今天开始学习Lua语言,感觉Lua非常便捷.我用的编译器是SciTE,很不错. 举例一:无需引用,内置输出语句   print() print(6)   --> 6 print(type(6))  -->   number 举例二:对数字字符串进行数字化处理 print("2"+3 )  -->5 举例三:..连接俩个字符串,#返回字符串长度 举例四:print()输出函数,可以有多个输出值 a = 1,b =2,c = a+b    print(a,b,c,a,b,

lua协程一则报错解决“attempt to yield across metamethod/C-call boundary”

问题 attempt to yield across metamethod/C-call boundary 需求跟如下帖子中描述一致: http://bbs.chinaunix.net/forum.php?mod=viewthread&action=printable&tid=4065715 模拟一个场景,在C中创建出coroutine来执行Lua脚本,并且提供C API给Lua使用,当某些操作可能会阻塞时(如网络I/O),C函数中执行yield将协程切换出去,然后未来的某个时刻,如果条件

lua闭合函数

function count( ... ) local i = 0 return function( ... ) i = i+ 1 return i end end local func = count(...) print(func()) print(func()) print(func()) 结果如下: 1 2 3 [Finished in 0.1s] lua 闭合函数:一个函数加上该函数所需访问的所有“非局部变量”. 如上所示:count()函数返回了另一个函数,而这个函数使用了count

Mac下Lua Sublime Text2 开发环境搭建

1.安装Lua编译器 下载Lua, http://www.lua.org/: 解压后,cd进入该文件夹src目录下 在当前文件夹执行make macosx   然后回车 cd 到上一目录,执行sudo make install 完成之后执行lua -v 可以看到:Lua 5.3.3  Copyright (C) 1994-2016 Lua.org, PUC-Rio 2.安装Sublime Text2 1.下载http://sublime-text-2.cn.uptodown.com后打开: {

Nginx+Lua 积累

Nginx+Lua 积累 1.解析16进制编码的中文参数 复制代码 local encodeStr = "%E6%B0%94" local decodeStr = ""; for i = 2, #encodeStr - 1, 3 do local num = encodeStr:sub(i, i + 1); num = tonumber(num, 16); decodeStr = decodeStr .. string.char(num); end ngx.say(

nginx与Lua执行顺序

Nginx顺序 Nginx 处理每一个用户请求时,都是按照若干个不同阶段(phase)依次处理的,而不是根据配置文件上的顺序. Nginx 处理请求的过程一共划分为 11 个阶段,按照执行顺序依次是 post-read.server-rewrite.find-config.rewrite.post-rewrite. preaccess.access.post-access.try-files.content.log. post-read: 读取请求内容阶段 Nginx读取并解析完请求头之后就立即

在 Lua 里 使用 Cocos Studio 导出的 .csb 文件

1. 加载 节点到场景 第一种方法 local scene = cc.CSLoader:createNode("scene.csb") self:addChild(scene) 第二种方法 local scene = cc.uiloader:load("MainScene.csb"):addTo(self) 2.强转精灵类型 local sprite = tolua.cast(object,"cc.Sprite") CocoStudio 做的里面

深入lua栈交互—cpp调用lua数据

lua是通过lua_state这个栈来和c 交互的 1.....lua栈 index 下往上增长 如: 1 2 3 4 5 6 2.....lua栈 index 是循环的 如下 index 上到下 是 3 2 1 0 -1 -2 -3 ,栈对应的值为     1     2     3     x     1     2     3      3......lua函数多个返回值如果上面是function返回了3个返回值,那么return a ,b,c  中 a=3 b=2 c=1 第一个返回值先

[nginx] 由Lua 粘合的Nginx生态环境-- agentzh tech-club.org

[nginx] 由Lua 粘合的Nginx生态环境-- agentzh tech-club.org 演讲听录 [复制链接] kindle LT管理团队 Rank: 9Rank: 9Rank: 9 未绑定新浪微博 签到222 注册时间1970-1-1最后登录2015-6-5在线时间168 小时阅读权限200积分19025帖子119主题35精华2UID9223 LT总司令 LT元老 LT教授 串个门加好友打招呼发消息 电梯直达跳转到指定楼层 1# 发表于 2013-1-12 12:43:47 |只看