Lua表格作为数据库的前端

我正试图把一个数据库实现为一个Lua表格。使用元表,这个表格将是空的,当从表格请求或修改一个元素时,它将返回或修改数据库中的元素。除了被请求的部分,数据库本身永远不会被加载到内存中。它应该像一个表格一样被程序与之交互(因为它是一个表格)。该表格,因为它只是一个“前端”,将会把修改后的数据保存到数据库中(而不是将该元素值定义在表格中)。

在一个没有子表的表格中,这很容易实现。我正在试图让它适用于多层次、深度可变的表格。 (附:我考虑使用的数据库是redis。理想情况下,只需更改基本操作语法,就可以为任何数据库或类似数据库的服务器实现它。)

由于Lua元表的行为,当在顶层修改(或创建,如果使用代理)时才会使用“__newindex”方法。当调用是对子表格中的某些东西进行修改时,即使调用是读取操作,“__index”方法也会被调用。因此,我正在尝试编写一个“__index”方法,当请求一个表格时,它会返回另一个伪代理(代理数据库而不是另一个表格),行为相同,只是代理是为顶层内的表格/数组/列表而服务的,等等,深度不定。我很困惑。

我的问题是:

  • 这以前实现过吗?
  • 我是否应该关注“正确”的类系统而不是我现在所做的?
点赞
用户3979429
用户3979429

当你创建一个表时,只需在伪造的表中添加一个空表并设置其metatable:

local fake = {}
do
   local lookup = {} --将用它来避免使用很多metatable

   local real = {}

   local meta
   meta = {
      __index = function(self,i)
         return rawget(lookup[self], i)
      end,
      __newindex = function(self,i,v)
         rawset(lookup[self], i, v)
         if type(v) == "table" then
             rawset(self, i, setmetatable({},meta))
             lookup[self[i]] = v
         end
      end
   }

   setmetatable(fake, meta)
   lookup[fake] = real
end

fake[1] = "hello"
print(fake[1])
print(rawget(fake, 1))
fake.x = {"hi"}
print(fake.x)
print(rawget(fake, 'x')) --这仍然会打印一个表,因为实际上确实有一个,但实际上它遵守了我们的规则
print(fake.x[1])
print(rawget(fake.x, 1))
fake.x.y = "aha"
print(fake.x.y)
print(rawget(fake.x, 'y'))

这种方法唯一的警告是他们可以直接修改数据库,如下所示:

fake.myvalue = {}
fake.myvalue = 5

另一种方法是按需包装:

local fake = {}
do
   local lookup = {} --将用它来避免使用很多metatable
   local cache = {} --将用于避免使用大量新对象

   local real = {}

   local meta
   meta = {
      __index = function(self,i)
         local val = rawget(lookup[self], i)
         if type(val) == "table" then
            if cache[val] then
                return cache[val]
            else
                local faker = {}
                lookup[faker] = val
                cache[val] = faker
                return setmetatable(faker, meta)
             end
         else
            return val
         end
       end,
      __newindex = function(self,i,v)
         rawset(lookup[self], i, v)
      end
   }

   setmetatable(fake, meta)
   lookup[fake] = real
end

fake[1] = "hello"
print(fake[1])
print(rawget(fake, 1))

fake.x = {"hi"}
print(fake.x)
print(rawget(fake, 'x')) --这仍然会打印一个表,因为实际上确实有一个,但实际上它遵守了我们的规则
print(fake.x[1])
print(rawget(fake.x, 1))
fake.x.y = "aha"
print(fake.x.y)
print(rawget(fake.x, 'y'))

这样完全避免了直接修改问题。

2016-02-27 12:31:05