Очереди и двойные очереди в луа, Пример из книги Р.Е.
Пользователь
Сообщений: Регистрация: 25.09.2020
22.01.2024 11:26:34
VPM, С ног на голову, говорите? Ну, давайте поставим на ноги: мой скрипт давно написан, отлажен, работает в боевом режиме прямо сейчас, тикеров у него на два порядка больше, чем у вас, никаких проблем ни с памятью, ни с быстродействием нет от слова "совсем", все глюки системного софта (по крайней мере, все известные мне) купированы, моё "частное решение" реализовано! Ваше же "общедоступное решение" НЕ реализовано и не будет реализовано НИКОГДА.
Пользователь
Сообщений: Регистрация: 15.06.2023
22.01.2024 11:30:25
Владимир, Я Вас поздравляю с отличным решением! Я ушел заниматься своим, а то действительно не соберу. НО ХОЧЕТСЯ ПОДНЯТЬ новую тему?
Пользователь
Сообщений: Регистрация: 15.06.2023
20.03.2024 21:44:56
----- lifo из двусвязной очереди и пример позволят вести себя как стек - обеспечивая LIFO-порядок.
Скрытый текст
-- Создание класса двусвязной очереди local Queue = {} Queue.__index = Queue
function Queue.new() return setmetatable({first = 0, last = -1}, Queue) end
function Queue:push_left(value) local first = self.first - 1 self.first = first self[first] = value end
function Queue:pop_left() local first = self.first if first > self.last then error("queue is empty") end local value = self[first] self[first] = nil -- to allow garbage collection (разрешить сбор мусора) self.first = first + 1 return value end
function Queue:push_right(value) local last = self.last + 1 self.last = last self[last] = value end
function Queue:pop_right() local last = self.last if self.first > last then error("queue is empty") end local value = self[last] self[last] = nil -- to allow garbage collection (разрешить сбор мусора) self.last = last - 1 return value end
-- Создание LIFO из двусвязанной очереди local LIFO = {} LIFO.__index = LIFO function LIFO.new() return setmetatable({queue = Queue.new()}, LIFO) end
function LIFO:push(value) self.queue:push_right(value) end
function LIFO:pop() return self.queue:pop_right() end
-- Пример использования local lifo = LIFO.new() lifo:push(1) lifo:push(2) print(lifo:pop()) -- Выведет 2 print(lifo:pop()) -- Выведет 1
Еще одна реализация стека, по сути это тоже самое что и в букваре только реализация в виде класса, просто удобней пользоваться., наследуется без проблем, легко дописать фифо. У себя делаю лифо для контроля памяти, создавая динамические окна. Выкладываю может кому еще сгодится.
Пользователь
Сообщений: Регистрация: 02.01.2026
28.01.2026 11:46:39
Цитата
Nikolay написал: Открываете пример MA.lua, например расчет SMA, и видите использование массива sum равного длине периода.
Да нет, вроде, в sum хранятся значения по всем свечам:
Код
function F_SMA()
local sum = {}
local it = {p=0, l=0}
return function (I, P, VT, ds)
if I == 1 then
sum = {}
it = {p=0, l=0}
end
if CandleExist(I,ds) then
if I~=it.p then it={p=I, l=it.l+1} end
local Ip,Ipp,Ippp = Squeeze(it.l,P),Squeeze(it.l-1,P),Squeeze(it.l-P,P)
sum[Ip] = (sum[Ipp] or 0) + GetValueEX(it.p,VT,ds)
if it.l >= P then
return (sum[Ip] - (sum[Ippp] or 0)) / P
end
end
return nil
end
end
Пользователь
Сообщений: Регистрация: 02.01.2026
04.02.2026 18:10:47
А, разобрался: функция Squeeze возвращает индекс в массиве "по кольцу" в пределах заданного периода.
Пользователь
Сообщений: Регистрация: 02.01.2026
12.02.2026 10:35:27
Цитата
VPM написал: -- Создание LIFO из двусвязанной очереди
table.insert / table.remove делают то же самое, только быстрее.
Пользователь
Сообщений: Регистрация: 02.01.2026
12.02.2026 15:50:01
Цитата
VPM написал: ---- lifo из двусвязной очереди и пример позволят вести себя как стек - обеспечивая LIFO-порядок
Можно гораздо проще:
Код
local LIFO = {}
LIFO.__index = LIFO
function LIFO.new()
return setmetatable({}, LIFO)
end
function LIFO:push(v)
self[#self+1] = v
end
function LIFO:pop()
local idx = #self
if idx == 0 then return nil end
local v = self[idx]
self[idx] = nil
return v
end
Пользователь
Сообщений: Регистрация: 15.06.2023
12.02.2026 22:21:28
Если Вы про оптимизацию, то согласен без спорно быстрее. Но иногда просто нужен тупой код прямолинейный как стрела, для пользователей типа меня, просто что его можно было прочесть, и успокоится что все выполняется как надо. Здесь ключевое выполняется как надо? А разве нет?
Пользователь
Сообщений: Регистрация: 02.01.2026
01.03.2026 19:13:13
Цитата
VPM написал: Но иногда просто нужен тупой код прямолинейный как стрела
Прямолинейный для LIFO - это работа с таблицей стандартными методами. Поскольку у вас добавление и извлечение всегда с конца таблицы, то как функции table.insert / table.remove, так и обычное присваивание последнему элементу в таблице будет работать вполне себе быстро и понятно. Зачем городить очередь с двумя индексами для LIFO - большая загадка
Пользователь
Сообщений: Регистрация: 15.06.2023
01.03.2026 19:32:33
Йцукен, Речь шла прежде всего про универсальность, какой еще подход дает возможность, из одного подхода создавать разные очереди? Тут удивляться не чему, одна базовая и делай из нее под свою задачу что нужно. А если это модуль то и сколько нужно. Возможно инженерия кода и заслуживает замечаний, но речь идет о самом подходе. В Вашем примере меня сильно смущает вот этот момент self[idx] = nil дыр не будет? А если все в порядке, то почему бы нет, все на первый взгляд элегантно?
Пользователь
Сообщений: Регистрация: 02.01.2026
01.03.2026 21:54:06
Цитата
VPM написал: подход дает возможность, из одного подхода создавать разные очереди?
Не, ну если вам нужно одновременно для одной очереди и FIFO и LIFO, тогда да, очередь с двумя индексами более подходящая под эту задачу. Но нужно понимать, что нельзя в таком случае в разных потоках работать с одной стороны очереди. В разных потоках можно только FIFO: в одном потоке положил, в другом вытащил. Но если используется для конкретной задачи LIFO, то не стоит усложнять. Достаточно классических методов работы с таблицами.
Цитата
VPM написал: В Вашем примере меня сильно смущает вот этот момент self[idx] = nil дыр не будет?
Каких дыр? Мы вынули последний элемент таблицы, и присвоили ему значение nil. В таком случае оператор # работает корректно. Но я просто привёл пример, как можно упростить ваш код, если вам "кровь из носу" нужно ООП. А вообще для LIFO достаточно:
Код
tbl = {} -- создать таблицу
tbl[#tbl+1] = v -- добавить в конец таблицы
v = tbl[#tbl] -- получить последний элемент таблицы
tbl[#tbl] -- удалить последний элемент таблицы
tbl = {} -- создать таблицу
tbl[ # tbl + 1 ] = v -- добавить в конец таблицы
v = tbl[ # tbl] -- получить последний элемент таблицы
tbl[ # tbl] -- удалить последний элемент таблицы
tbl[ # tbl] -- удалить последний элемент таблицы Как это удаляет? ================= Так просто лишь у дилетантов. --------------------- Посмотрите как реализован стек в VMLua. ========================== Операции Некоторые операции, которые можно производить со стеком:
Push — добавляет новый элемент в стек.
Pop — удаляет элемент, который находится сверху.
Peek (или Top) — показывает верхний элемент стека, но не убирает его.
Size — возвращает количество элементов, которые находятся в стеке.
СложностьВременная сложность всех основных операций стека, работающих по принципу LIFO, — O(1). Пространственная сложность — O(n), где n — количество элементов. ПрименениеПринцип LIFO используется в разных областях, например:
Программирование — например, когда одна задача вызывает другую и нужно вернуться обратно после завершения.
История действий в браузере — каждый переход по ссылке сохраняется в стек, при нажатии «Назад» происходит pop, и открывается предыдущая страница.
Управление товарными запасами на складе — первыми отгружаются товары, поступившие на склад последними. Новая партия размещается поверх предыдущей, что упрощает процессы погрузки и разгрузки.
Важно: в некоторых языках и системах у стека фиксированный объём памяти, например, в низкоуровневых языках (C, C++). В многопоточных приложениях доступ к стеку нужно контролировать — если два потока используют стек одновременно, могут возникнуть ошибки из-за конфликта при обращении к данным ======================================== Программировать тоже надо учиться.
Пользователь
Сообщений: Регистрация: 02.01.2026
02.03.2026 10:15:45
Цитата
Йцукен написал: tbl[#tbl] -- удалить последний элемент таблицы
Опечатался. Должно быть так:
Код
tbl[#tbl] = nil -- удалить последний элемент таблицы
Но умные люди и так всё поняли, в отличие от недоделанного бота.
Пользователь
Сообщений: Регистрация: 15.06.2023
02.03.2026 10:39:07
nikolz, Вы правы, это классическая и очень правильная структура. Стек — идеальный выбор для хранения фиксированного числа последних значений (5, 20 и т.д.) благодаря дисциплине LIFO и операциям сложности O(1).
Но, если мы хотим реализовать стек в чистом Lua для QUIK, то не можем использовать встроенный stack, но можем создать его на основе таблицы. По чему ОПП удобно, таблица знает о себе все. Да можно быстрей, но архитектуру выбираем сами под задачу. Ведь так?
Для образного понимания смысла, мне нравятся проводить аналогии. В этом случае, можно сравнить 2 автомобиля. 1. 60 годов, для диагностики неисправностей которого требовался опытный механик. 2. современный авто где без подключения к компьютеру уже не разобраться. Так и использование мета таблиц в луа, это просто современный авто.
Пользователь
Сообщений: Регистрация: 02.01.2026
02.03.2026 10:41:28
Цитата
Йцукен написал: Но нужно понимать, что нельзя в таком случае в разных потоках работать с одной стороны очереди.
Если нужно использовать очередь LIFO в разных потоках, то лучше воспользоваться потокобезопасными функциями table.sinsert / table.sremove
Пользователь
Сообщений: Регистрация: 15.06.2023
02.03.2026 11:00:07
Просто пример:
Код
-- Класс Stack (LIFO)
Stack = {}
Stack.__index = Stack
function Stack:new(max_size)
local o = {
elements = {}, -- таблица-хранилище
max_size = max_size or 5, -- по умолчанию храним 5 элементов
count = 0
}
setmetatable(o, Stack)
return o
end
-- Добавить элемент наверх (push)
function Stack:push(value)
table.insert(self.elements, value)
self.count = self.count + 1
-- Если превышен максимальный размер, удаляем самый старый (снизу)
if self.count > self.max_size then
table.remove(self.elements, 1) -- удаляем первый (самый старый)
self.count = self.max_size
end
end
-- Удалить и вернуть верхний элемент (pop)
function Stack:pop()
if self.count == 0 then return nil end
local value = table.remove(self.elements) -- удаляем последний
self.count = self.count - 1
return value
end
-- Посмотреть верхний элемент без удаления (peek)
function Stack:peek()
if self.count == 0 then return nil end
return self.elements[self.count]
end
-- Получить все элементы в порядке от старого к новому (для отображения)
function Stack:get_all()
return self.elements
end
-- Получить все элементы в обратном порядке (новые сверху)
function Stack:get_all_reversed()
local rev = {}
for i = self.count, 1, -1 do
table.insert(rev, self.elements[i])
end
return rev
end
-- Текущий размер
function Stack:size()
return self.count
end
Как использовать в скрипте. Вместо отдельных массивов и ручного управления размером создаём стеки:
Код
-- В начале скрипта, после конфигурации
local deltaP_stack = Stack:new(20) -- для нормализации (20 значений)
local deltaQ_stack = Stack:new(20)
local c_stack = Stack:new(5) -- для отображения последних 5 значений
local alpha_stack = Stack:new(5)
local signal_stack = Stack:new(5)
-- для отображения исходных данных (опционально)
local deltaP_display_stack = Stack:new(5)
local deltaQ_display_stack = Stack:new(5)
local deltaOI_display_stack = Stack:new(5)
local deltaNB_display_stack = Stack:new(5)
local deltaNA_display_stack = Stack:new(5)