快速掌握Lua 5.3 —— "Iterators"和"Generic for"

Q:什么是”iterator”?

A:一种允许你访问一个集合中的元素的构造。在Lua中用函数来表示”iterator”,每次调用这个函数,函数就返回集合中的下一个元素。

Q:如何创建以及使用”iterator”。

A:使用”Closure”就可以创建”iterator”。

-- 一个类似于ipairs()的iterator,但他只返回table中的value。
function list_iter(t)
    local i = 0
    return function()
        i = i + 1
        if i <= #t then return t[i] end
    end
end

t = {10, 20, 30}
-- 用"while"使用这个"iterator"。
iter = list_iter(t)    -- creates the iterator
while true do
    local element = iter()   -- calls the iterator
    if element == nil then break end
    print(element)
end

-- 用"Generic for"使用这个"iterator"。
for element in list_iter(t) do
    print(element)
end

可以看出,”iterator”与”Generic for”配合的更好。省去了iter变量保存这个”iterator”,省去了判断break条件,省去了显式调用”iterator”。实际上”Generic for”还做了更多的事情,在之后的”Q & A”总会看到如何创建一个高效的”iterator”,其中就用到了”Generic for”额外的特性。

Q:”Generic for”的内部是如何实现的?

A:之前有提到过“Generic for”的语法,

for var1, var2, ..., var_n in exp do 
    dosomething
end

实际上其内部做了如下的工作,

do
  --[[ 首先计算"in"后面的"exp"。
       实际上"Generic for"允许"exp"返回三个值,他们的含义分别是:
       _f: "iterator function",
       _s: "invariant state",
       _var: "control variable"。]]
  local _f, _s, _var = exp
  while true do
    --[[ 以"invariant state"和"control variable"作为参数调用"iterator function"。
         如果"iterator"还有值,则"var_1"到"var_N"都会得到值,
         否则"iterator"返回"nil",即"var_1"到"var_N"都会得到"nil"]]
    local var_1, ... , var_n = _f(_s, _var)
    _var = var_1    -- "control variable"得到"var_1"的值。
    --[[ 如果"control variable"得到的是"nil",
         说明"iterator"中已经没有值了,那么跳出"for"循环。]]
    if _var == nil then break end
    --[[ 否则"control variable"得到的不是"nil",
         说明"iterator"中还有值,那么执行"for"中给出的代码。]]
    dosomething    -- 这个"dosomething"是上面"Generic for"循环中的那个"dosomething"。
  end
end

结合一个实际的例子来理解会更容易一些,

-- 用Lua实现ipairs()。
function iter(a, i)    -- 每次都会传递"invariant state"和"control variable"。
  i = i + 1    -- 更新"control variable"。
  local v = a[i]
  if v then
    --[[ 首先返回新的"control variable"
         (因为"Generic for"的内部实现中"control variable"接收的是"var_1"的值),
         再返回其他的值。]]
    return i, v
  end
end

function ipairs(a)
  --[[ iter是"iterator function",
       a是"invariant state",
       0是"control variable"的初始值。
       结合上面"Generic for"的内部实现,就能明白这3个变量的意义了。]]
  return iter, a, 0
end

Q:什么是”stateless iterator”?

A:不需要自己保存”iterator”的状态以及”control variable”,所有均由”Generic for”负责管理。

Q:什么是”complex state iterator”?

A:需要自己保存”iterator”的状态以及,并且在调用”iterator”的过程中其状态会变化。一般会使用一个”table”来保存会变化的状态(同时,一般也将”iterator”的”control variable”放入其中,所以此种模式的”iterator”一般不需要”Generic for”的”control variable”),然后将此”table”作为”invariant state”。

Q:如何创建一个高效的”iterator”?

A:首先应该尝试写一个”stateless iterator”,因为”Generic for”内部会帮助你管理”iterator”的状态,每一次调用”iterator”也没有额外的开销。如果这种模式不合适,那么就要尝试写一个”closure”,”closure”的开销比”table”的开销小,同时”closure”的访问速度也比”table”的访问速度快。如果这种模式依旧不合适,你才要考虑写一个”complex state iterator”。最后如果实在没办法了,你可以使用”coroutine”(在此不提,之后会提到)来创建”iterator”,他的功能十分强大,但是开销也不小。
“stateless iterator”的典型例子是上面用Lua实现的ipairs()。
接下来,通过一个找出输入的字符串中每一个单词的例子,展示如何创建”closure”模式以及”complex state iterator”模式的”iterator”。

--[[ "closure" mode.
     每次使用时都会创建一个"closure"(自己保存"line"变量和"pos"变量)。]]
function allwords ()
  local line = io.read()  -- current line
  local pos = 1           -- current position in the line
  return function ()      -- iterator function
    while line do         -- repeat while there are lines
      local s, e = string.find(line, "%w+", pos)
      if s then           -- found a word?
        pos = e + 1       -- next position is after this word
        return string.sub(line, s, e)     -- return the word
      else
        line = io.read()  -- word not found; try next line
        pos = 1           -- restart from first position
      end
    end
    return nil            -- no more lines: end of traversal
  end
end

-- 用"Generic for"使用这个"iterator"。
for word in allwords() do
  print(word)
end
--[[ "complex state iterator" mode.
     "line"变量和"pos"变量存储在"state"这张"table"中,
     "state"作为"Generic for""invariant state"。]]
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

function allwords ()
  local state = {line = io.read(), pos = 1}
  return iterator, state
end

-- 用"Generic for"使用这个"iterator"。
for word in allwords() do
    print(word)
end

附加:

1、在连续调用”iterator”的过程中,”iterator”需要保存一个“状态”,通过这个“状态””iterator”才能知道如何获取下一个元素,“Closures”能很好的完成这个任务(“Closures”是一个能够访问包含他的函数中的局部变量的函数)。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值