返回首页

教程导航

返回首页

🏷️ 标签(1)

🗂️ 分类(1)

第 8 章 迭代器

第 8 章 迭代器

迭代器把“逐个产生值”的细节封装起来。Lua 使用泛型 for 驱动迭代器,语言内置的 pairs 和 ipairs 已经覆盖了大多数表遍历场景。

泛型 for 的机制

泛型 for 接收三个值:迭代函数、状态和初始控制值。每轮调用迭代函数,直到第一个返回值为 nil:

local function iterator(state, control)
  local nextValue = control + 1
  if nextValue <= state.limit then
    return nextValue, nextValue * nextValue
  end
end

for value, square in iterator, { limit = 4 }, 0 do
  print(value, square)
end

实际开发中通常直接使用库函数或自定义闭包迭代器,不需要手动构造三元组。

pairs 与 ipairs

pairs 遍历全部键值对,顺序未定义:

local data = { a = 1, b = 2, c = 3 }

for key, value in pairs(data) do
  print(key, value)
end

ipairs 从键 1 开始,遇到第一个 nil 停止:

local values = { "a", "b", "c" }

for index, value in ipairs(values) do
  print(index, value)
end

判断“是否存在某键”时应使用 nil 比较,不要依赖真假转换:

if data.a ~= nil then
  print("存在 a")
end

自定义迭代器

闭包可以保存遍历状态:

local function range(first, last, step)
  local current = first - step

  return function()
    current = current + step
    if current <= last then
      return current
    end
  end
end

for value in range(1, 5, 1) do
  print(value)
end

遍历树或链式结构时也可以使用闭包:

local function stackItems(root)
  local stack = { root }

  return function()
    local node = table.remove(stack)
    if not node then
      return nil
    end

    for index = #node.children, 1, -1 do
      stack[#stack + 1] = node.children[index]
    end

    return node.value
  end
end

无状态迭代器

无状态迭代器只依赖状态和控制变量,不捕获可变外部状态。它更容易复用,也便于推理:

local function digits(text)
  return function(source, position)
    local startIndex, endIndex = source:find("%d+", position)
    if not startIndex then
      return nil
    end
    return tonumber(source:sub(startIndex, endIndex)), endIndex + 1
  end, text, 1
end

for number in digits("a12b345c") do
  print(number)
end

当迭代器需要读取文件、网络或复杂对象时,有状态闭包通常更直观;当迭代完全由控制变量决定时,无状态迭代器更合适。

使用 coroutine.wrap 简化生成器

local function linesFromText(text)
  return coroutine.wrap(function()
    for line in text:gmatch("[^\n]+") do
      coroutine.yield(line)
    end
  end)
end

for line in linesFromText("a\nb\nc") do
  print(line)
end

协程的详细用法会在第 13 章展开。这里只需要知道它可以把复杂生成逻辑写成顺序代码。

上一章:表 | 下一章:字符串与模式匹配