类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. 如何在Visual Studio(VS)2012里使用libsvm工具箱

    原文:http://blog.csdn.net/u014691453/article/details/40393137 软件版本: Visual Studio版本:VS2012 (注:使用方法在 VS ...

  2. Hibernate_8_Person和IdCard实例_一对一关系:基于外键

    1)建立Person类: public class Person { private Integer id; private String name; private IdCard IdCard; p ...

  3. Dragon of Loowater

    option=com_onlinejudge&Itemid=8&page=show_problem&problem=2267" style="color:b ...

  4. GameCenter 使用指南

    原地址:http://www.cocoachina.com/gamedev/misc/2010/1022/2229.html GameCenter 为单机游戏为主的 iPhone 游戏平台引入了社会化 ...

  5. python urllib2 httplib HTTPConnection

    httplib实现了HTTP和HTTPS的客户端协议,一般不直接使用,在python更高层的封装模块中(urllib,urllib2)使用了它的http实现. import httplib conn  ...

  6. VMware - "Determining IP Information for eth0...Failed

    Linux ifup eth0 出现错误: Dertermining IP information for eth0....failed - no link present check cable D ...

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

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

  8. 【微信小程序】再次授权地理位置getLocation+openSetting使用

    写在前面:在tarbar主页面,再次授权JS代码请放在onshow里面:在详情页(非一级主页面),再次授权JS代码请放在onReady里面,具体原因我前面博客讲了的. 我们知道: 1.微信的getLo ...

  9. iOS开发-代码片段(Code Snippets)提高开发效率

    简介 在 XCode4 引入了一个新特性,那就是“代码片段(Code Snippets)”.对于一些经常用到的代码,抽象成模板放到 Code Snippets 中,使用的时候就只需要键入快捷键就可以了 ...

  10. 【LeetCode】33. Search in Rotated Sorted Array (4 solutions)

    Search in Rotated Sorted Array Suppose a sorted array is rotated at some pivot unknown to you before ...