泛型for原理
迭代器是一种可以遍历集合中所有元素的机制,在Lua中通常将迭代器表示为函数,每调用一次函数,就返回集合中“下一个”元素。每个迭代器都需要在每次成功调用之间保持一些状态,这样才能知道它所在的位置及如何步进到下一个位置,closure就可以完成此项工作。下面的示例是列表的一个简单的迭代器:
function values(t) local i = 0 return function() i = i + 1; return t[i] end end
循环调用:
t = {10, 20, 30} iter = values(t) while true do local el = iter() if el == nil then break end print(el) end
泛型for调用
for el in values(t) do print(el) end
泛型for为一次迭代循环做了所有的簿记工作。它在内部保存了迭代器函数,并在每次迭代时调用迭代器,在迭代器返回nil时结束循环。实际上泛型for保存了3个值:迭代器函数f、恒定状态s、控制变量a。for做的第一件事就是对in后面的表达式求值,并返回3个值供for保存;接着for会以s和a来调用f。在循环过程中控制变量的值依次为a1 = f(s, a0),a2 = f(s, a1),依次类推,直至ai为nil结束循环。
先看一段代码
for element in list_iter(t) do print(element) end
在不往下看之前,我们可以先试图根据我们已有的知识结构去理解这段代码。如果这样,list_iter(t)应该返回一个类似集合的东西,而我们已经知道实际上只返回了一个匿名函数,也就是迭代器。当然,每次调用迭代器都可以得到一个元素,迭代器的所有的结果倒是可以看成一个集合。因素齐了,我们需要一个逻辑上的解释,这个逻辑就是 泛型for的语义。
先看文法规定:
for <var-list> in <exp-list> do <body> end
整个过程是这样的:
首先,初始化,计算 in 后面表达式的值,表达式应该返回 泛型for 需要的三个值:迭代函数、状态常量、控制变量;与多值赋值一样,如果表达式返回的结果个数不足三个会自
动用nil 补足,多出部分会被忽略。
第二,将状态常量和控制变量作为参数调用迭代函数(注意:对于 for 结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
第三,将迭代函数返回的值赋给变量列表。
第四,如果返回的第一个值为 nil 循环结束,否则执行循环体。
第五,回到第二步再次调用迭代函数。
更具体地说:
for var_1, ..., var_n in explist do block end
等价于
do local _f, _s, _var = explist while true do local var_1, ... , var_n = _f(_s, _var) _var = var_1 if _var == nil then break end block end end
泛型 for 在自己内部保存三个值:迭代函数、状态常量、控制变量。
迭代器的状态
无状态的迭代器本身不保存任何状态,for循环只会用恒定状态和控制变量来调用迭代器函数。这类迭代器典型例子就是ipairs,下面是ipairs的Lua实现:
local function iter(s, i) i = i + 1 local v = s[i] if v then return i, v end end function ipairs(s) return iter, s, 0 end
当for循环调用ipairs(list)时,会获得3个值,然后Lua调用iter(list, 0)得到list, list[1],调用iter(list, 1)得到list, list[2],知道得到一个nil为止。
虽然泛型for只提供一个恒定状态和一个控制变量用于状态的保存,但有时需要保存许多其他状态。这时可以用closure来保存,或者将所需的状态打包为一个table,并保存在恒定状态中。
闭包、迭代器和泛型for
到现在,Lua为我们准备了三块积木:闭包、泛型for和迭代器。一个循环,我们可以利用闭包+迭代器,也可以使用泛型for+迭代器。那我们该怎么取舍呢?Lua也给出了建
议。
function iter (a, i) i = i + 1 local v = a[i] if v then return i, v end end function ipairs (a) return iter, a, 0 end for i, v in ipairs(a) do print(i, v) end
这种情况是Lua最推荐的,迭代器不依赖upvalue,不产生闭包,状态常量和控制变量借助泛型for保存,通过迭代器的参数传递给了迭代器。
再给一个书中的例子:
local iterator -- to be defined later function allwords() local state = {line = io.read(), pos = 1} return iterator, state end function iterator (state) while state.line do -- repeat while there are lines -- search for next word local s, e = string.find(state.line, "%w+", state.pos) if s then -- found a word? -- update next position (after this word) state.pos = e + 1 return string.sub(state.line, s, e) else -- word not found state.line = io.read() -- try next line... state.pos = 1 -- ... from first position end end return nil -- no more lines: end loop end
这样好不好呢,Lua给的答案是否定的。书中有一段话说得很清楚:
我们应该尽可能的写无状态的迭代器,因为这样循环的时候由for 来保存状态,不需要创建对象花费的代价小;如果不能用无状态的迭代器实现,应尽可能使用闭包;尽可能不
要使用table 这种方式,因为创建闭包的代价要比创建table 小,另外Lua 处理闭包要比处理table 速度快些。
Lua,迭代器,for
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]