Не знаете, как насчет метатаблиц Lua на стр. 108 книги "Программирование на Lua"

Я изучаю Lua по книге Программирование на Lua, первое издание. У меня проблемы с пониманием метатаблиц.

Это код и пояснения, которые появляются на странице 108:

Set = {}

function Set.new (t)
  local set = {}
  for _, l in ipairs(t) do set[l] = true end
  return set
end

function Set.union (a,b)
  local res = Set.new{}
  for k in pairs(a) do res[k] = true end
  for k in pairs(b) do res[k] = true end
  return res
end

function Set.intersection (a,b)
  local res = Set.new{}
  for k in pairs(a) do
    res[k] = b[k]
  end
  return res
end

Чтобы помочь проверить наши примеры, мы также определяем функцию для печати наборов:

function Set.tostring (set)
  local s = "{"
  local sep = ""
  for e in pairs(set) do
    s = s .. sep .. e
    sep = ", "
  end
  return s .. "}"
end

function Set.print (s)
  print(Set.tostring(s))
end

Теперь мы хотим, чтобы оператор сложения (+) вычислял объединение двух наборов. Для этого мы сделаем так, чтобы все таблицы, представляющие наборы, совместно использовали метатаблицу, и эта метатаблица будет определять, как они реагируют на оператор сложения. Наш первый шаг - создать обычную таблицу, которую мы будем использовать в качестве метатаблицы для наборов. Чтобы не загрязнять наше пространство имен, мы сохраним его в таблице Set:

Set.mt = {}    -- metatable for sets

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

function Set.new (t)   -- 2nd version
  local set = {}
  setmetatable(set, Set.mt)
  for _, l in ipairs(t) do set[l] = true end
  return set
end

После этого каждый набор, который мы создаем с помощью Set.new, будет иметь ту же таблицу, что и его метатаблица:

s1 = Set.new{10, 20, 30, 50}
s2 = Set.new{30, 1}
print(getmetatable(s1))          --> table: 00672B60
print(getmetatable(s2))          --> table: 00672B60

Наконец, мы добавляем в метатаблицу так называемый метаметод, поле __add, которое описывает, как выполнить объединение:

Set.mt.__add = Set.union

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

Имея метаметод на месте, мы можем использовать оператор сложения для объединения наборов:

s3 = s1 + s2
Set.print(s3)  --> {1, 10, 20, 30, 50}

Когда я попытался запустить его, то получил результат: { union, mt, intersection, tostring, new, print} вместо чисел в s3. Кажется, вместо этого я распечатал содержимое метатаблиц. Может кто-нибудь объяснить, что здесь происходит? В книге описана версия 5.0, и я использую Lua 5.1. Может ли это быть причиной этого?


person maxam    schedule 22.08.2013    source источник
comment
Форматирование вашего кода в вашем сообщении кажется несколько неправильным.   -  person Nicol Bolas    schedule 22.08.2013
comment
Я только что попробовал код в lua.org/pil/13.1.html и он отлично работает в Lua 5.1.   -  person lhf    schedule 22.08.2013


Ответы (1)


Ошибка в коде, который вы запустили, а не в коде, который вы разместили в вопросе. :

В Set.tostring, строка 28, вы изменили for e in pairs(set) на for e in pairs(Set), и поэтому он всегда показывает содержимое Set, а не содержимое данного набора.

person lhf    schedule 22.08.2013
comment
Спасибо, но это не так. Перепроверил, написал Set.print (s3). Есть другие идеи? - person maxam; 22.08.2013
comment
Вот и все! Большое спасибо! Это второй раз, когда я сталкиваюсь с такой досадной ошибкой в ​​lua, когда я опечатываю письмо, и весь код оказывается испорченным. На мой взгляд, это потому, что lua чувствителен к регистру и в то же время позволяет использовать переменные без их фактической инициализации. - person maxam; 22.08.2013
comment
@ user2697177, в этом случае инициализируется Set. Это не вина Lua. - person lhf; 22.08.2013
comment
Вы правы, он инициализируется глобально ... а set инициализируется локально, так что я думаю, что это другой случай - person maxam; 22.08.2013
comment
@ user2697177, вы можете использовать require 'strict', чтобы возникла ошибка при использовании необъявленной глобальной переменной. См. lua wiki - Обнаружение неопределенных переменных. - person greatwolf; 23.08.2013