lua系统学习13-自定义创建一个迭代器
使用Closure特性自己创建迭代器
function IteratorDIY(t1)
function iteratorTableValue(t)
local i=0
return function () i=i+1; return t[i] end
end
for i in iteratorTableValue(t1) do
print(i)
end
end
IteratorDIY({0,1,2,3,4,5,6,'\\0'});
0
1
2
3
4
5
6
\0
在上面案例中 我们使用了Closure的特性,在一个函数内部定义了局部变量,在尾调用处返回一个匿名函数 在匿名函数中调用了这个在IteratorDIY中的局部变量。还记得之前说到Closure的时候,说这样的写法在匿名函数中会把在IteratorDIY的局部变量看作一个非局部的变量。对于函数内的匿名函数来说它既不是全局变量也不是局部变量,但对于IteratorDIY函数来说却又是局部变量。
接着我们使用for 接收变量 in initializeList do end
在这个示例中initializeList 会返回一个执行函数。这里呢,执行函数会在每次循环前被执行并把输出结果返回给接收变量,直到执行函数返回nil的时候 也就接收变量=nil的时候结束循环。
通过这样我们就实现了一个迭代器。当然我们也可以模拟实现IPair
function IteratorDIY2(t1)
function iteratorTablePair(t)
local i=0
return function () i=i+1;
if i>#t then
return nil,nil
else
return i,t[i]
end
end
end
for i,v in iteratorTablePair(t1) do
print(i,v)
end
end
IteratorDIY2({0,1,2,3,4,5,6,'\\0'});
for in 循环结束的条件 必须要第一个接收变量为nil的时候才结束
上例中使用条件判断 当“当前累计的i大于长度的时候就返回nil”。
迭代io中所有单词与数字
function IteratorAllWords()
function wordIterator()
for word in allWords() do
print(word)
end
end
function allWords()
local line=io.read()--他本身也是个迭代器
local pos=1--- 开始位置
return function()
while line do
local s,e= string.find(line,"%w+",pos)
if s then
pos=e+1---s是字符串的开始索引 e是字符串的长度
return string.sub(line,s,e)
else
line=io.read()
pos=1
if line=='quit' then
return nil
end
end
end
return nil
end
end
wordIterator()
end
IteratorAllWords()
asdsa23.fg
quit
asdsa23
fg
以上的这些示例 都是通过Closure会保存非局部的变量的特性实现的。但是如果每次循环迭代都会创建一个新的Closure就意味着要考虑性能问题了,那么有没有什么好的方案代替呢?
下面不使用Closure,利用For in会接收函数结果的特性来创建一个迭代器。
利用for-in特性创建无状态迭代器
function ForVar(t)
function iteratorTablePair(t,i)
return function () i=i+1;
if i>#t then
return nil,nil
else
return i,t[i]
end
end
end
i=0
for i,v in iteratorTablePair(t,i) do
print(i,v)
end
end
ForVar({2,3,4,5})
1 2
2 3
3 4
4 5
前面说过For varList in initializeList do
for在初始化时实际上会保存着initializeList执行的3个值:1.一个迭代器函数,2.恒定变量(表),3.一个控制变量。迭代器工厂用于返回一个迭代器函数,恒定变量在这里就是表,是为for提供的数据源。上面这个案例里,initializeList直接返回了迭代器函数,恒定变量和控制变量没有返回,所以默认为nil,但不影响运行。因为我们已经将t和i的引用了传入initializeList 当中了,for每次循环都会把t和i参数自动代入迭代器函数中运行。
varlist会接收迭代器函数的结果 当varlist[0]的结果为nil,循环为nil。所以我们把varlist中第一个变量称之为循环的控制变量,控制循环的结束。
上面迭代单词的示例中使用的是Closure的非局部变量的保存来实现迭代器,在这个示例中我们可以通过参数传值的方式代替Closure的非局部变量。利用到Closure无非就是能保存上一次变量计算的值,那么我们直接把返回过来的值再传回去不就好了。所以就有了 for i in func(t,i) do的实现。其实还可以更简洁,for in其实在循环过程内部保存了迭代器函数
用Ipair来举例 实际lua中的ipair不会像我们上面写的那些例子那样,它不会有closure的开销。
可能解释的不够清楚,看一下下面这个ipair示例就明白了
function ipairNoColsureImplent()
local function ipairIter(t,i)
i=i+1
local v=t[i]
if v then
return i,v
end
end
function dummyIpair(t)
return ipairIter,t,0
end
t={"a","b","c"}
for i,v in dummyIpair(t) do
print(i,v)
end
end
ipairNoColsureImplent()
1 a
2 b
3 c
dummyIpair返回3个值,第一个是迭代器函数(for每次循环要执行的函数)、第二个是恒定变量,第三是控制变量。
实际上dummyIpair在for初始化时候执行而不是参与循环时候执行,真正参与循环的是dummyipair返回的函数。所以在dummyipair中返回恒定变量、控制变量 与 for varlist in ipairItera,恒定变量,控制变量 do。 这样的写法是一样的。因为在for in do 中 in的优先级是最高的,可以理解为迭代器初始化,然后才进行循环。
示例:
function ipairNoColsureImplent2()
local function ipairIter(t,i)
i=i+1
local v=t[i]
if v then
return i,v
end
end
t={"a","b","c"}
for i,v in ipairIter,t,0 do
print(i,v)
end
end
我们可以称dummyipair为迭代器的工厂,用来创建迭代器的。
在lua中会把恒定变量与控制变量的值,作为参数在第一次执行迭代器即ipairIter函数时候传入进去。
因为迭代器会返回控制变量的值,所以后面for in会自动把恒定变量和控制变量i再作为参数传入迭代器进行迭代。(此步骤是由for in 传入参数,不是从迭代器的工厂传入)
pair的实现
function pairNoColsureImplent()
function dummyPair(t)
return next,t,nil
end
t={"a","b","c",1,2}
for i,v in dummyPair(t) do
print(i,v)
end
end
pairNoColsureImplent()
pair中用到了lua自带的next函数。用来访问table下一个元素
就有点像地址下移似的。这个直接通过操作指针应该也是可以的。
复杂的迭代器
来源:麦瑞克博客
链接:https://www.playcreator.cn/archives/programming-life/lua/3455/
本博客所有文章除特别声明外,均采用CC BY-NC-SA 4.0许可协议,转载请注明!