迭代器教程

lua-users home
wiki

for 语句迭代器在 ForTutorial 中有介绍。以下是一些关于编写自定义迭代器的笔记。

算法

我们可以编写自己的迭代器,供 for 语句调用。以下 Lua 伪代码描述了 for 语句如何使用迭代器。

-- Equivalent to "for var1, ���, varn in explist do block end"
do
  local iterator, state, var1 = explist
  local var2, ... , varn
  while true do
    var1, ..., varn = iterator(state, var1)
    if var1 == nil then break end
      -- for block code
  end
end

状态和当前键值被传递给迭代器。迭代器返回键的新值和任何其他值,例如迭代器生成的值。如果返回 nil,则 for 循环终止。

简单示例

以下迭代器将返回一系列平方值。当我们不返回值(即 n>=state)时,Lua 返回 nil,从而终止迭代。请注意,迭代器为给定迭代返回序列中的下一个值。我们使用 state 来保存我们希望执行的迭代次数。

> function square(state,n) if n<state then n=n+1 return n,n*n end end

以下是调用迭代器的 for 语句

> for i,n in square,5,0 do print(i,n) end
1       1
2       4
3       9
4       16
5       25

我们可以将上面的示例包装起来(类似于 pairs())并提供一个 squares(nbvals) 迭代器构造函数。例如:

> function squares(nbvals) return square,nbvals,0 end  -- iterator,state,initial value

现在我们可以像调用 pairs() 一样调用它

> for i,n in squares(5) do print(i,n) end
1       1
2       4
3       9
4       16
5       25

复杂示例

以下迭代器类似于 ipairs,但允许迭代多个表。

function ipairs(...)
  local t = {...}
  local tmp = {...}
  -- if nothing to iterate over just return a dummy iterator
  if #tmp==0 then
    return function() end, nil, nil
  end
  local function mult_ipairs_it(t, i)
    i = i+1
    for j=1,#t do
      local val = t[j][i]
      if val == nil then return val end
      tmp[j] = val
    end
    return i, unpack(tmp)
  end
  return mult_ipairs_it, t, 0
end

local t1 = {'a', 'b', 'c', 'd', 'e'}
local t2 = {'A', 'B', 'C', 'D', 'E', 'F'}

for k,v1,v2 in ipairs(t1, t2) do
  print(k,v1,v2)
end

数论示例

此迭代器生成其参数的素数因子。它依赖于一个显而易见的事实,即非零整数大于 1 的最小正因子必须是素数。

primdiv = function (n)
   assert(n ~= 0)
   if n < 0 then n = -n end
   local f = function (s,v) -- s not used
                   local p
                   if n == 1 then return end
                   while n%v > 0 and v*v < n do
                    if v == 2 then v = 3
                    else v = v + 2 end
                   end -- while
                   if n%v == 0 then
                    n = n/v; return v
                   end
                   if v*v > n then
                    p = n
                    n = 1; return p
                   end
                 end -- function
     return f,nil,2
    end -- function

 for p in primdiv(84) do io.write(p," ") end --> 2 2 3 7

RiciLake 说:我忍不住要重写它

function primdiv(n)
  assert(n ~= 0)
  if n < 0 then n = -n end
  local function f(_, v)
    if n > 1 then
      while n%v > 0 do
        v = v + (v == 2 and 1 or 2)
        if v*v > n then v = n end
      end -- while
      n = n / v
      return v
    end -- if
  end -- function
  return f,nil,2
end -- function primdiv

for p in primdiv(84) do io.write(p," ") end --> 2 2 3 7

GavinWraith 说:这要好得多。但是,我认为使用 function primdiv(n) 等同于美沙酮,用于一级语言瘾君子。

其他示例

Lua 中的 ipairs

function ipairs(t)
  local function ipairs_it(t, i)
    i = i+1
    local v = t[i]
    if v ~= nil then
      return i,v
    else
      return nil
    end
  end
  return ipairs_it, t, 0
end

Lua 中的逆序 ipairs

function ripairs(t)
  local max = 1
  while t[max] ~= nil do
    max = max + 1
  end
  local function ripairs_it(t, i)
    i = i-1
    local v = t[i]
    if v ~= nil then
      return i,v
    else
      return nil
    end
  end
  return ripairs_it, t, max
end

-- from the end backwards
function ripairs(t)
  local function ripairs_it(t,i)
    i=i-1
    local v=t[i]
    if v==nil then return v end
    return i,v
  end
  return ripairs_it, t, #t+1
end

-- traversing the whole 'array'
function ripairs(t)
  idx={}
  for k,v in pairs(t) do
    if type(k)=="number" then idx[#idx+1]=k end
  end
  table.sort(idx)
  local function ripairs_it(t,_)
    if #idx==0 then return nil end
    k=idx[#idx]
    idx[#idx]=nil
    return k,t[k]
  end
  return ripairs_it, t, nil
end

t1 = {'a', 'b', nil, 'd', 'e', nil}
for k,v in ripairs(t1) do print(k,v) end
--> 5 e
--> 4 d
--> 2 b
--> 1 a

最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2009 年 12 月 9 日下午 12:03 GMT (差异)