Конкатенация объектов
Часть раздела Object Oriented Programming путешествия по Lua на Coddy — урок 28 из 70.
Оператор .. в Lua объединяет строки, но что, если вы хотите объединить объект со строкой? Метаметод __concat позволяет точно определить, как именно это должно работать.
Когда Lua встречает a .. b и один из операндов имеет метаметод __concat, она вызывает эту функцию с обоими значениями. Это полезно, когда вы хотите, чтобы объекты естественным образом вписывались в строковые выражения.
local Name = {}
Name.__index = Name
function Name:new(first, last)
local obj = {first = first, last = last}
setmetatable(obj, Name)
return obj
end
function Name.__concat(a, b)
if type(a) == "string" then
return a .. b.first .. " " .. b.last
else
return a.first .. " " .. a.last .. b
end
endОбратите внимание, что функция проверяет, какой из операндов является строкой. Поскольку .. может использоваться как "Hello " .. obj или obj .. "!", ваш метаметод должен обрабатывать оба случая.
local person = Name:new("Ada", "Lovelace")
print("Dr. " .. person) -- Вывод: Dr. Ada Lovelace
print(person .. " PhD") -- Вывод: Ada Lovelace PhDКак и другие метаметоды операторов, __concat использует синтаксис с точкой и получает оба операнда в качестве аргументов. Ключевое отличие от арифметических метаметодов заключается в том, что вы обычно работаете со смешанными типами — вашим объектом и строкой.
Задание
ЛегкоДавайте создадим класс Title, который может естественным образом встраиваться в строковые выражения с помощью оператора ... Это полезно, когда вы хотите объединять объекты со строками понятным и интуитивным способом — например, добавляя обращения или суффиксы к заголовку или званию человека.
Вы организуете свой код в двух файлах:
Title.lua: Определите класс Title с полемtext, в котором хранится строка заголовка. Включите конструктор:new(text)и реализуйте метаметод__concat. Ваш метаметод должен обрабатывать оба случая: когда строка стоит перед объектом ("prefix" .. obj) и когда строка стоит после него (obj .. "suffix"). Результат должен соответствующим образом объединять строку с текстом заголовка.main.lua: Подключите модуль Title, создайте экземпляр заголовка и продемонстрируйте конкатенацию с обеих сторон.
Вы получите три входных значения:
- Текст заголовка (например, "Engineer")
- Строку-префикс для размещения перед заголовком (например, "Senior ")
- Строку-суффикс для размещения после заголовка (например, " III")
В вашем основном файле создайте Title с заданным текстом. Затем выполните две конкатенации и выведите каждый результат на отдельной строке:
- Объедините строку-префикс с объектом заголовка
- Объедините объект заголовка со строкой-суффиксом
Например, если входные данные — Engineer, Senior и III, вывод должен быть следующим:
Senior Engineer
Engineer IIIПервая строка показывает префикс, объединенный с заголовком, а вторая — заголовок, объединенный с суффиксом. Ваш метаметод __concat делает такое естественное смешивание строк возможным!
Шпаргалка
Метаметод __concat позволяет определить пользовательское поведение для оператора конкатенации .. при работе с объектами.
Когда Lua встречает a .. b и один из операндов имеет метаметод __concat, она вызывает эту функцию с обоими значениями.
Базовая реализация
local MyClass = {}
MyClass.__index = MyClass
function MyClass:new(value)
local obj = {value = value}
setmetatable(obj, MyClass)
return obj
end
function MyClass.__concat(a, b)
if type(a) == "string" then
return a .. b.value
else
return a.value .. b
end
endОбработка обоих порядков операндов
Поскольку .. может использоваться как "string" .. obj или obj .. "string", ваш метаметод должен проверять, какой операнд является строкой, и обрабатывать оба случая:
function Name.__concat(a, b)
if type(a) == "string" then
return a .. b.first .. " " .. b.last
else
return a.first .. " " .. a.last .. b
end
endПример использования
local person = Name:new("Ada", "Lovelace")
print("Dr. " .. person) -- Вывод: Dr. Ada Lovelace
print(person .. " PhD") -- Вывод: Ada Lovelace PhDКак и другие метаметоды операторов, __concat использует синтаксис с точкой и получает оба операнда в качестве аргументов.
Попробуйте сами
-- Чтение входных данных
local titleText = io.read()
local prefix = io.read()
local suffix = io.read()
-- Подключение модуля Title
local Title = require('Title')
-- TODO: Создать экземпляр Title с заданным titleText
-- TODO: Объединить prefix с объектом title и вывести результат
-- TODO: Объединить объект title с suffix и вывести результат
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Концепция 'self'
Таблицы с функциямиЯвный 'self'Синтаксис с двоеточиемТочка против двоеточияИтоги — Движущаяся точка4Проект: Цифровой банк
Настройка проектаМетод пополнения7Полиморфизм и переопределение
Переопределение методовВызов родительских методовУтиная типизацияОбщий интерфейсПроверка типаИтоги — Роли сотрудников2Паттерн прототипа класса
Концепция прототипаСвязывание через __indexКонструктор :new()Инициализация атрибутовНезависимые экземплярыИтоги — Автозавод5Перегрузка операторов в ООП
Сложение объектовВычитание объектовКонкатенация объектовСравнение объектов (<, >)Повторение — Математика кошелька8Инкапсуляция
Соглашения об именованииЗамыкания для приватностиДоступ через замыканияТаблицы только для чтенияЛогика валидацииИтоги — Защищенное хранилище11Паттерны проектирования (Lite)
Фабричные функцииТаблица SingletonПаттерн IteratorObserver (Слушатель)Итоги — Logger Factory3Состояние и поведение объектов
Переменные экземпляраГеттерыСеттерыВычисляемые свойстваФорматирование строкПроверка на равенствоПовторение — Оценка студента6Основы наследования
Настройка наследованияНаследование методовРасширение конструктораДобавление дочерних методовОбщее и уникальноеИтоги: Иерархия фигур