在Lua中,如何在不复制的情况下获取数组的尾部?

时间:2012-09-11 15:07:45

标签: lua

我正在使用Lua 5.2,并且为了这个问题,假设这些表专门用作数组。

这是一个返回数组 tail 的函数(数组减去第一个元素):

function tail(t)

   if # t <= 1 then
      return nil
   end

   local newtable = {}
   for i, v in ipairs(t) do
      if i > 1 then
          table.insert(newtable, v)
      end
   end

   return newtable
end 

例如:

  

提示&GT; table.concat(tail({10,23,8}),“,”)

     

23,8

然而,这是通过返回表的新副本来实现的。有没有办法避免创建新表?

我正在寻找等效的C返回指向下一个元素(t++)的指针。有可能吗?

5 个答案:

答案 0 :(得分:9)

如前所述,这通常是不可能的。

但是,使用元表,你可以实现一个tail函数,通过引用原始表来执行你想要的而不复制所有数据。以下适用于Lua 5.2中的大多数操作,但不适用于table.concat

function tail(t)
  return setmetatable({}, {
    __index = function(_, k) return t[k+1] end,
    __newindex = function(_, k, v) t[k+1] = v end,
    __len = function(_) return #t-1 end,
    __ipairs = function(_) return 
      function(_, i)
        if i+1==#t then return nil end
        return i+1, t[i+2] end, 
      t, 0 end,
    __pairs = function(t) return ipairs(t) end,
  })
end

答案 1 :(得分:4)

这是我知道实现tail()的最好方法。它创建了一个新表,但我认为这是不可避免的。

function tail(list)
    return { select(2, unpack(list)) }
end

答案 2 :(得分:2)

  

我正在寻找等效的C返回指向下一个元素的指针(t ++)。有可能吗?

没有。您可能想要的唯一可能的原因是性能。这种功能只能在低级编程语言中找到。 Lua是一种脚本语言:性能不是优先考虑的事情。

只需制作您正在做的另一张桌子,或使用table.remove修改原件。哪个最适合你。请记住:像表和userdata这样的重要大对象都是通过引用存储在Lua中,而不是按值存储。

答案 3 :(得分:2)

Nicol是正确的,你不能引用一个数组的片段,但有一个更容易/更短的方法来做你想做的事情:

function tail(t)
  local function helper(head, ...) return #{...} > 0 and {...} or nil end
  return helper((table.unpack or unpack)(t))
end
然后

print(table.concat(tail({10, 23, 8}), ", "))将打印23,8

(添加table.unpack or unpack使其也适用于Lua 5.2)

答案 4 :(得分:1)

prapin的建议,使用metatables来呈现序列的视图,大致就是我这样做的方式。可能有用的抽象是为定义元表,它可以是返回一对表和偏移索引的0-ary函数 - 我们这里只使用函数来表示元组。然后我们可以定义一个metatable,使该函数的行为类似于表:

do
  local tail_mt = {
    __index = function(f, k) local t, i=f(); return t[k+i] end,
    __newindex = function(f, k, v) local t,i=f(); t[k+1] = v end,
    __len = function(f) local t,i=f(); return #t-i end,
    __ipairs = function(f) 
      local t,i = f () 
      return
        function (_, j)
          if i+j>=#t then 
            return nil
          else
            return j+1, t[i+j+1] 
          end
        end, nil, 0 
      end,
  }
  tail_mt.__pairs = tail_mt.__ipairs -- prapin collapsed this functionality, so I do too

  function tail (t)
    if type(t) == "table" then
      return setmetatable ( function () return t, 1 end, tail_mt )
    elseif type(t) == "function" then
      local t1, i = t ()
      return setmetatable ( function () return t1, i+1 end, tail_mt )
    end
  end
end

使用__index和__newindex元方法,您可以编写诸如f [2] = f [1] +1之类的代码。

虽然这个(未经测试的)代码不会无休止地创建一次性元表,但它可能不如prapin的效率高,因为它会调用thunks(0-ary函数)来获取它们的内容。但是如果您可能对扩展功能感兴趣,比如对序列有更多的一般看法,我认为这样会更灵活。