类Python的List

lua的优美之处在于把数组和关联数组都用table实现了(Python中叫list和dict,C++中叫vector和map)。
一般我们把数字索引的table叫做list。penlight里的List模仿了Python,看起来从Python借鉴是个好主意。
下面是一个List的例子,List实现了__tostirng,因此可以输出自己。
 
)

)
,
}
,
}
{
,,,
}
,
)
{
,,,,
}

,,,
}

)
 
 
)
 
true > = l:reverse()  ---> note: doesn't make a copy! {30,20,10,5}

尽管一些方法,如 sort和reverse,会就地操作改变list,但会返回原始的list。这样做可以链式调用,如
ls = ls:append(10):append(20):reverse():append(1)。但是没有额外的复制开销,因此ls没有改变
等式。不像string,List是可变的,如果想复制一个list,List(ls)就行。如果传入的参数是qit表,List
会设置这个表的元表为List,而不是复制。
Python list的一个特性是slice,如同string.sub。Penlight的List也支持注意索引从1 开始。
,,,
}
,)  
}
,
)
{

}
,
)
{
,
}
,
)
{
,
}
,,{,,
})
{
,,,,,
}
,
)
{
,,,,}

slice_assign和chop会修改list,前者如同Python的  l[i1:i2]=seq,后者如同 del l[i1:i2]。
List对象如果lua 表,而且增加了等于和连接操作符,两个list相等,仅当list里的所有对象相等。
,,
}
,,
}
,,,,,}
 
List的构造函数可以用函数做参数,此时它假设此函数是个迭代器,并可以重复调用产生序列。
比如io.lines.
-- linecount
require 'pl' 
ls = List(io.lines())
print(#ls)
 

​List iterate可以捕获其认为是序列的内容,如

>for ch in List.iterate 'help' do io.write(ch,' ') end
>h e l p
List内部构造函数调用了此函数,所以string可以轻松转为list。
 
还有些函数超过了Python原有List。如,partition分割list为子list。
,,,}
> ops =require 'pl.operator' 
 end)
{false,},true,}}
},,,List{,}}
},string,},table},{,}}}
这个函数返回了table而不是List。注意List里的函数可以使用普通的table,但仅对table的数组部分有些。 
 
栈在计算机中经常使用。List支持类栈的操作,如pop(移除并返回最后一个元素),push(在末尾添加一个元素),push是append的别名,size即#操作符。队列也被实现了,使用pop出对,put入队。
 
你可能会继承List,由于list的返回类型会相应变化,如slice会返回子类型,而不是List。例子,如下:
tests,,}
n2 ,,}
ns ,,})
min,max ,),))
asserteq(n1,)

Map和Set
 

Map的访问和table类似,如m[‘alice‘]与m.alice等效。另外Map提供了

set和get方法,当Map被继承是很有用,还有updat。
,two}
,four,two}
,two,three,four}
true
 
方法values返回value序列,keys返回key序列,都是无序的。getvalues,
返回与key关联的value。
,two,three}
,}
> = m:getvalues(m:keys()) == m:values()
true
当查询Map的值时,最后用get方法:
  > print(m:get 'one', m:get 'two')
 1     2
 
m[key]是模棱两可的,例如一个索引是get,那么m["get"],总会成功,原因是如果
找不到get,它会在Map的元表里找,而元表里有get方法。还没有解决这个恼人问题
的简单法子。
有一些有用的继承自Map的类,如有序的OrderedMap,当用其迭代器遍历时,会按插入
顺序返回。
Set集合,特殊类型的Map,所有的value都是true,提供了并集(+),交集(*)操作。
> Set = require 'pl.Set'
> = Set{'one','two'} == Set{'two','one'}
true
> fruit = Set{'apple','banana','orange'}
> = fruit['banana']
true
> = fruit['hazelnut']
nil
> = fruit:values()
{apple,orange,banana}
> colours = Set{'red','orange','green','blue'}
> = fruit,colours
 [apple,orange,banana]   [blue,green,orange,red]
> = fruit+colours
[blue,green,apple,red,orange,banana]
> = fruit*colours
[orange]
 
还有Set.difference(差集)和Set.symmetric_difference(先求并集再除去交集)。
> = fruit - colours
[apple,banana]
> = fruit ^ colours
[blue,green,apple,red,banana]
添加元素只需fruit[‘peach’] = true,删除只需fruit[‘apple’] = nil,为了更简单,
Set类没有方法,你可以使用[]操作符,也可以使用Set.intersect,从而避免了Map的问题。
 
Table的一些有用操作
Lua的table有数组和关联数组两部分,一些操作只对数组有效,另一些则只对关联数组有效。
(实际都是关联数组,只是对数组的实现非常高效)。
List提供了一些高级的更有效的操作,如复制表:
 local res = {}
for k,v in pairs(T) do
     res[k] = v
end
return res
tablex模块提供了copy,浅复制;还有deepcopy深复制,可以复制元表和嵌套表。还提供了
icopy可以对类list的表有选择的部分复制,同时删除左边的部分。
 asserteq(icopy({1,2,3,4,5,6},{20,30}),{20,30})   -- start at 1
asserteq(icopy({1,2,3,4,5,6},{20,30},2),{1,20,30}) -- start at 2
asserteq(icopy({1,2,3,4,5,6},{20,30},2,2),{1,30}) -- start at 2, copy from 2
move会覆盖,而不出是删除。
asserteq(move({1,2,3,4,5,6},{20,30}),{20,30,3,4,5,6})
asserteq(move({1,2,3,4,5,6},{20,30},2),{1,20,30,4,5,6})
asserteq(move({1,2,3,4,5,6},{20,30},2,2),{1,30,3,4,5,6})
(类似C里的strcpy和memmove)
总结一下,用copy和deepcopy复制一个表。复制到类map的表用updae;复制到类list的用icopy,
如果是更新目的表用move。
insertvalues和table.insert类似,它可以插入table里值,removevalues移除某些值。
asserteq(insertvalues({1,2,3,4},2,{20,30}),{1,20,30,2,3,4})
asserteq(insertvalues({1,2},{3,4}),{1,2,3,4})
 > T = require 'pl.tablex'
> t = {10,20,30,40}
> = T.removevalues(t,2,3)
{10,40}
> = T.insertvalues(t,2,{20,30})
{10,20,30,40}
和deepcopy类似,deepcompare会深入比较两个表,如果它们有相同的值和结够才返回true。
 > t1 = {1,{2,3},4}
> t2 = deepcopy(t1)
> = t1 == t2
false
> = deepcompare(t1,t2)
true
find可以查找类list表的值,并返回索引。和string.find类似,你可以指定起点,还有可选的终点参数。
这样可以定义rfind,从末尾查找。
function rfind(t,val,istart)
     return tablex.find(t,val,istart,true)
end
find是线性搜索,可能会减慢代码的运行。对很大的表可以用index_map,它会返回一个表,其索引
是原来表的值。
 > t = {'one','two','three'}
> = tablex.find(t,'two')
2
> = tablex.find(t,'four')
nil
> il = tablex.index_map(t)
> = il['two']
2
> = il.two
2
另一个版本的index_map是makeset,其值都是true。它很有用,尤其是用deepcompar比较两个set时。
 > = deepcompare(makeset {1,2,3},makeset {2,1,3})
true
 
让我们思考一个关于职工的问题。假设我们有两份职工的文件名:
(last-month.txt)
smith,john
brady,maureen
mongale,thabo
 
(this-month.txt)
smith,john
smit,johan
brady,maureen
mogale,thabo
van der Merwe,Piet
想找到不同,只需把职工list转为set,如下:
 require 'pl'
 
function read_employees(file)
   local ls = List(io.lines(file)) -- a list of employees
   return tablex.makeset(ls)
end
 
last = read_employees 'last-month.txt'
this = read_employees 'this-month.txt'
 
-- who is in this but not in last?
diff = tablex.difference(this,last)
 
-- in a set, the keys are the values...
for e in pairs(diff) do print(e) end
 
 --  *output*
-- van der Merwe,Piet
-- smit,johan
difference操作符的实现并不难;
for e in pairs(this) do
   if not last[e] then
     print(e)
   end
end
用difference并不是代码技巧,而是让读你代码的人更易懂(对六个月后的你,也是如此)。
find_if使用函数查找表,可选的第三个参数,将会作为函数的第二个参数传入。pl.operator为
lua提供了操作符的函数包装,这样可以实现一些基本的比较操作。
 > ops = require 'pl.operator'
> = tablex.find_if({10,20,30,40},ops.gt,20)
 3       true
注意find_if会返回传入函数返回的值。
 
deepcompare会递归比较,但是默认的compare允许你使用指定的函数比较类list的表。
当类list表包含相同元素时,compare_no_order返回true,不需要特定的比较函数。如
比较set:
> = compare_no_order({1,2,3},{2,1,3})
true
> = compare_no_order({1,2,3},{2,1,3},'==')
true
(注意,上面的“==”字符串,不必用ops.gt,ops.eq,直接用'>'或'==")
 
sort和sortv返回待排序表里的迭代器。sort以key的顺序遍历,sortv以value的顺序遍历。
如下面的姓名年龄表:
> t = {john=27,jane=31,mary=24}
> for name,age in tablex.sort(t) do print(name,age) end
 jane    31
 john    27
 mary    24
> for name,age in tablex.sortv(t) do print(name,age) end
 mary    24
 john    27
 jane    31
在PL里有多种合并表的方法。如果是类list的可以用pl.List里的的操作,如concatenation;
如果是类map的用merge。若merge的第三个参数是假,则只会取两个表里相同key的;为真
,则是两个表里所有的key。
 > S1 = {john=27,jane=31,mary=24}
> S2 = {jane=31,jones=50}
> = tablex.merge(S1, S2, false)
{jane=31}
> = tablex.merge(S1, S2, true)
{mary=24,jane=31,john=27,jones=50}
 
当用table时,你发现自己掉入了循环里。循环是程序员的第二天性,但不是最优雅和自我描述
的方式。看一下map函数,它把原始表的每个元素传入到函数,返回一个新表。
> = map(math.sin, {1,2,3,4})
 {  0.84,  0.91,  0.14, -0.76}
> = map(function(x) return x*x end, {1,2,3,4})
{1,4,9,16}
 
map让你少写循环,代码更整洁。
pairmap将传入的函数作用到key和value上。
> t = {fred=10,bonzo=20,alice=4}
> = pairmap(function(k,v) return v end, t)
{4,10,20}
> = pairmap(function(k,v) return k end, t)
{'alice','fred','bonzo'}
(通常第一个值被作为value,第二个作为key)如果函数返回两个值,则第二个值被作为新的key。
 > = pairmap(t,function(k,v) return v+10, k:upper() end)
{BONZO=30,FRED=20,ALICE=14}
 
map2作用到两个表上:
> map2(ops.add,{1,2},{10,20})
{11,22}
> map2('*',{1,2},{10,20})
{10,40}
以上操作都是产生表,reduce则表里的元素都作用到有两个参数的函数,返回一个标量。
 > reduce ('+', {1,2,3})
6
> reduce ('..', {'one','two','three'})
'onetwothree'
 
最后,zip把不同的表里的元素组成新表。
 > = zip({1,2,3},{10,20,30})
{{1,10},{2,20},{3,30}}
查看PL的文档,你会发现tablex和List共享一下函数。如tablex.imap和List.map基本是相同的函数。
这也可以表达为 'f(x) for x' (t),可以让操作更明确。List是一个对table的轻包装,List接口并不强调
使用Penlight的table。
 
二维表操作符
二维表很容易在lua表里表示,如{{1,2},{3,4}},即把行作为子表存储,即A[col][row]。与lua的矩阵库
LuaMatrix类似。pl.array2d并不提供矩阵操作,这是另外库的工作。
iter是ipairs生成器(额外参数指定索引).
 > a = {{1,2},{3,4}}
> for i,j,v in array2d.iter(a,true) do print(i,j,v) end
 1       1       1
 1       2       2
 2       1       3
 2       2       4
 
注意你可以用List(tablex.map(List,a))把2d数组转为list中的list。
map也可作用到arra2d上:
 > array2d.map('-',a,1)
{{0,1},{2,3}}
2d数组按行存储,但是列可以提取。
> array2d.column(a,1)
{1,3}
还有reduce2函数,或者行,或者列。
> array2d.reduce_rows('+',a)
{3,7}
> array2d.reduce_cols('+',a)
{4,6}
和tablex.reduce('*',array.reduce_rows('+',a))等效。
 > array2d.reduce2('*','+',a)
 21    `
与tablex.map2对应,array2d提供了array2d.map2.
 > b = {{10,20},{30,40}}
> a = {{1,2},{3,4}}
 > = array2d.map2('+',2,2,a,b)  -- two 2D arrays
{{11,22},{33,44}}
 > = array2d.map2('+',1,2,{10,100},a)  -- 1D, 2D
{{11,102},{13,104}}
 > = array2d.map2('*',2,1,a,{1,-1})  -- 2D, 1D
{{1,-2},{3,-4}}
 
当然你可以简化算术。有一个2d array 字符串,用合适的方式打印出来。第一步,
给map string.len函数作用到array上;第二步,使用math.max换算列宽;最后,用
stringx.rjust处理这些宽度。
 maxlens = reduce_cols(math.max,map('#',lines))
lines = map2(stringx.rjust,2,1,lines,maxlens)
 
product用来计算1d数组的笛卡尔乘积,返回2d数组。
> array2d.product('{}',{1,2},{'a','b'})
{{{1,'b'},{2,'a'}},{{1,'a'},{2,'b'}}}
 
PL为2d array提供了一系列操作,如swap_rows、swap_cols,第一个是效率是线性的。
remove_row和remove_col是table.remove的特例。还有extract_rows,extract_cols,可以
提取指定的索引。 extract_cols(A,{2,4})仅保存第2列和第4列。
 
List.slice用在1d数组上。slice与其相同,只需提供起始(row,column)。
> A = {{1,2,3},{4,5,6},{7,8,9}}
> B = slice(A,1,1,2,2)
> write(B)
  1 2
  4 5
> B = slice(A,2,2)
> write(B,nil,'%4.1f')
  5.0 6.0
  8.0 9.0
 
write用漂亮的来输出数组,第二个参数为空时,使用标准输出(stdout),也可以使用文件对象,
第三个可选参数用来格式化(用法同string.format)。
parse_range 可以解析电子表格似的范围表示法,如"A1:B2"或“R1C1:R2C2",返回四个number,
返回值可以作为slice的参数。slice依据范围返回合适的数组,如果范围代表row或column,返回
值是1d,其它则2d。
这对iter一样,也可以选择范围。
 > for i,j,v in iter(A,true,2,2) do print(i,j,v) end
 2       2       5
 2       3       6
 3       2       8
 3       3       9
 
new构造一个新的2d数组,可以提供初始化元素,如果参数可调用,加上L前缀,表示utils.string_lambda。
我们可以按照下面的方式,产生一个单位矩阵。
 asserteq(
     array.new(3,3,L'|i,j| i==j and 1 or 0'),
     {{1,0,0},{0,1,0},{0,0,1}}
)
 
注意大多数array2d函数是协变的,即返回类型和传入类型相同。当reshaped或sliced时,任何用data.new或matrix.new将保留数据或矩阵对象。数据对象含有array2d的函数。
 

lua工具库penlight--02表和数组的更多相关文章

  1. lua工具库penlight--01简介

    lua的设计目标是嵌入式语言,所以和其它动态语言(如python.ruby)相比其自带的库缺少很多实用功能. 好在有lua社区有Penlight,为lua提供了许多强大的功能,接下来的几篇博客,我会简 ...

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  9. lua工具库penlight--08额外的库(二)

    执行一系列的参数 类型说明符也可以 是' ('MIN '..' MAX)' 的形式. local lapp = require 'pl.lapp' local args = lapp [[ Setti ...

随机推荐

  1. Report studio交叉表求指定维度和的问题

    场景:有年份,维度值,不求所有的维度值的和,只求自己想要的省份的和,其中有些省份的值可能为空. 交叉表设计: 运行如图示: 下面要求在交叉表下方多出一列,求上海市与河南省的和.然后空白显示0 解决: ...

  2. 【中英】mac电脑清理软件 ToolWiz Mac Boost

    简单介绍: ToolWiz Mac Boost是一款适用于Mac电脑清理加速最好的终极应用, 使您的Mac电脑干净有条理, 执行飞速且稳定.始终保持最佳状态! ToolWiz Mac Boost 运用 ...

  3. centos下docker网络桥接

    停止服务 停止docker0网卡 Ip link set dev docker0 down 删除docker0 Brctl delbr docker0 进入到网卡的配置文件创建桥接网络br0 Brct ...

  4. 解决iOS Xcode 模拟器键盘不弹出

    1. 选中模拟器,在屏幕上方的菜单中找到Hardware->Keyboard 2. 直接快捷键shift+command+k

  5. 使用Bundle进行VIM插件的管理

    http://www.oschina.net/p/vundle git clone https://github.com/gmarik/vundle.git ~/.vim/bundle/vundle ...

  6. linux cut 命令

    cut:以某种方式按照文件的行进行分割 参数列表: -b 按字节选取 忽略多字节字符边界,除非也指定了 -n 标志 -c 按字符选取 -d 自定义分隔符,默认为制表符. -f 与-d一起使用,指定显示 ...

  7. Android WiFi直连 双向通信

    代码地址如下:http://www.demodashi.com/demo/13965.html 原文地址:https://blog.csdn.net/VNanyesheshou/article/det ...

  8. Hibernate基于注解方式的各种映射全面总结

    1. 使用Hibernate Annotation来做对象关系映射 1) 添加必须包: hibernate-jpa-2.0-api-1.0.0.Final.jar 2) 在实体类中添加JPA的标准注解 ...

  9. SQL server 2008定期的备份数据库及删除job

    在SQL Server中出于数据安全的考虑,所以需要定期的备份数据库.而备份数据库一般又是在凌晨时间基本没有数据库操作的时候进行,所以我们不可能要求管理员 每天守到晚上1点去备份数据库.要实现数据库的 ...

  10. 使用TestFlight邀请外部人员測试APP

    怎样使用TestFlight邀请外部人员測试APP 详细过程例如以下: 1.在邀请測试人员的时候.按上线流程打包APP,提交. 2.提交审核,在邀请測试员的时候,你必须先提交审核,苹果会在大约2个工作 ...