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提供了正规和非正规两种方式,你可以随意使用。

 正规方式:

  1. local utils = require 'pl.utils'
  2. utils.printf("%s\n","hello, world!")
  3.  
  4. 非正规方式:
  1. require 'pl'
  2. utils.printf("%s\n","That feels better")

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

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

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

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

  1. local pl = require'pl.import_into'()

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

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

  1. local _ENV,M = require 'pl.import_into' ()
  2.  
  3. function answer ()
  4. -- all the Penlight modules are available!
  5. return pretty.write(utils.split '10 20 30', '')
  6. end
  7.  
  8. return M

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

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

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

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

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

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

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

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

声明。如下:

  1. > require 'pl.strict'
  2. > print(x)
  3. stdin:1: variable 'x' is not declared
  4. > x = nil
  5. > print(x)
  6. nil
  7.  
  8. strict模块符合Penlight的“用时加载”策略。
    strict也禁止对全局变量赋值,除非在主程序里。如果你想绕过这个机制使用rawsetrawget
    如果你想让strict全局生效,在pl/init.lua最后加上require 'pl.strict',并在主程序里调用。
    1.1.0之后,strict提供了strict.module函数来创建(或修改)模块,以符合此机制。
    如:
  1. -- mymod.lua
  2. local strict = require 'pl.strict'
  3. local M = strict.module (...)
  4.  
  5. function M.answer ()
  6. return 42
  7. end
  8.  
  9. return M
  1. 如果你不小心错打成 mymod.Answer(),你会得到运行时错误:“variable Answer is not declared
    in mymod’”。
    这对已有的模块也有效,也可以修改lua标准库。
  1. strict.make_all_strict(_G)
  1. 这样当你错打成 math.cosine 时会得到错误信息,而不是nil
  2.  
  3. 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. > _1 = func._1
  2. > = List{10,20,30}:map(_1+1)
  3. {11,21,31}
    (译注:_1,_2表示第几个参数)
  1. 另外一个有用的是utils.string_lambda ,也会自动调用
  1. > = List{10,20,30}:map '|x| x + 1'
  2. {11,21,31}
    (译注:即|x|=x+1
  1. 少用循环的利弊
    循环是一种更机器的方式,如:
  1. local res = {}
  2. for i = 1,#ls do
  3. res[i] = fun(ls[i])
  4. end
    但是这可以更高效的写作,ls:map(fun),不但代码更少,而且更易理解。
    通常人们认为,这么写效率低,更消耗内存。如ls1:map2(‘*’,ls2):reduce ‘+’
    会产生多余的临时对象。但是效率是相对的。
    写循环容易出错而且乏味。(译注:省略了许多议论,结论是利大于弊)
  5.  
  6. 通用函数
     utils.memoize用来存储需要多次调用且花销大的函数。它可以保存调用函数后的已有结果,
    如果参数相同直接返回结果。
  1. sum = utils.memoize(function(n)
  2. local sum = 0
  3. for i = 1,n do sum = sum + i end
  4. return sum
  5. end)
  6. ...
  7. s = sum(1e8) --takes time!
  8. ...
  9. s = sum(1e8) --returned saved value!
  1.  应用支持
    app.parse_args是一个简单的命令行参数解析器,支持gnu风格。
    app.appfile,用来存储应用的配置。
    prett模块让table更易读。(译注:省略了很多用法讲解,可以直接看api
    简单的面向对象
    提供了面想对象的支持,
  1. -- animal.lua
  2.  
  3. class = require 'pl.class'
  4.  
  5. class.Animal()
  6.  
  7. function Animal:_init(name)
  8. self.name = name
  9. end
  10.  
  11. function Animal:__tostring()
  12. return self.name..': '..self:speak()
  13. end
  14.  
  15. class.Dog(Animal)
  16.  
  17. function Dog:speak()
  18. return 'bark'
  19. end
  20.  
  21. class.Cat(Animal)
  22.  
  23. function Cat:_init(name,breed)
  24. self:super(name) -- must init base!
  25. self.breed = breed
  26. end
  27.  
  28. function Cat:speak()
  29. return 'meow'
  30. end
  31.  
  32. class.Lion(Cat)
  33.  
  34. function Lion:speak()
  35. return 'roar'
  36. end
  37.  
  38. fido = Dog('Fido')
  39. felix = Cat('Felix','Tabby')
  40. leo = Lion('Leo','African')
  41.  
  42. $ lua -i animal.lua
  43. > = fido,felix,leo
  44. Fido: bark Felix: meow Leo: roar
  45. > = leo:is_a(Animal)
  46. true
  47. > = leo:is_a(Dog)
  48. false
  49. > = leo:is_a(Cat)
  50. true
  51.  
  52. 也可以这么写:
  1. local class = require 'pl.class'
  2.  
  3. class.Named {
  4. _init = function(self,name)
  5. self.name = name
  6. end;
  7.  
  8. __tostring = function(self)
  9. return 'boo '..self.name
  10. end;
  11. }
  12.  
  13. b = Named 'dog'
  14. print(b)
  15. --> boo dog
  16.  
  17. Penlight提供了一些有用的类如:ListSetMapMultiMap,OrderdMap.
    所有的类有catch函数,可以处理未预料的情况,如:
  1. Strings:catch(function(self,name)
  2. return function() error("no such method "..name,2) end
  3. end)
    上面只是简单的输出错误,但你可以创造一些好玩的,如;
  1. Strings:catch(List.default_map_with(string))
  2.  
  3. ls = Strings{'one','two','three'}
  4. asserteq(ls:upper(),{'ONE','TWO','THREE'})
  5. asserteq(ls:sub(1,2),{'on','tw','th'})
    (译注:list没有upper,sub,但是catch让没有的函数都执行string里函数)
  1. cast类型转换,它不产生新对象,而是返回你传入的对象。
  1. local sizes = ls:map '#' --即取长度
  2. asserteq(sizes, {3,3,5})
  3. asserteq(utils.type(sizes),'Strings')
  4. asserteq(sizes:is_a(Strings),true)
  5. sizes = Vector:cast(sizes)
  6. asserteq(utils.type(sizes),'Vector')
  7. asserteq(sizes+1,{4,4,6})
    (译注,lsStrings{'one','two','three'})
  1. utils.type可以返回类中_name属性的字符串。
  2.  
  3. 属性模式,我们想控制用户对属性的访问,但不想让用户,使用obj:get_field()这么长
    的代码访问。解决办法如下:
  1. local MyProps = class(class.properties)
  2. local setted_a, got_b
  3.  
  4. function MyProps:_init ()
  5. self._a = 1
  6. self._b = 2
  7. end
  8.  
  9. function MyProps:set_a (v)
  10. setted_a = true
  11. self._a = v
  12. end
  13.  
  14. function MyProps:get_b ()
  15. got_b = true
  16. return self._b
  17. end
  18.  
  19. local mp = MyProps()
  20.  
  21. mp.a = 10
  22.  
  23. asserteq(mp.a,10)
  24. asserteq(mp.b,2)
  25. asserteq(setted_a and got_b, true)
  1.  规则是内部的变量以"_"开头,当访问mp.a时先检查特定的getter(取值器),
    get_a。同理setter(设定器)也被使用了。
    当然这也带来了更多的花销,所以你需要自己把握。
  1.  
  2. 原文:http://stevedonovan.github.io/Penlight/api/topics/01-introduction.md.html 

lua工具库penlight--01简介的更多相关文章

  1. lua工具库penlight--06数据(二)

    词法扫描 虽然 Lua 的字符串模式匹配是非常强大,但需要更强大的东西.pl.lexer.scan可以提供标记字符串,按标记机分类数字.字符串等. > lua -lpl Lua 5.1.4  C ...

  2. lua工具库penlight--07函数编程(一)

    函数编程 序列 Lua 迭代器 (最简单的形式) 是一个函数,可以多次调用返回一个或多个值.for in语句理解迭代器和循环,直到该函数将返回nil. Lua有标准的序列迭代器 (ipairs和pai ...

  3. lua工具库penlight--09技术选择

    模块化和粒度 在理想的世界,一个程序应该只加载它需要的库.Penlight需要额外100 Kb 的字节码来工作.它是简单但却乏味要加载你需要什么: local data = require 'pl.d ...

  4. lua工具库penlight--08额外的库(一)

    额外的库 在这一节中的库不再被认为是Penlight的核心部分,但在需要时,仍提供专门的功能. 简单的输入的模式 Lua 的字符串模式匹配是非常强大,通常您将不需要传统的正则表达式库.即便如此,有时  ...

  5. lua工具库penlight--07函数编程(二)

    列表压缩 列表压缩是以紧凑的方式通过指定的元素创建表.在 Python里,你可以说: ls = [x for x in range(5)]  # == [0,1,2,3,4] 在 Lua,使用pl.c ...

  6. lua工具库penlight--04路径和目录

    使用路径 程序不应该依赖于奇葩的系统,这样你的代码会难以阅读和移植.最糟糕的是硬编码的路径, windows和Unix的路径分隔符正好相反.最好使用path.join,它可以帮助你解决这个问题. pl ...

  7. lua工具库penlight--02表和数组

    类Python的List lua的优美之处在于把数组和关联数组都用table实现了(Python中叫list和dict,C++中叫vector和map). 一般我们把数字索引的table叫做list. ...

  8. lua工具库penlight--06数据(一)

    这篇太长了,分了两部分.(这个是机器翻译之后我又校对了一下,以后的都这样,人工翻译太累了.) 读数据文件 首先考虑清楚,你的确需要一个自定义的文件读入器吗?如果是,你能确定有能力写好吗? 正确,稳健, ...

  9. lua工具库penlight--05日期和时间

    创建和显示时间 Date类提过了简洁的使用date和time的方法.它依赖于os.date和os.time. Date对象可以通过table创建,如果os.date,同时提过了获取和设置date 成员 ...

随机推荐

  1. [AngularJS] ng-change vs $scope.$watch

    <div class="form-group"> <label for="pwd">Password</label> < ...

  2. Android研究之动态创建UI界面具体解释

     Android的基本UI界面一般都是在xml文件里定义好,然后通过activity的setContentView来显示在界面上.这是Android UI的最简单的构建方式.事实上,为了实现更加复 ...

  3. .NET反编译之manager,base.AutoScaleMode修复

    使用反编译软件导出项目时,出现警告:设计器无法处理第X 行的代码:this.AutoScaleMode = AutoScaleMode.Font;方法"InitializeComponent ...

  4. 从主机系统向虚拟机系统里面copy 文件

    从主机系统向虚拟机系统里面copy 文件: 一:请确保你的虚拟机里面安装了 VMTools 1:安装VMTools 2: 进入虚拟机系统里面,如果没有自动运行 VMtool安装程序,请打开我的电脑,手 ...

  5. 算法笔记_134:字符串编辑距离(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 给定一个源串和目标串,能够进行如下操作: 在任意位置上插入一个字符: 替换掉任意字符: 删除任意字符. 写一个程序,实现返回最小操作次数,使得对源串 ...

  6. ant design pro(一)安装、目录结构、项目加载启动【原始、以及idea开发】

    一.概述 1.1.脚手架概念 编程领域中的“脚手架(Scaffolding)”指的是能够快速搭建项目“骨架”的一类工具.例如大多数的React项目都有src,public,webpack配置文件等等, ...

  7. 解构赋值 和 symbol

    1.数组解构 let [a,b,c,d] = ['aa','bb',77,88] 嵌套数组解构 let [a,b,[c,d],e] = ['aa','bb',[33,44],55] 空缺变量 let ...

  8. 数据库-IO系统性能之衡量性能的几个指标

    转自http://storage.it168.com/a2011/0323/1169/000001169755_all.shtml 作为一个数据库管理员,关注系统的性能是日常最重要的工作之一,而在所关 ...

  9. jquery api 常见api 效果操作例子

    addClass_removeClass_toggleClass_hasClass.html <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 ...

  10. sqlserver学习笔记(二)—— 创建登录名、用户名

    (重要参考:51自学网——SQL Server数据库教程) 登录名与用户名的区别: 1.登录名是指可以使用新建的登录名和密码登录数据库这个程序软件,但不能打开或展开用户自己创建的数据库: 2.用户名是 ...