美文网首页
记录Programming In Lua

记录Programming In Lua

作者: _armour | 来源:发表于2019-06-20 21:16 被阅读0次

简介

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

相关文章

  • 记录Programming In Lua

    简介 Lua特有的特征: 可扩展性。Lua 的扩展性非常卓越,以至于很多人把 Lua 用作搭建领域语言的 工具(注...

  • 在spring-data-redis中使用lua脚本

    参考 Lua 脚本Programming in Lua api 使用 RedisScript 使用 Redi...

  • Lua笔记

    书:Programming in lua,2th edition 参考手册:http://www.lua.org/...

  • #Lua#The Stack

    序 这几天重看 Programming in Lua,介绍了 The Stack的概念,中文有很多翻译:Lua堆栈...

  • lua虚变量 _

    《Programming in Lua》中有一段使用虚变量的代码

  • Programming in Lua 3rd

    在亚马逊上花了我250RMB,花钱的书,更有动力看,这样的心态下,我竟然看了三遍,收获良多,值得。

  • Programming in Lua 4th Edition

    5.3 加入 64-bit integer, 以前只有 double-precision floating;sma...

  • lua基本函数说明

    lua函数说明 部分lua函数在lua的dash文档中没找到或者没有使用例子,特记录,备忘; getfenv(f)...

  • 无标题文章te

    本文所有内容全部都是我在阅读了《Programming in Lua》这本书之后整理出来的,可以理解本文为一个快速...

  • REDIS----配置文件----LUA SCRIPTING

    LUA SCRIPTING部分: Lua脚本的最长执行时间(毫秒)。如果达到最大执行时间,Redis将记录脚本在允...

网友评论

      本文标题:记录Programming In Lua

      本文链接:https://www.haomeiwen.com/subject/vmujqctx.html