下面的Lua迭代器是无状态的吗?

时间:2016-08-03 07:25:49

标签: lua iterator

我对无状态迭代器的概念感到困惑。作为练习,我编写了一个迭代器来打印给定字符串的所有非空子串。代码如下。

local function iter(state, i)
    local str = state.str
    if type(str) ~= "string" or str == "" then return nil end
    if state.ending > #str then return nil end

    local start = state.start
    local ending = state.ending

    if start == ending then
        state.ending = ending + 1
        state.start = 1
    else
        state.start = start + 1
    end

    return string.sub(str, start, ending)
end

function allSubstrings(str)
    return iter, { str = str, start = 1, ending = 1 }, nil
end

for substr in allSubstrings("abcd123") do
    print(substr)
end

我使用表{ str = str, start = 1, ending = 1 }作为所谓的不变状态,但我必须在iter本地函数中更改此表中的字段。这个迭代器是无状态还是复杂状态?如果它不是无状态的,有没有办法用无状态迭代器实现这个功能?

谢谢。

3 个答案:

答案 0 :(得分:2)

这不是stateless iterator,而且确实是complex state的迭代器。

在无状态迭代器中,只有一个控制变量,并且它应该在整个迭代器中被视为纯值。

您可以考虑使用闭包来实现此功能。不是无状态的,但确实使用upvalues而不是表查找,这应该更有效。

local function allSubstrings (str)
    if type(str) ~= "string" or str == "" then
        return function () end -- NOP out on first iteration
    end

    local length = #str
    local start, ending = 1, 1

    return function ()
        if ending > length then return nil end

        local st, ed = start, ending

        if start == ending then
            ending = ending + 1
            start = 1
        else
            start = start + 1
        end

        return string.sub(str, st, ed)
    end
end

for substr in allSubstrings("abcd123") do
    print(substr)
end

答案 1 :(得分:2)

PIL-chapter about stateless iterators州:

  

顾名思义,无状态迭代器是一个迭代器,它不会保留任何状态。因此,我们可以在多个循环中使用相同的无状态迭代器,从而避免创建新闭包的成本。

代码表示两个for循环:

f, s, var = pairs( t )
for k,v in f, s, var do print( k, v ) end
for k,v in f, s, var do print( k, v ) end

应该产生相同的输出。这仅在迭代期间不变状态s和迭代器函数f的任何上升值都不会改变时才起作用,否则第二个for循环将具有与第一个循环不同的起始条件。

所以,不,你的迭代器不是无状态迭代器,因为你在迭代期间改变了不变状态。

有一种方法可以使你的迭代器无状态(流行的Lua库luafun广泛使用这种技术):在循环控制变量var中保持所有可变状态(即使你需要分配)每个分配步骤中都有一个新表:

local function iter( str, var )
  if type( str ) ~= "string" or str == "" then return nil end
  if var[ 2 ] > #str then return nil end
  local start, ending = var[ 1 ], var[ 2 ]
  if start == ending then
    return { 1, ending+1 }, string.sub( str, start, ending )
  else
    return { start+1, ending }, string.sub( str, start, ending )
  end
end

function allSubstrings2( str )
  return iter, str, { 1, 1 }
end

for _,substr in allSubstrings2( "abcd123" ) do
  print( substr )
end

缺点是第一个迭代变量var(循环控制变量)对于迭代器的用户没有任何用处,因为你必须为每个迭代步骤分配一个表,"避免创造新封闭的成本"对于另一个循环并不重要。

luafun无论如何都会这样做,因为它没有能力重新创建迭代器元组(它是通过外部代码中的函数参数传递的),对于某些算法,你绝对需要多次运行相同的迭代

这种方法的另一种选择是将所有可变状态集中在"不变状态" sf的所有upvalues必须是不可变的),并提供一种复制/克隆此状态以供以后迭代的方法:

f, s, var = allSubstrings("abcd123")
s2 = clonestate( s )
for str in f, s, var do print( str ) end
for str in f, s2, var do print( str ) end

这仍然是不是无状态迭代器,但它比具有堆分配的循环控制变量的无状态迭代器更具内存效率,并且它允许您重新启动迭代。

答案 2 :(得分:0)

PIL书中称“复杂状态的迭代器” http://www.lua.org/pil/7.4.html

相关问题