Lua笔记

  1. 书:Programming in lua,2th edition

  2. 参考手册:http://www.lua.org/manual/5.1/

  3. IDEA:https://blog.csdn.net/zjz520yy/article/details/79919267


lua大小写敏感

lua源码是C实现的


自己编译lua源文件,包含链接库,解释器以及编译器。

library: lapi.c lcode.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c
lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c
ltable.c ltm.c lundump.c lvm.c lzio.c
lauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c loslib.c
ltablib.c lstrlib.c loadlib.c linit.c

interpreter: library, lua.c

compiler: library, luac.c print.c


lua注释

-- 一行注释
--[[multiline
multiline
]]

注释小技巧

--[[
xxx
--]]

取消注释前面加一个-即可
---[[
xxx
--]]

还可以在[[以及]]之间加入任意数量的=,用于某些内容也包含[]符号的情况
--[==[
xxx
]==]

用lua解释器

lua -i xx.lua -- 在执行一个lua文件后开始交互模式
lua xx.lua-- 执行一个lua文件后结束
lua xx.out -- 执行一个编译后的lua文件
lua -- 进入可交互模式
lua -e "print(math.sin(12))" -- 直接执行代码
lua -i -l lib -e "x = 10" -- 加载lib库后,运行"x=10",然后进入可交互模式


交互模式的每一行都是一个程序块,执行完就结束了,当用全局变量的时候,一点问题都没,当用了局部变量,下一行再访问它,就是访问同名的全局变量了。


在UNIX输入CTRL+D,在windows输入CTRL_Z或者输入os.exit()可以退出交互模式

用lua编译器

luac xx.lua

lua函数

dofile("xx.lua") -- 执行lua文件,这个函数做了两件事,先loadfile得到一个函数,再调用它。
loadfile("xx.lua") -- 编译lua文件,不运行,返回一个函数,如果发生错误则返回nil和错误信息。(更灵活)
loadstring("xxx") -- 与loadfile差不多,只是loadfile是从文件读取代码,loadstring是从字符串读取
load(xxx) -- 最底层的load函数
loadxxx函数把程序块当作一个匿名函数,function(...) 程序块 end

lua常量

_VERSION -- lua版本
_PROMPT -- 可以修改lua交互模式下的提示符


全局变量,局部变量

a -- global
local a -- local

类型

nil, Boolean, number, string, userdata, function, thread, and table
type(var) -- 打印变量类型的字符串描述

and or not

短路求值

a and b, 结果是a,如果a是false,反之是b
a or b,结果是a,如果a是true,反之是b
x=x or v -- 常见写法
x = a and b or c -- 常见写法
not not x -- 一定返回boolean

POSIX系统(如Unix)直接执行lua文件

在lua文件开头加上#!/usr/local/bin/lua或者#!/usr/bin/env lua,然后给lua文件加执行权限,就可以
./xx.lua直接执行lua文件,解释器会忽略开头以#开始的行。相当于lua xx.lua

全局变量arg

在命令行模式中lua -e "sin=math.sin" script a b
arg[-3] = "lua"
arg[-2] = "-e"
arg[-1] = "sin=math.sin"
arg[0] = "script"
arg[1] = "a"
arg[2] = "b"

string

'#'长度操作符,取字符串长度。按byte取,如果是其它编码,如中文,要自定义一个取长度接口。
‘#’长度操作符还能取数组table的元素个数。

长字符串
在[[...]]之间的内容不转义

page = [[
<html>
<head>
<title>An HTML Page</title>
</head>
<body>
<a href="http://www.lua.org">Lua</a>
</body>
</html>
]]
这个方括号间也可以加入任意数量的=,但是前后=数量要匹配,用于一些内容也包含[]这两个符号的情况,如
[==[
xxx
]==]

%取模操作符

a % b = a - floor(a/b)*b


table删除某个字段可以把那个字段设nil,如w.x=nil


lua多重赋值,如x,y=y,x 这样x,y值就互换了。用于接收函数返回的多个值,或者就像例子那样交换两个变量的值。


用do...end把代码包起来就形成一个块,类似其它的语言的{}。在交互模式可以使用阻止局部变量被释放。
尽量使用局部变量阻止扰乱全局环境。


控制结构:if...else,while,for,repeat...until(重复执行知道until条件为真)


break,return只能写在程序块的最后一个语句上。想在其它地方return,比如语句块的中间,可以用do return end,或者if true then return end


o.foo(o,x) <==> o:foo(x)
如果函数返回多个值,用table构造式{foo()},会把函数返回值作为table的初始元素。比如foo返回"a","b",table的元素为{"a","b"},把函数调用放在一个()内,可以迫使它只返回一个值,如(foo())就只返回"a"。


变长参数...,可以赋值给变量,如local a, b = ...,相当于用变长参数列表的第一个以及第二个参数赋值给a,b,如果没有则是nil。也可以用{...}初始化一个table。...有点像返回多个值的函数。

当变长参数里夹杂着nil时,可以用select函数来获取变长参数,用法如下


函数定义就是一个赋值语句,函数名是一个变量,持有了函数。



lua有全局变量,局部变量,非局部变量(就是upvalue)

访问速度是局部变量最快,非局部变量次之,全局变量最慢。

lua函数的本质是闭包,闭包是由一个函数和它所需要用到的非局部变量组成,就是一个函数里面包含了另一个函数,里面的那个函数用到了外部函数的局部变量(upvalue)。如下图:

lua尾调用

在函数末尾调用了另一个函数后就没其它事情了,像如下形式:
return <func>(<args>)
类似goto,可用来实现状态机
好处在于就没有堆栈信息了,就不会出现栈溢出。如下所示:




用闭包实现迭代器

需要一个迭代器工厂函数,返回一个迭代器函数,每次调用它都会保留调用完的状态,直到迭代器返回nil,迭代就结束。




泛型for的语义

无状态的迭代器(不用闭包,闭包是有开销的),尽量用这种迭代器

package.loadlib(path,functionname)

require调用这个函数来加载一个库,并把库中的函数注册到lua


pcall(protected call),xpcall,debug.debug,debug.traceback


error (message [, level])

Terminates the last protected function called and returns message as the error message. Function error never returns.

Usually, error adds some information about the error position at the beginning of the message. The level argument specifies how to get the error position. With level 1 (the default), the error position is where the error function was called. Level 2 points the error to where the function that called error was called; and so on. Passing a level 0 avoids the addition of error position information to the message.


coroutine(协程)

有4个状态:挂起,运行,死亡,正常。
刚创建的时候是挂起状态。



当协程A唤醒了协程B,协程B是运行状态,协程A的状态变为正常。
resume和yield可以交换数据。


第一次唤醒协程的时候,通过resume的额外参数传给协程的主函数的形参,然后把yield圆括号内的参数返回给resume,会返回类似true,yield参数1,yield参数2...,然后协程的状态变为挂起。再次resume,这回额外参数传进去,会变为yield函数返回的值。如下图所示:


coroutine.wrap(f)这个函数会返回一个函数,每次调用这个返回的函数,就会唤醒一次由f构造的协程,与resume的区别是不会返回错误代码


协程是非抢先式的多线程,即当一个线程阻塞后,整个程序都会卡住,多线程只是会充分利用时间,而不是同时运行,就是当一个线程卡住了就换另一个线程跑,直到全部跑完


foo("xxx") <====> foo "xxx"
foo({}) <====> foo {}
这样的语法糖可以用来实现一些东西,感觉像数据,其实是函数调用。


table数值如果index不是从1开始,或者中间有nil,或者不连续,很多内置函数和#操作符的结果就会有问题


pairs只遍历非nil元素


string.format("%q", a) -- 用双引号括住字符串a,并对字符串a中的双引号等其它特殊字符自动转义。


当用一些保留字做table的key时,不能写成{if=xxx},而应该写成{["if"]=xxx}


metatable

getmetatable(table)
setmetatable(t, meta)

所有字符串拥有同一个元表(不可改),只能设置修改table的元表,其它类型没有元表,要修改只能改lua的C源码。在元表中设置相应的字段和元方法,就可以在具体操作的时候调用相应的元方法

算术类的元方法:

__add (加+),__sub (减-), __mul (乘*), __div(除/),__unm(相反数-),__mod(取模%),__pow(幂^),__concat(连接..)

在算术表达式中,会先查找第一个操作数的元表是否有对应运算符的元方法,有的话就执行,没有就找第二个操作数,如果都没有就报错。

关系类的元方法:

__eq(==),__lt(<),__le(<=)

库定义的元方法

__tostring(tostring(x)) -- print(a),会调用a的tostring实现。
__metatable -- 当设置了这个字段,getmetatable就会返回这个字段的值,setmetatable就会报错

table访问的元方法

__index(table[index]时触发)-- 这个字段的值可以是一个函数function(t,key),也可以是一个table(对这个table继续执行t[index]),如果不想触发__index,可以用rawget(t,i),就是原始的访问。
__newindex (table[index]=xx,如果index原先不存在,就会触发) -- 可以是函数function(t,key,value),也可以是一个table(对这个table进行赋值),如果不想触发就用rawset(t,k,v)

弱引用的元方法

__mode -- 'k'表示key是弱引用,'v'表示value是弱引用, 'kv'完全弱引用



所有的全局变量存放在表_G里面


setfenv(f,t)或者setfenv(level,t)

设置函数环境,设置的是全局环境


require(modulerName) <=> require "modulerName"

所有的模块都存在一个包里,即package.loaded,用名字索引,require只是返回了package.loaded[name]
如果在package.preload中没找到对应的模块加载器,就会使用通用的规则查找lua文件(loadfile)或者C程序库(loadlib)。loadfile,loadlib只是加载了代码,还需要以模块名作为参数调用这些代码。
多次require同一个模块是不会有问题的,因为有缓存,如果想强行再次加载一个模块,可以先把package.loaded["modulerName"] = nil再require。



require函数会用传进入的参数替换每个路径里面的?,遍历每个路径,直到找到。路径存放在package.path(这个是lua文件的路径)里面(以;分隔)。在启动lua时,会用LUA_PATH环境变量初始化package.path,lua会把其中的字串";;"替换成默认路径。
如果找不到lua文件,就会找C程序库,路径存放在package.cpath,相应用LUA_CPATH初始化。文件一般以so或者dll为扩展名。


一个模块的写法

local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M
M.Constant = 10
function M.func() {}

当加载一个模块的时候,实际上就是把模块代码包在function(...) end里面,require函数的参数就会传给这个函数的...,如果要清理一个模块,要写_G[modname] = nil以及package.loaded[modname]=nil。如果我们不把模块赋值给全局变量,我们就不能直接用全局变量使用这个模块,只能通过返回的table来使用,但是想释放,还说要写package.loaded[modname]=nil


另一种模块的写法(更正规)

-- 模块设置
local modname = ...
local M = {}
_G[modname] = M
package.loaded[modname] = M

-- 声明这个模块从外界需要的所有东西
local sqrt = math.sqrt
local io = io

setfenv(1, M)

function funct() end  -- 这个会自动赋值到M这个table里,访问变量也是从M获取

module函数包含了上述更正规模块写法。

-- 包含了上述模块设置,以及设置环境代码
module(..., package.seeall)

-- 模块的其它定义
function funct()  end

子模块

require("mod.sub"),在查找文件时会把‘.’替换为目录分隔符,如在windows中替换为‘/’,这样就可以把模块子模块按目录结构存放了。
在查找C程序库时,其初始化函数会被命名为luaopen_mod_sub。如果模块名是a-b,初始化函数名是luaopen_b(省略连字符之前的字符)




面向对象编程

利用a.func(a,x) <=>a:func(x) 以及元表元方法__index,就可以实现类表以及从类表调用new返回对应的对象。

function Class:func(x)
        -- 这里隐藏了一个参数self,即Object:func(x)时,self就是Object这个table
end

基类,继承基类的类还有对象

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

function Account:deposit(v)
     self.balance = self.balance + v
end

SpecialAccount = Account:new()
s = SpecialAccount:new()

多重继承,__index不是table而是一个函数

local function search(k, plist)
    for i = 1, #plist do
        local v = plist[i][k]
        if v then return v end
    end
end

function createClass(...)
      local c = {}
      local parents = {...}

      setmetatable(c, {__index = function(t, k)
          local v = search(k, parents) 
          -- t[k] = v
          return v
      end})
      c.__index = c
      function c:new(o)
          o = o or {}
          setmetatable(o,c)
          return o
      end
      return c
end

私密性写法,通过闭包实现,把不让访问的东西放在非全局变量里,返回开放的东西,如果只有一个接口,直接返回函数就好了,就是我们先前说的闭包。

function newAccount(initialBalance)
    local self = {balance = initialBalance}
    local withdraw = function (v)  self.balance = self.balance - v  end
    return {withdraw = withdraw}
end

lua的全局变量要设为nil才会被清理,table里面不需要的值也需要设nil才会被清理。


弱引用table,用元表的元方法__mode实现
有三种模式:弱引用key(当key被回收,整个条目都会从table删除),弱引用value(当value被回收,整个条目都会从table删除),同时弱引用key和value(当key或value被回收,整个条目都会从table删除)。
只有对象会被回收。数字和boolean这样的值不会被回收,如果value被设nil了才会被回收


调试库

debug.getinfo等等

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,729评论 0 38
  • 大部分人搞lua可能是为了开发游戏,我个人倒是没有从事游戏开发,所以用lua仅仅是搞了搞魔兽的插件开发。这篇主要也...
    老胡聊聊天阅读 5,981评论 0 2
  • 写在前面,有一点乱。 8种数据 nil, boolean, number, string, function, u...
    黒可乐阅读 563评论 0 0
  • 今天做了一个清醒梦,能感受到我所有的情绪,仿佛亲临现场般的感觉。以前也经常做梦,不过能够清醒记得的并不多,人们常说...
    蓝色孤枫阅读 445评论 0 0
  • 你知道吗 这个秋天的童话快要结束了 我们踩着落叶相逢归期无期 我独自跑过操场 到图书馆学习 风很大的时候觉得一个人...
    追光者阿阅读 374评论 1 1