Как регистрировать обращения / записи во вложенных таблицах? - PullRequest
2 голосов
/ 26 сентября 2019

Как часть проекта, над которым я работал, я хотел иметь возможность распечатывать каждый раз, когда к таблице обращаются или пишут, и к чему обращались / записывали в таблице.Посмотрев это, я нашел this , в котором описывалось, как отслеживать каждый раз, когда к таблице обращаются / обновляют, используя прокси-таблицу и метаметоды __index и __newindex.Однако предоставляемый ими код не отображает должным образом то, что происходит, если в качестве прокси-таблицы используется вложенная таблица.Предположим, я пишу следующий код, адаптированный из предыдущего:

mt = {}
function mt.__index(self, key)
  print('accessing key '..key)
  return self.proxy[key]
end
function mt.__newindex(self, key, value)
  print('setting key '..key..' to value '..tostring(value))
  self.proxy[key] = value
end

function setproxy(t)
  new_t = {proxy = t}
  setmetatable(new_t, mt)
  return new_t
end

t = {
  a = 1,
  b = 2,
  c = {
    a = 3,
    b = 4,
  },
}

t = setproxy(t)
t.a = 2 -- prints "setting key a to value 2" as expected
t.c.a = 4 -- prints "accessing key c", nothing else

Проблема здесь в том, что __index вызывается для доступа к ключу c, и он возвращает значение в прокси-таблице, ноон не имеет такой же метатабельности, поэтому он не регистрирует запись в t.c.Я хотел бы, чтобы во втором задании было напечатано что-то вроде setting key c.a to value 4, но я не совсем уверен, с чего начать на самом деле реализацию такой вещи.

После долгих раздумий, я думаю, вы могли бы сделать это, если бы каждый ключ, имеющий табличное значение, был также другой прокси-таблицей, но тогда вам придется

  • рекурсивно заменить все табличные значения на прокси-таблицы, и я думал, что каждая прокси-таблица будет содержать некоторую информацию, которая позволяет __newindex для этой прокси-таблицы выводить правильный ключ
  • , если какой-либо ключ установлен в табличное значениеВы должны рекурсивно заменить его на прокси-таблицы, прежде чем сможете установить фактическое значение

, и это просто кажется такой большой работой и сложностью для чего-то, что должно быть проще, чем это.

1 Ответ

1 голос
/ 26 сентября 2019

Вам нужна прокси-таблица для каждой таблицы, к которой вы хотите получить доступ.Самый простой способ сделать это, хотя и не самый производительный, - возвращать новую прокси-таблицу всякий раз, когда к исходному прокси-серверу обращаются и возвращается нормальная таблица:

mt = {}
function mt.__index(self, key)
  print('accessing key '..key)
  local value = self.proxy[key]
  if type(value)=='table' then
    return setmetatable({proxy=value}, mt)
  else
    return value
  end
end
function mt.__newindex(self, key, value)
  print('setting key '..key..' to value '..tostring(value))
  self.proxy[key] = value
end

function setproxy(t)
  new_t = {proxy = t}
  setmetatable(new_t, mt)
  return new_t
end

t = {
  a = 1,
  b = 2,
  c = {
    a = 3,
    b = 4,
  },
}

t = setproxy(t)
t.a = 2 -- Works as expected
t.c.a = 4 -- Also works as expected

Замечание о производительности:

Поскольку таблицы в Lua собирают мусор, создание новых таблиц обычно считается «медленным».Это все еще вопрос перспективы, хотя;Если вы пишете простой скрипт, который запускаете вручную, не беспокойтесь об оптимизации, он все равно будет очень быстрым.Если вы пишете вложенные циклы с миллионами итераций или если ваш код должен отвечать в течение как можно меньшего количества миллисекунд, то вам следует рассмотреть вместо этого кэширование этих прокси-таблиц, в зависимости от вашего варианта использования .Если вы обнаружите, что ваш код снова и снова обращается к тем же прокси-таблицам, каждый раз создавая новые прокси-таблицы, вы можете кэшировать их в таблицу proxies , где proxies[table_A] == proxy_to_A и установить метаметод __index, который создаетпрокси, если он не существует.(На данный момент компромисс заключается в том, что создание новых прокси может быть немного медленнее из-за вызова метаметода).

...