记录Programming In Lua

简介

Lua特有的特征:

  1. 可扩展性。Lua 的扩展性非常卓越,以至于很多人把 Lua 用作搭建领域语言的 工具(注:比如游戏脚本)。Lua 被设计为易于扩展的,可以通过 Lua 代码或者 C 代码扩展,Lua 的很多功能都是通过外部库来扩展的。Lua 很容易与 C/C++、java、 fortran、Smalltalk、Ada,以及其他语言接口。
  2. 简单。Lua 本身简单,小巧;内容少但功能强大,这使得 Lua 易于学习,很容 易实现一些小的应用。他的完全发布版(代码、手册以及某些平台的二进制文件)仅用一张软盘就可以装得下。
  3. 高效率。Lua 有很高的执行效率,统计表明 Lua 是目前平均效率高的脚本语 言。
  4. 与平台无关。Lua 几乎可以运行在所有我们听说过的系统上,如 NextStep、 OS/2、PlayStation II (Sony)、Mac OS-9、OS X、BeOS、MS-DOS、IBM mainframes、EPOC、PalmOS、MCF5206eLITE Evaluation Board、RISC OS,及所有的 Windows 和 Unix。Lua 不是通过使用条件编译实现平台无关,而 是完全使用 ANSI (ISO) C,这意味着只要你有 ANSI C 编译器你就可以编译并 使用 Lua。

loadstring 和 load 函数

5.2以下

    local f = assert(loadstring("print('Hello World!')"))
    f()

5.2后废除loadstring,改为load

    local f = assert(load("print('Hello World!')"))
    f()

require 函数

Lua 提供高级的 require 函数来加载运行库。粗略的说 require 和 dofile 完成同样的功 能但有两点不同:

  1. require 会搜索目录加载文件
  2. require 会判断是否文件已经加载避免重复加载同一文件

Lua 保留一张所有已经加载 的文件的列表(使用 table 保存),可以通过全局变量_LOADED 访问文件名列表

模拟Continue

list = {1, 2, 3, 4, 5}
for i, v in ipairs(list) do
    repeat
        if i == 3 then
            break
        end
        -- todo
        print(i)
    until true
end

输出:1 2 4 5

goto语句

i = 0
::start::
print(i)
i = i + 1
if i < 5 then
    goto start
end

输出:0 1 2 3 4

协同程序

协同程序(coroutine)与多线程情况下的线程比较类似:有自己的堆栈,自己的局 部变量,有自己的指令指针(IP,instruction pointer),但与其它协同程序共享全局变量 等很多信息。线程和协同程序的主要不同在于:在多处理器情况下,从概念上来讲多线 程程序同时运行多个线程;而协同程序是通过协作来完成,在任一指定时刻只有一个协 同程序在运行,并且这个正在运行的协同程序只在必要时才会被挂起。

co = coroutine.create(function ()  
    print("hi") 
end) 
 
print(co)  --> thread: 0x8071d98 

coroutine有三种状态:

  • suspended:挂起
  • running:运行中
  • dead:停止
    查看状态:
coroutine.status(co)

执行coroutine:

coroutine.resume(co)

基本代码:

co = coroutine.create(function(count)
        for i = 1, count do
            print(i)
            coroutine.yield(i)   --> 返回i
        end
    end)
print(co)
print(coroutine.status(co))
print("resume:", coroutine.resume(co, 10))    --> 传入参数10,返回第一个参数bool(是否执行成功),后续参数为yield返回值
print(coroutine.status(co))
print("resume:", coroutine.resume(co))

字符串连接

strs = {}
for i = 1, 10 do
    table.insert(strs, i)
end
print(table.concat(strs))           --> 12345678910
print(table.concat(strs, "\t"))    --> 1   2   3   4   5   6   7   8   9   10

序列化

function serialize(o, i)
    if i == nil then
        io.write(tostring(o), " = ")
    end
    i =  i or 1
    if type(o) == "number" then
        io.write(o)
    elseif type(o) == "string" then
        io.write(string.format("%q", o))
    elseif type(o) == "table" then
        io.write("{\n")
        for k, v in pairs(o) do
            for c=1, i do
                io.write("    ")
            end
            io.write("[")
            serialize(k)
            io.write("] = ")
            serialize(v, i + 1)
            io.write(",\n")
        end
        for c=1, i - 1 do
            io.write("    ")
        end
        io.write("}")
    end
end

Metatables and Metamethods

基础代码,外加__tostring
Set = {}

Set.mt = {}

function Set.new(t)
    t = t or {}
    local set = {}
    setmetatable(set, Set.mt)
    for _, l in ipairs(t) do
        set[l] = true
    end
    return set
end

function Set.tostring(set)
    local s = "{"
    local sep = ""
    for e in pairs(set) do
        s = s .. sep .. e
        sep = ", "
    end
    return s .. "}"
end

function Set.print(s)
    print(Set.tostring(s))
end

s1 = Set.new({10, 20, 30, 50})
s2 = Set.new({30, 10})
print(getmetatable(s1))
print(getmetatable(s2))
-- 可以发现,s1 跟 s2 的metatable 指向的引用是相同的

Set.mt.__tostring = Set.tostring
print(s1)
关于__add
-- 相加
function Set.union(a, b)
    local res = Set.new()
    for k in pairs(a) do
        res[k] = true
    end
    for k in pairs(b) do
        res[k] = true
    end
    return res
end

Set.mt.__add = Set.union
Set.print(s1 + s2)
关于__mul
-- 相乘(并集)
function Set.intersection(a, b)
    local res = Set.new()
    for k in pairs(a) do
        res[k] = b[k]
    end
    return res
end

Set.mt.__mul = Set.intersection
Set.print(s1 * s2)
关于__le(小于或等于), __lt(小于),__eq(等于)
-- 小于等于
function Set.le(a, b)
    for k in pairs(a) do
        if not b[k] then
            return false
        end
    end
    return true
end

-- 小于
function Set.lt(a, b)
    return a <= b and not (b <= a)
end

-- 等于
function Set.eq(a, b)
    return a <= b and b <= a
end

Set.mt.__lt = Set.lt
Set.mt.__le = Set.le
Set.mt.__eq = Set.eq
print(s1 <= s2)
print(s1 < s2)
print(s1 >= s2)
print(s1 > s2)
print(s1 * s2 == s2)
关于__metatable

setmetatable/getmetatable 函数也会使用 metafield,在这种情况下,可以保护metatables。假定你想保护你的集合使其使用者既看不到也不能修改 metatables。如果你 对 metatable 设置了__metatable 的值,getmetatable 将返回这个域的值,而调用 setmetatable 将会出错

Set.mt.__metatable = "not your business"
print(getmetatable(s1))          --> not your business 
setmetatable(s1, {})               --> error
关于__index

当我们访问一个表的不存在的域,返回结果为 nil,这是正确的,但并不 一定正确。实际上,这种访问触发 lua 解释器去查找__index metamethod:如果不存在, 返回结果为 nil;如果存在则由__index metamethod 返回结果。

Window = {}
Window.prototype = {x = 0, y = 0, width = 100, height = 100}
Window.mt = {}

function Window.new(o)
    setmetatable(o, Window.mt)
    return o
end
-- 可以是一个函数
Window.mt.__index = function(table, key)
    return Window.prototype[key]
end

w = Window.new({x = 10, y = 10})
print("x:", w.x, "y:", w.y, "width:", w.width, "height:", w.height)
--[[ 当 Lua 发现 w 不存在域 width 时,但是有一个 metatable 带有__index 域,
Lua 使用 w(the table)和 width(缺少的值)来调用__index metamethod,
metamethod 则通过访问 原型表(prototype)获取缺少的域的结果。 
--]]

-- __index metamethod 不需要非是一个函数,他也可以是一个表
Window.mt.__index = Window.prototype

w = Window.new({x = 20, y = 20})
print("x:", w.x, "y:", w.y, "width:", w.width, "height:", w.height)
关于__newindex

__newindex metamethod 用来对表更新,__index 则用来对表访问。
下面是一个把table修改成只读的方法:

function readOnly(t)
    local p = {}
    local mt = {
        __index = t,
        __newindex = function(t, k, v)
            error("attempt to update a read-only table", 2)
            -- 二个参数 2,将错误信息返回给企图执行的地方(报错指向18行而不是8)
        end
    }
    setmetatable(p, mt)
    return p
end

t = readOnly({1, 2, 3})
print(t[1])
t[2] = 4        --> attempt to update a read-only table

关于全局 _G

Lua 用一个名为 environment 普通的表来保存所有的全局变量。(更精确的说,Lua 在一系列的environment 中保存他的“global”变量,但是我们有时候可以忽略这种多样 性)这种结果的优点之一是他简化了 Lua 的内部实现,因为对于所有的全局变量没有必 要非要有不同的数据结构。另一个(主要的)优点是我们可以像其他表一样操作这个保存 全局变量的表。为了简化操作,Lua 将环境本身存储在一个全局变量_G 中,(_G._G 等 于_G)。
下面可以遍历打印出_G包含了那些变量:

for n in pairs(_G) do
    print(n)
end

防止其他人把一些局部变量写成全局,可以用到__index,__newindex:

setmetatable(_G, {
    __index = function(_, k)
        error("attempt to write to undeclared variable "..k, 2)
    end,
    __newindex = function(_, k, v)
        error("attempt to read undeclared variable "..k.." = "..v, 2)
    end
})

a = 10     --> attempt to read undeclared variable a = 10
print(a)   --> attempt to write to undeclared variable a

但是我们如何声明一个新的变量呢?使用 rawset,可以绕过 metamethod:

function declare (name, initval)
    rawset(_G, name, initval or false)
end

但是现在,为了测试一个变量是否存在,我们不能简单的比较他是否为 nil。如果他 是 nil 访问将抛出错误。所以,我们使用 rawget 绕过 metamethod:

if rawget(_G, var) == nil then
    -- 'var' is undeclared
    ... 
end

setfenv

当我们在全局环境中定义变量时经常会有命名冲突,尤其是在使用一些库的时候,变量声明可能会发生覆盖,这时候就需要一个非全局的环境来解决这问题。setfenv函数可以满足我们的需求。
setfenv(f, table):设置一个函数的环境

  • 当第一个参数为一个函数时,表示设置该函数的环境
  • 当第一个参数为一个数字时,1代表当前函数,2代表调用自己的函数,3代表调用自己的函数的函数,以此类推
local P = {}
setfenv(1, P)

print("abc")    --> attempt to call global `print' (a nil value)

这时候可以使用setmetatable来继承_G:

local P = {}
setmetatable(P, {__index = _G})
setfenv(1, P)

print("abc")    --> abc

PA = {
    desc = "PA"
}

function PA.new(o)
    o = o or {}
    setmetatable(o, {__index = PA})
    return o
end

function PA:Print()
    print(self.desc)
end

a = PA.new()
a.Print(a)
a:Print()

随机数

math.randomseed(os.time())          -- 设置随机种子
print(math.random())                -- [0, 1)
print(math.random(4))               -- [1, 4]
print(math.random(2,10))          -- [2, 10]

正则匹配

. 任意字符
%a 字母
%c 控制字符
%d 数字
%l 小写字母
%p 标点字符
%s 空白符
%u 大写字母
%w 字母和数字
%x 十六进制数字
%z 代表 0 的字符

+ 匹配前一字符 1 次或多次
* 匹配前一字符 0 次或多次
- 匹配前一字符 0 次或多次
? 匹配前一字符 0 次或 1 次

  • string.find
date = "17/7/1990"
_, _, d, m, y = string.find(date, "(%d+)/(%d+)/(%d+)")
print(d, m, y)  --> 17  7  1990
  • string.gsub
print(string.gsub("hello Lua!", "(%a)", "%1-%1")) 
--> h-he-el-ll-lo-o L-Lu-ua-a!

print(string.gsub("hello Lua", "(.)(.)", "%2%1")) 
-->  ehll ouLa

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

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,714评论 0 38
  • 第一篇 语言 第0章 序言 Lua仅让你用少量的代码解决关键问题。 Lua所提供的机制是C不擅长的:高级语言,动态...
    testfor阅读 2,642评论 1 7
  • 指令集 lua_capture_error_log lua_use_default_type lua_malloc...
    吃瓜的东阅读 11,963评论 0 2
  • 一个人的情结是内力,哲学是心法。情结强哲学弱则容易走火入魔,情结弱哲学强则是花拳绣腿。
    深夜煮屎阅读 178评论 0 0
  • 时间成本,就是这个时间如果用于做别的事情,你可以获得的收益。它是一种特殊形式的机会成本。 懂得计算时间成本,可以帮...
    丫丫足迹阅读 103评论 0 0