lua工具库penlight--07函数编程(一)
函数编程
序列
Lua 迭代器 (最简单的形式) 是一个函数,可以多次调用返回一个或多个值。for in语句理解迭代器和循环,直到该函数将返回nil。 Lua有标准的序列迭代器 (ipairs和pairs) , io.lines是返回文件中的所有行的迭代器。在的Penlight库中,这种迭代器也称为序列。单个值 (比如从io.lines ) 的序列称为单值,由pairs定义的序列是双值.
pl.seq提供一些有用的迭代器和一些操作序列的函数。乍一看本示例尝试在Lua写里 Python (有序列):
> for i in seq.range(1,4) do print(i) end
1
2
3
4
但是range 实际上相当于 Python 的xrange,因为它会生成一个序列,而不是列表。若要获取列表,请使用seq.copy(seq.range(1,10)),它使用任何单值序列作参数,并从seq.list生成表,seq.copy的行为和ipairs一样,除了它不给你索引中,只有值。
> for x in seq.list {1,2,3} do print(x) end
1
2
3
enum 可以把序列变成双值序列组,所以enum(list(ls))是实际上相当于ipairs 。一个更有趣的例子,打印出带有行号的文件:
for i,v in seq.enum(io.lines(fname)) do print(i..' '..v) end
序列可以合并,由 'zipping(压缩)' 它们或将它们连接起来。
> for x,y in seq.zip(l1,l2) do print(x,y) end
10 1
20 2
30 3
> for x in seq.splice(l1,l2) do print(x) end
10
20
30
1
2
3
seq.printall用于打印出单值的序列,并提供一些格式化的精细控制,如分隔符,断行,可以使用格式化字符串(详见string.format) 的。
> seq.printall(seq.random(10))
0.0012512588885159 0.56358531449324 0.19330423902097 ....
> seq.printall(seq.random(10), ',', 4, '%4.2f')
0.17,0.86,0.71,0.51
0.30,0.01,0.09,0.36
0.15,0.17,
map 可以把在序列上应用函数。
> seq.printall(seq.map(string.upper, {'one','two'}))
ONE TWO
> seq.printall(seq.map('+', {10,20,30}, 1))
11 21 31
filter 可以使用布尔值的函数 (通常称为谓词)来筛选。例如,此代码仅打印文件中的全是数字的行:
for l in seq.filter(io.lines(file), stringx.isdigit) do print(l) end
下面的示例返回所有大于0的表 (相当于tablex.filter(ls, ‘>’, 0))
ls = seq.copy(seq.filter(ls, '>', 0))
在讨论 input.numbers时已经提到过了seq.sum。这也可以用seq.reduce :
> seq.reduce(function(x,y) return x + y end, seq.list{1,2,3,4})
10
seq.reduce可以递归的调用二元函数(译注:即带有两个参数的函数),如:
reduce(op,{1,2,3}) => op(1,reduce(op,{2,3}) => op(1,op(2,3))
(译注:即不断的向op传入两个参数,直到最后两个)
现在可以轻松地进行累积操作,pl.operator中提供的标准操作很有用:
> ops = require 'pl.operator'
> -- can also say '*' instead of ops.mul
> = seq.reduce(ops.mul,input.numbers '1 2 3 4')
24
有一些函数可以从一个数字序列中提取统计数字:
> l1 = List {10,20,30}
> l2 = List {1,2,3}
> = seq.minmax(l1)
10 30
> = seq.sum(l1)
60 3
会经常碰到在值重复的序列里提取值。count_map可以对这种序列进行处理,返回每个键,及其关联的值是它们出现的次数所组成的表:
> t = seq.count_map {'one','fred','two','one','two','two'}
> = t
{one=2,fred=1,two=3}
它也可以处理数值序列,但你不能期望结果是适当的列表,即有没有“hole”。相反,您总是需要使用pairs来迭代结果 — — 请注意索引 5 处有一个hole:
> t = seq.count_map {1,2,4,2,2,3,4,2,6}
> for k,v in pairs(t) do print(k,v) end
1 1
2 4
3 1
4 2
6 1
unique使用count_map 返回唯一值得列表。
last 可以使用当前值和前一个值把单值序列转为双值序列:
> for current,last in seq.last {10,20,30,40} do print (current,last) end
20 10
30 20
40 30
这样就可以轻松做事,如识别文件中重复的行,或构建值之间的差异。filter 可以处理双值的序列,如通过使用operator.lt,或只是 ' <',返回当前值小于上一个值的序列,结果会复制到表中。
> ls = {10,9,10,3}
> = seq.copy(seq.filter(seq.last(s),'<'))
{9,3}
序列的包装
pl.seq 中的函数涵盖了处理序列时常见的模式,但将这些函数链接在一起可以导致丑陋的代码。考虑前面的例子 ,seq重复了三次,所生成的表达式要从右到左读。第一个问题可以使用局部变量别名解决,这样表达式改为copy(filter(last(s),‘<’)) 。第二个问题是指秩序有点不自然的应用程序。我们往往更喜欢从左到右进行读操作,这是为什么面向对象的表示法成为最受欢迎的原因之一。序列适配器允许此表达式就像这样:
seq(s):last():filter('<'):copy()
这种表示法,可以用到左到右的链式调用。
序列不是一个基本的 Lua 类型,他们通常是函数或可调用对象。表达式seq(s)将一个序列包装在sequence wrapper,它是一个对象,可以理解pl.seq中所有的函数。然后,此对象显式表示序列。
作为一种特殊情况,该构造函数 (这是当您调用表seq ) 将包装简单表。在这里我们将长度运算符应用到序列中的字符串,并将它们打印出来。
> seq{'one','tw','t'} :map '#' :printall()
3 2 1
为方便,seq.lines表现就像io.lines一样,除了它将包装的结果作为显式序列类型。这个例子需要从标准输入一次读入10 行,把读入的字符变为大写,并把它变成一个计数、值序列,再用连接运算符连接,最后打印出带有换行符结果。
seq.lines():take(10):upper():enum():map('..'):printall '\n'
请注意方法upper,而不是一个seq函数。如果未知的方法被调用,序列的包装会将该方法应用到 (这是隐式使用的mapmethod )序列中的所有值 。
用这种方式创建自定义的序列很简单。在 Unix 上, /dev/random给你无限序列的随机字节,所以我们使用take 来限制序列,然后用map 缩放结果到所需范围。关键的一步是使用seq来包装的迭代器函数:
-- random.lua
local seq = require 'pl.seq'
function dev_random()
local f = io.open('/dev/random')
local byte = string.byte
return seq(function()
-- read two bytes into a string and convert into a 16-bit number
local s = f:read(2)
return byte(s,1) + 256*byte(s,2)
end)
end
-- print 10 random numbers from 0 to 1 !
dev_random():take(10):map('%',100):map('/',100):printall ','
另一个一行Linux 程序取数据于/proc文件系统,可以当前正在运行的进程的列表:
pids = seq(lfs.dir '/proc'):filter(stringx.isdigit):map(tonumber):copy()
此版本的Penlight有一个实验性的功能,它依赖于所有Lua 类型可以都有 metatables,包括函数的事实。这使隐式序列包装成为可能:
> seq.import()
> seq.random(5):printall(',',5,'%4.1f')
0.0, 0.1, 0.4, 0.1, 0.2
这可以避免如seq(seq.random(5))尴尬的构造,完全来自其他地方的迭代器,如下:
> ('one two three'):gfind('%a+'):printall(',')
one,two,three,
seq.import后不再需要显式包装序列函数。
但对于这种便利付出的代价。每个函数都受影响,这样的任何函数可以用,适当的或不适当的:
> math.sin:printall()
..seq.lua:287: bad argument #1 to '(for generator)' (number expected, got nil)
> a = tostring
> = a:find(' ')
function: 0042C920
什么函数被返回了?它是几乎肯定会在当前上下文中没有任何意义的东西。所以隐式序列可能会使某些类型的编程错误的更难抓到 — — 他们是最好用于交互式探索和小脚本。
lua工具库penlight--07函数编程(一)的更多相关文章
- lua工具库penlight--01简介
lua的设计目标是嵌入式语言,所以和其它动态语言(如python.ruby)相比其自带的库缺少很多实用功能. 好在有lua社区有Penlight,为lua提供了许多强大的功能,接下来的几篇博客,我会简 ...
- lua工具库penlight--07函数编程(二)
列表压缩 列表压缩是以紧凑的方式通过指定的元素创建表.在 Python里,你可以说: ls = [x for x in range(5)] # == [0,1,2,3,4] 在 Lua,使用pl.c ...
- lua工具库penlight--06数据(二)
词法扫描 虽然 Lua 的字符串模式匹配是非常强大,但需要更强大的东西.pl.lexer.scan可以提供标记字符串,按标记机分类数字.字符串等. > lua -lpl Lua 5.1.4 C ...
- lua工具库penlight--06数据(一)
这篇太长了,分了两部分.(这个是机器翻译之后我又校对了一下,以后的都这样,人工翻译太累了.) 读数据文件 首先考虑清楚,你的确需要一个自定义的文件读入器吗?如果是,你能确定有能力写好吗? 正确,稳健, ...
- lua工具库penlight--09技术选择
模块化和粒度 在理想的世界,一个程序应该只加载它需要的库.Penlight需要额外100 Kb 的字节码来工作.它是简单但却乏味要加载你需要什么: local data = require 'pl.d ...
- lua工具库penlight--08额外的库(一)
额外的库 在这一节中的库不再被认为是Penlight的核心部分,但在需要时,仍提供专门的功能. 简单的输入的模式 Lua 的字符串模式匹配是非常强大,通常您将不需要传统的正则表达式库.即便如此,有时 ...
- lua工具库penlight--04路径和目录
使用路径 程序不应该依赖于奇葩的系统,这样你的代码会难以阅读和移植.最糟糕的是硬编码的路径, windows和Unix的路径分隔符正好相反.最好使用path.join,它可以帮助你解决这个问题. pl ...
- lua工具库penlight--02表和数组
类Python的List lua的优美之处在于把数组和关联数组都用table实现了(Python中叫list和dict,C++中叫vector和map). 一般我们把数字索引的table叫做list. ...
- lua工具库penlight--03字符串
字符串提取函数 这些方法也是从Python借鉴来的,但索引从1开始.stringx定义了一些函数如isalpha和isdigit, 用来判断字母和数字:startswith和endswith可以方便用 ...
随机推荐
- iOS:网页视图控件UIWebView的详解
网页视图控件:UIWebView 功能:它是继承于UIView的,是一个内置的浏览器控件,以用来浏览从网络下载下来的网页或者本地上加载下来的文档. 枚举: //网页视图导航类型 typedef NS_ ...
- linux下使用C++ Json库
安装Json库 1.下载JsonCpphttp://sourceforge.net/projects/jsoncpp/files/ 2.下载sconshttp://sourceforge.net/pr ...
- 流畅的python第十七章使用期物处理并发
从 Python 3.4 起,标准库中有两个名为 Future 的类:concurrent.futures.Future 和asyncio.Future.这两个类的作用相同:两个 Future 类的实 ...
- 关于http协议session和cookie的理解
http是无状态协议,不能够记录访问者的身份, 为了解决这一问题服务器端设置了session 浏览器端设置了cookie 这种机制 当浏览器第一次访问服务器的时候,服务器会判断是否有cookie的存在 ...
- Centos6.8配置svn
svn的安装:yum -y install subversion 一.一个仓库放所有的项目 创建仓库,以后所有代码都放在这个下面,创建成功后在svn下面多了几个文件夹.1.创建仓库:svnadmin ...
- [React + Functional Programming ADT] Create Redux Middleware to Dispatch Multiple Actions
We only have a few dispatching functions that need to be known by our React Application. Each one ac ...
- [Tools] Unlock TypeScript's Features in Vanilla JS with @ts-check and JSDoc
TypeScript can help you with your plain JavaScript files if you add a simple //@ts-check comment. Th ...
- Mybatis错误:Result Maps collection already contains value for 。。。。
解决方法 原因:xml文件中存在重名对象,保持名称不要一样即可正常启动.因为我再次使用逆向工程生成mapper接口和xml文件时,忘了删除原来的xml文件,新生成的与旧的同时出现旧重复了. 那么我们在 ...
- 优化SQL Server的内存占用之执行缓存
在论坛上常见有朋友抱怨,说SQL Server太吃内存了.这里笔者根据经验简单介绍一下内存相关的调优知识 首先说明一下SQL Server内存占用由哪几部分组成.SQL Server占用的内存主要 ...
- MongoDB数据库设计中6条重要的经验法则
Part 1 原文:6 Rules of Thumb for MongoDB Schema Design: Part 1 By William Zola, Lead Technical Support ...