为什么这不是一个有效的表迭代器?

我在Lua课程中看到了这个例子:

function fromto(a, b)
  return
    function(state, seed)
      if (seed >= state) then return nil
      else return seed+1 end
    end, b, a-1
end

它返回从a到b(包括a和b)连续的整数。因此,我尝试通过编写以下表迭代器来应用相同的逻辑:

function values(t) -- t是一个表
  return
    function(state, seed)
      return state[seed+1]
    end, t, 0
end

它正确返回第一个值,但随后抛出错误,指出我不能在一个字符串值( seed )上进行算术运算。但是,接收到的值 seed 不是整数值 0 吗?发生了什么?

由于第一个示例中没有递增过程(例如 a = a + 1 ),我认为Lua可能在后台处理它......但如果它不这样做,那么可能就是我困惑的原因。

点赞
用户501459
用户501459

seed 是你迭代函数的第二个参数而不是一个好的名称,它实际上是控制变量。当你从 values 返回 0 时,你才 "种下" 它,但是在循环期间它的值会改变:它将成为迭代函数返回的第一个值。

你从迭代器函数返回了一个字符串 (return state[seed+1]),因此下次调用迭代器函数时,它会被传递该字符串。你试图对其进行数学计算,然后就...崩溃了。

通用的 for 循环需要三个参数:一个迭代器函数、一个不变的状态和一个初始的控制值。迭代器使用状态和控制值进行调用。迭代器然后 返回 下一个控制值,或者返回 nil 表示迭代结束。

t = {"foo","bar","zip","zap"}

local function iteratorFunction (state, index)
  index = index + 1
  local val = state[index]
  if val == nil then return nil end
  return index, val
end

for k, v in iteratorFunction, t, 0 do
    print(k,v)
end

因此,对 iteratorFunction 的第一次调用接收到的是 t0 作为参数。对 iteratorFunction下一次 调用会得到 t 和 _从 iteratorFunction 返回的第一个值_,以此类推。

当你编写一个 "generate" 函数像 values 一样,你仅仅是返回了通用 for 循环所需的三个初始值,这样在使用该迭代器时你的代码更简洁:

function values(t)
    local function iteratorFunction (state, index)
      index = index + 1
      local val = state[index]
      if val == nil then return nil end
      return index, val
    end
    return iteratorFunction, t, 0 -- 和上面循环中使用的三个值相同
end

for k, v in values(t) do
    print(k,v)
end

通用 for 循环仅需要迭代器函数作为必需参数。不变的状态和控制变量可以为 nil,如果你的迭代逻辑在一个闭包中完成,你可能会这么做:

function values(t)
    local index = 0
    -- 我们的迭代器函数是一个绑定到 `index` 和 `t` 的闭包
    local function iteratorFunction()
      index = index + 1
      local val = t[index]
      if val == nil then return nil end
      return index, val
    end
    return iteratorFunction, t, 0 -- 和上面循环中使用的三个值相同
end

for k, v in values(t) do
    print(k,v)
end

如果你将 return index, val 更改为 return valvalues(t) 现在将只迭代 t 中的值。我们之前无法这样做,因为我们需要返回一个控制变量供下次循环迭代使用。但是有了闭包,我们可以通过一个绑定到闭包的变量(即 "upvalue")来维护控制变量。

2015-08-09 20:13:55
用户2546626
用户2546626

你可以使用闭包来使用另一种迭代器形式:

local function values(t)
    local i = 0
    return function()
        i = i + 1
        return t[i]
    end
end

for x in values({1, 2, 3}) do
    print(x)
end

1 2 3

一个迭代器会一直执行直到返回nil。 一个无效的table字段(位于最后一个之后)始终为 nil,所以只需要继续执行即可。

在你的示例中使用传递计数器值的表单在更新最后计数器值方面使用返回值,通常不会被使用。你使用table条目字符串更新了seed,所以下一次执行会失败。

顺便提一下:你的values函数与ipairs相同。

2015-08-09 20:14:18