Full path to the required value

拜拜、爱过 提交于 2021-01-27 20:11:26

问题


How do I get the full path to the required value in the table? I want to track changes in another table through a proxy table.

I understand that I need to use metatables and __index in it. But I haven't been able to come up with a tracker yet.

Sample table structure:

Objects = {
Panel = { layer = 1, x = 600, y = 328, w = 331, h = 491;
    objects = {
        label = { layer = 1, x = 0, y = 0, text = 'header' };
        Window = { layer = 2, x = 400, y = 100, w = 100, h = 100;
            objects = {
                label = { layer = 1, x = 0, y = 0, text = 'lorem ipsum dorem' };
            };
        };
    };
};
};

Path: Objects.Panel.objects.Window.objects.label.text

I tried to create a metatable for each of the tables and collect the result of each call to __index into a table in order to roughly understand which key and value were retrieved or changed in order to synchronize these values ​​with other tables.


回答1:


This will prove itself to be horrendously slow and memory inefficient. Anyway, you were right on the track: proxy and handle __index and __newindex metamethods to your liking. This being said you also need to track the state of the proxy somehow.

You can try to hide it with some closures and upvalues but the easy way is to store the information directly in the proxy tables:

function make_tracker (o, name)
  local mt = {}
  mt.__index = function (proxy, key)
    local path = {unpack(rawget(proxy, "__path"))} -- Stupid shallow copy
    local object = rawget(proxy, "__to")
    table.insert(path, key)
    if type(object[key]) == "table" then
      return setmetatable({__to = object[key], __path = path}, mt)
    else
      return table.concat(path, ".") .. " = " .. tostring(object[key])
    end
  end
  return setmetatable({__to = o, __path = {name}}, mt)
end

__to fields indicates what proxy should point to and __path is there to cover fields we have trespassed so far. It does a shallow copy, so that one can use subproxies with local variables. name parameter is there to initialize the name of the first table, as you just simply can't know that. You use it like this:

local tObjects = make_tracker(Objects, "Objects")
local subproxy = tObjects.Panel.objects.Window
print(subproxy.objects.label.text)
print(tObjects.Panel.objects.label.text)
print(subproxy.x)

-- prints:
-- Objects.Panel.objects.Window.objects.label.text = lorem ipsum dorem
-- Objects.Panel.objects.label.text = header
-- Objects.Panel.objects.Window.x = 400

Of course, I doubt that appending the path to the original value is what you want. Modify insides of else block:

return table.concat(path, ".") .. " = " .. tostring(object[key])

according to your needs, e.g:

register_tracked_path(table.concat(path, "."))
return object[key]

If you want to handle modification of values you need to extend the metatable with similar __newindex.



来源:https://stackoverflow.com/questions/63577908/full-path-to-the-required-value

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!