lua工具库penlight--01简介

   2023-02-09 学习力0
核心提示:lua的设计目标是嵌入式语言,所以和其它动态语言(如python、ruby)相比其自带的库缺少很多实用功能。好在有lua社区有Penlight,为lua提供了许多强大的功能,接下来的几篇博客,我会简译Penlight的wiki。 目的 常有人说lua不带电池。因为lua的目标是可以运

lua的设计目标是嵌入式语言,所以和其它动态语言(如python、ruby)相比其自带的库缺少很多实用功能。

好在有lua社区有Penlight,为lua提供了许多强大的功能,接下来的几篇博客,我会简译Penlight的wiki。

 

目的

 常有人说lua不带电池。因为lua的目标是可以运行在各种机器上的简洁语言,(有些机器甚至不

支持布尔系统)。lua类似于操作系统内核(注:即不是完整的系统,只有基本功能),lua的作者

并没有把围绕lua开发完整的生态系统看做自己的职责。这是社区的角色。

 软件设计的原则是识别通用模式,并复用。这不但让代码更易管理,而且更易阅读。

   Penlight使用了许多编码原则,这样你的代码变得更简洁。如在Lua里通常用{unpack(t)}复制

表,但这只对“小”表有用而且不健壮。因此Penlight提供了tablex.deepcopy(),它可以用来复制

嵌套的表和相关联的元表,所以可以用它复制复杂对象。

 lua默认采用这样的错误处理:如果参数类型错误,抛出错误;如果有问题,返回nil,message。

也有些例外的函数,如input.fields默认是返回message并立即关闭程序(可以改变默认方式)。

对脚本而言,与提供堆栈跟踪相比,这是更合适的方式。词法函数为了简化编码,总是返回错误,

必须用pcall封装。

  如果你习惯Python的约定,注意lua里的索引从1开始。

  lua不建议用table.foreach,而是泛型for,但是使用Penlight提供的高阶函数,它在某些情况下

特别有用。注意tablex.foreach,颠倒了顺序,先传值,之后索引。虽然乖张,符合预期要更好。

  Penlight唯一外部依赖是 LuaFileSystem (lfs ),如果你想dir.copyfile在Windows上成功工作

,你需要 alien 或 LuaJIT 。(如果失败,会调用等效的shell命令)

 

注入还是不注入

  人们很早就意思到大的程序里需要保证命名清晰的方法,如tables (Lua), namespaces (C++)

或modules (Python)。在大公司里很容易遇到命名冲突,在一些简单的语言,如Lua,并没有提

供像C++那样解决命名冲突的详细机制,因此更易遇到。在一小圈朋友里‘Bruce‘是唯一的,不

要加姓。这取决于你写十多行的脚本,还是上万行的程序。

 Penlight提供了正规和非正规两种方式,你可以随意使用。

 正规方式:

 local utils = require 'pl.utils'
 utils.printf("%s\n","hello, world!")

非正规方式:
 require 'pl'
 utils.printf("%s\n","That feels better")

 require ‘pl’ 让所有的Penlight模块可用,而不需要单独调用。

 正规方式在写模块时更好。

 Andrew Starks贡献了另一种方法,很好的在两种方法里取得了平衡。

  require'pl.import_into'返回一个函数,它有一个表参数,把Penlight库导入,如果参数为空,返回一个新的。 

local pl = require'pl.import_into'()

 pl表是一个 ‘lazy table’,即按需载入,因此可以使用pl.utils.printf,这样不会污染全局。

   如果你在lua5.2里使用 _ENV来定义模块,要用Penlight可以这么做:

 local _ENV,M = require 'pl.import_into' ()

 function answer ()
     -- all the Penlight modules are available!
     return pretty.write(utils.split '10 20  30', '')
 end

 return M

 

默认把Penlight放在_ENV里,可能会对模块造成意外效果(和 module(…,package.seeall)一样

。为了方便和安全,你需要向这个函数传入true,这样module M就和_ENV不一样,只包含导出的

函数。(注:对lua5.2不了解,这句话不解)

 你想把pl.stringx里的函数导入到标准库里,可以这么做:

 require 'pl'
 stringx.import()
或者,更快的:
require('pl.stringx').import()

更好的方法师把表导入到局部环境,这样可以让名称简介。
 > require 'pl'
 > utils.import(math) --把math里的函数直接导入到全局
 > = sin(1.2)
 0.93203908596723 

 

 当第一次require来导入时,utils.import 的参数也可以是字符串。在module里使用时,import会

把符号导入到module的上下文。

 对于动态语言,保持全局域简洁是很重要的。 pl.strict模块,引入了简单的强制措施:全局变量必须

声明。如下:

 > require 'pl.strict'
 > print(x)
 stdin:1: variable 'x' is not declared
 > x = nil
 > print(x)
 nil

strict模块符合Penlight的“用时加载”策略。
strict也禁止对全局变量赋值,除非在主程序里。如果你想绕过这个机制使用rawset和rawget。
如果你想让strict全局生效,在pl/init.lua最后加上require 'pl.strict',并在主程序里调用。
从1.1.0之后,strict提供了strict.module函数来创建(或修改)模块,以符合此机制。
如:
 -- mymod.lua
 local strict = require 'pl.strict'
 local M = strict.module (...)

 function M.answer ()
     return 42
 end

 return M
如果你不小心错打成 mymod.Answer(),你会得到运行时错误:“variable ‘Answer’ is not declared
in ‘mymod’”。
这对已有的模块也有效,也可以修改lua标准库。
 strict.make_all_strict(_G)
这样当你错打成 math.cosine 时会得到错误信息,而不是nil。


Penlight的函数参数是什么?
许多Penlight里的函数的参数是函数,如map,它把函数作用到table的每个元素上。
pl.operator 导出了lua里的运算符,且和Python里的名字一样。如 operator.gt表示">".
map函数传递任何参数到函数,因此可以简写ls:filter(function(x) return x > 0 end)
ls:filter(‘>’,0)
最后,pl.func支持Boost lambda(译注:C++里的)的占位符,如一个乘法的匿名函数可以
表示为;_1*_2.
可以直接用,在Penlight里的所有的函数参数都会通过 utils.function_arg ,pl.func在这个
函数中注册。所以你可以直接在标准函数里使用占位符。
 > _1 = func._1
 > = List{10,20,30}:map(_1+1)
 {11,21,31}
(译注:_1,_2表示第几个参数)
另外一个有用的是utils.string_lambda ,也会自动调用
 > = List{10,20,30}:map '|x| x + 1'
 {11,21,31}
(译注:即|x|=x+1)
少用循环的利弊
循环是一种更机器的方式,如:
 local res = {}
 for i = 1,#ls do
     res[i] = fun(ls[i])
 end
但是这可以更高效的写作,ls:map(fun),不但代码更少,而且更易理解。
通常人们认为,这么写效率低,更消耗内存。如ls1:map2(‘*’,ls2):reduce ‘+’
会产生多余的临时对象。但是效率是相对的。
写循环容易出错而且乏味。(译注:省略了许多议论,结论是利大于弊)

通用函数
 utils.memoize用来存储需要多次调用且花销大的函数。它可以保存调用函数后的已有结果,
如果参数相同直接返回结果。
sum = utils.memoize(function(n)
     local sum = 0
     for i = 1,n do sum = sum + i end
     return sum
 end)
 ...
 s = sum(1e8) --takes time!
 ...
 s = sum(1e8) --returned saved value!
 应用支持
app.parse_args是一个简单的命令行参数解析器,支持gnu风格。
app.appfile,用来存储应用的配置。
prett模块让table更易读。(译注:省略了很多用法讲解,可以直接看api)
简单的面向对象
提供了面想对象的支持,
-- animal.lua

 class = require 'pl.class'

 class.Animal()

 function Animal:_init(name)
     self.name = name
 end

 function Animal:__tostring()
   return self.name..': '..self:speak()
 end

 class.Dog(Animal)

 function Dog:speak()
   return 'bark'
 end

 class.Cat(Animal)

 function Cat:_init(name,breed)
     self:super(name)  -- must init base!
     self.breed = breed
 end

 function Cat:speak()
   return 'meow'
 end

 class.Lion(Cat)

 function Lion:speak()
   return 'roar'
 end

 fido = Dog('Fido')
 felix = Cat('Felix','Tabby')
 leo = Lion('Leo','African')

 $ lua -i animal.lua
 > = fido,felix,leo
 Fido: bark      Felix: meow     Leo: roar
 > = leo:is_a(Animal)
 true
 > = leo:is_a(Dog)
 false
 > = leo:is_a(Cat)
 true

也可以这么写:
 local class = require 'pl.class'

 class.Named {
     _init = function(self,name)
         self.name = name
     end;

     __tostring = function(self)
         return 'boo '..self.name
     end;
 }

 b = Named 'dog'
 print(b)
 --> boo dog

Penlight提供了一些有用的类如:List,Set,Map,MultiMap,OrderdMap.
所有的类有catch函数,可以处理未预料的情况,如:
Strings:catch(function(self,name)
     return function() error("no such method "..name,2) end
 end)
上面只是简单的输出错误,但你可以创造一些好玩的,如;
 Strings:catch(List.default_map_with(string))

 ls = Strings{'one','two','three'}
 asserteq(ls:upper(),{'ONE','TWO','THREE'})
 asserteq(ls:sub(1,2),{'on','tw','th'})
(译注:list没有upper,sub,但是catch让没有的函数都执行string里函数)
cast类型转换,它不产生新对象,而是返回你传入的对象。
local sizes = ls:map '#'  --即取长度
 asserteq(sizes, {3,3,5})
 asserteq(utils.type(sizes),'Strings')
 asserteq(sizes:is_a(Strings),true)
 sizes = Vector:cast(sizes)
 asserteq(utils.type(sizes),'Vector')
 asserteq(sizes+1,{4,4,6})
(译注,ls即Strings{'one','two','three'})
utils.type可以返回类中_name属性的字符串。


属性模式,我们想控制用户对属性的访问,但不想让用户,使用obj:get_field()这么长
的代码访问。解决办法如下:
local MyProps = class(class.properties)
 local setted_a, got_b

 function MyProps:_init ()
     self._a = 1
     self._b = 2
 end

 function MyProps:set_a (v)
     setted_a = true
     self._a = v
 end

 function MyProps:get_b ()
     got_b = true
     return self._b
 end

 local mp = MyProps()

 mp.a = 10

 asserteq(mp.a,10)
 asserteq(mp.b,2)
 asserteq(setted_a and got_b, true)
 规则是内部的变量以"_"开头,当访问mp.a时先检查特定的getter(取值器),
get_a。同理setter(设定器)也被使用了。
当然这也带来了更多的花销,所以你需要自己把握。


原文:http://stevedonovan.github.io/Penlight/api/topics/01-introduction.md.html 
 
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • LUA解析json小demo
    需要修改的json数据gui-config.json{"configs": [{"server": "JP3.ISS.TF","server_port": 443,"password": "58603228","method": "aes-256-cfb","remarks": ""},{"serv
    03-16
  • 第二十三篇:在SOUI中使用LUA脚本开发界面
    像写网页一样做客户端界面可能是很多客户端开发的理想。做好一个可以实现和用户交互的动态网页应该包含两个部分:使用html做网页的布局,使用脚本如vbscript,javascript做用户交互的逻辑。当需求变化时,只需要在服务端把相关代码调整一下,用户即可看到新的
    03-16
  • windows下编译lua源码"><转>windows下编译lua源
    因为之前一直使用 lua for windows 来搭建lua的使用环境,但是最新的 lua for windows 还没有lua5.2,我又想用这个版本的lua,所以被逼无奈只能自己编一下lua源码。首先从 lua的官网 下载你想要使用的lua源码,比如我下载的就是lua5.2。解压后内容如下:
    03-16
  • lua:使用Lua处理游戏数据
    在之前lua学习:lua作配置文件里,我们学会了用lua作配置文件。其实lua在游戏开发中可以作为一个强大的保存、载入游戏数据的工具。 比如说,现在我有一份表单:data.xls用什么工具解析这个Excel文件并将数据载入游戏?我们可以使用Lua来完成这个工作。不过要
    03-16
  • 第1课 - 学习 Lua 的意义
    第1课 - 学习 Lua 的意义
    第1课 - 学习 Lua 的意义1.Lua 简介           (1) 1993年、巴西(2) 小巧精致的脚本语言,大小只有 200K(3) 用标准C语言写成,能够在所有的平台上编译运行(4) 发明的目标是嵌入在C/C++中,为应用程序提供灵活的扩展和定制功能(5) 不适合用于开发
    03-16
  • RedisTemplate 常用API+事务+陷阱+序列化+pipeline+LUA
    RedisTemplate 常用API+事务+陷阱+序列化+pipel
    https://www.jianshu.com/p/7bf5dc61ca06/https://blog.csdn.net/qq_34021712/article/details/79606551https://www.jianshu.com/p/c9f5718e58f0dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId/depe
    03-08
  • Nginx动态路由的新姿势:使用Go取代lua nginx路由规则
    Nginx动态路由的新姿势:使用Go取代lua nginx路
    导语: 在Nitro 中, 我们需要一款专业的负载均衡器。 经过一番研究之后,Mihai Todor和我使用Go构建了基于Nginx、Redis 协议的路由器解决方案,其中nginx负责所有繁重工作,路由器本身并不承载流量。 这个解决方案过去一年在生产环境中运行顺畅。 以下是我
    03-08
  • cocos2d-lua 控制台输入Lua指令方便调试
    用脚本进行开发,如果不能实时去输入指令,就丧失了脚本的一大特色,所以对cocos2d-x程序稍微修改下,使其可以直接从控制台读入lua指令,方便调试。1 首先在行首加入lua的引用,如下1 #include "main.h"2 #include "AppDelegate.h"3 #include "cocos2d.h"4 #i
    02-09
  • lua_touserdata
    void *lua_touserdata(lua_State*L,intindex);如果给定索引处的值是一个完整的userdata,函数返回内存块的地址。如果值是一个lightuserdata,那么就返回它表示的指针。否则,返回NULL。例如: 在CCLuaStack::executeFunction()函数中有一段代码是用来获取c++
    02-09
  • Lua 5.2 中文参考手册
    闲来无事,发现Lua更新到了5.2.2,参考手册也更到了5.2,在网上发现只有云风翻译的5.1版,花了几天时间翻译了一些。参考手册有点长,又要随时修改,所以在github上建了项目,有需要的朋友可以看看,同时也欢迎指正。中文手册:Lua 5.2中文参考手册
    02-09
点击排行