当前位置: 首页 > news >正文

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

函数编程

 

序列

Lua 迭代器 (最简单的形式) 是一个函数,可以多次调用返回一个或多个值。for in语句理解迭代器和循环,直到该函数将返回nil。 Lua有标准的序列迭代器 (ipairspairs) , 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来迭代结果 — — 请注意索引 处有一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

 

什么函数被返回了?它是几乎肯定会在当前上下文中没有任何意义的东西。所以隐式序列可能会使某些类型的编程错误的更难抓到 — — 他们是最用于交互式探索和小脚本。

相关文章:

  • [LeetCode] NO. 169 Majority Element
  • apache添加 扩展php自定义的项目配置方法
  • 实现携程X分钟前有人预定功能
  • 怎么让块级元素水平和垂直都居中
  • 百度实习面经-JAVA研发方向
  • P1143 飘飘乎居士的约会
  • 让win7变成无线路由(需要用管理员权限打开)最后完善.rar
  • mysql 在大型应用中的架构演变
  • android 在布局中动态添加控件
  • JdbcTemplate+PageImpl实现多表分页查询
  • python os.path
  • 全国开设艺术类专业的211、985工程院校汇总
  • 优先队列的用法
  • 如何保持响应式设计新鲜感
  • 设计模式之Iterator模式
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • Android 初级面试者拾遗(前台界面篇)之 Activity 和 Fragment
  • canvas 绘制双线技巧
  • JavaScript对象详解
  • MobX
  • Otto开发初探——微服务依赖管理新利器
  • passportjs 源码分析
  • Python代码面试必读 - Data Structures and Algorithms in Python
  • Spark VS Hadoop:两大大数据分析系统深度解读
  • SpringCloud(第 039 篇)链接Mysql数据库,通过JpaRepository编写数据库访问
  • 阿里云应用高可用服务公测发布
  • 从0到1:PostCSS 插件开发最佳实践
  • 今年的LC3大会没了?
  • 使用 5W1H 写出高可读的 Git Commit Message
  • 使用Maven插件构建SpringBoot项目,生成Docker镜像push到DockerHub上
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • ​Z时代时尚SUV新宠:起亚赛图斯值不值得年轻人买?
  • #include
  • $GOPATH/go.mod exists but should not goland
  • (C)一些题4
  • (ibm)Java 语言的 XPath API
  • (pt可视化)利用torch的make_grid进行张量可视化
  • (Repost) Getting Genode with TrustZone on the i.MX
  • (二)Linux——Linux常用指令
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (原創) 未来三学期想要修的课 (日記)
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .NET CORE Aws S3 使用
  • .NET 简介:跨平台、开源、高性能的开发平台
  • .NET 跨平台图形库 SkiaSharp 基础应用
  • .NET 设计模式—适配器模式(Adapter Pattern)
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则
  • .NET6 命令行启动及发布单个Exe文件
  • .NET6实现破解Modbus poll点表配置文件
  • .NET开源项目介绍及资源推荐:数据持久层 (微软MVP写作)
  • .net企业级架构实战之7——Spring.net整合Asp.net mvc