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 

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. Node.js:常用工具、路由

    一.常用工具util util 是一个Node.js 核心模块,提供常用函数的集合,用于弥补核心JavaScript 的功能 过于精简的不足. 1.util.inherits util.inherit ...

  2. SQL IN

    here are some additional clause in the SQL language that can be used to simplify queries by decrease ...

  3. 介绍Visual Studio的Android模拟器

    介绍Visual Studio的Android模拟器 http://blogs.msdn.com/b/visualstudioalm/archive/2014/11/12/introducing-vi ...

  4. [Algorithm -- Dynamic Programming] Recursive Staircase Problem

    For example there is a staricase N = 3 | ---|   |---|    | |---|            | ---|                  ...

  5. 如何使用飞秋FeiQ实现两电脑通信(或传输文件)

    如何使用飞秋FeiQ实现两电脑通信(或传输文件) 1. 在两天电脑上,分别按照飞秋FeiQ 我使用的绿色飞秋2013正式版 2. 使用一根网线,将两电脑的网口连接一起 3. 设置飞秋FeiQ的端口号不 ...

  6. Java程序实现密钥库的维护

    1 Java程序列出密钥库所有条目 import java.util.*; import java.io.*; import java.security.*; public class ShowAli ...

  7. Android + Eclipse + PhoneGap 3.4 安卓最新环境配置,部分资料整合网上资料,已成功安装.

    前言:广州花都论坛,打算推出本地服务o2o应用.快速开发手机应用,phonegap 我的小站,http://www.w30.cn/ 如果有什么问题也可以到小组留言,可以的话,贡献一个ip:) phon ...

  8. itext处理pdf

    http://www.java2s.com/Tutorial/Java/0419__PDF/DecryptPdffile.htm

  9. js Object.is 相等判断

    Object.is使用“Same-value equality”(同值相等)算法进行相等判断.它用来比较两个值是否严格相等,与严格比较运算符(===)的行为基本一致. Object.is('foo', ...

  10. Android自动化框架

    Android自动化框架 已有 2085 次阅读2014-8-26 12:19 | Android 几种常见的Android自动化测试框架及其应用 随着Android应用得越来越广,越来越多的公司推出 ...