Рекурсия - когда функция ссылается сама на себя - вещь не самая удачная в программировании, однако иногда она все же нужна для работы роботов в Луа. В частности у меня написана функция инициации робота, которая вызывает сама себя, в случае, если нет соединения с сервером. Иными словами, в определенной точки функции, если не считаны параметры времени сервера (т.е. очевидно нет связи с ним), оператором sleep инициируется пауза в работе скрипта, а затем, функция вызывает сама себя и запускается с начала. Выглядит это в общих чертах так:
function OnInit() theDate = 0 -- инициируем переменную, которая будет содержать текущую дату InitBeforSession = false -- инициация перед началом сессии InitEveryDay () -- ежедневная инициализация end
function InitEveryDay () message ('Proliv InitEveryDay start') sleep (100)
-- Индикаторы состояния торговли isTempStoped = false; -- временная остановка работы скрипта isTrades = false; --есть ли торги по фьючерсам? isTradesPrevious = false isConnectedPrevious = false isConnected = false
-- Флаги работы скрипта is_run = false -- флаг работы цикла Main
-- Проверка на наличие соединения if dDT.year == nil then -- если значения полей пустые, значит нет соединения с сервером !!! по-видимому здесь происходит какое-то зацикливание из-за которого quik намертво виснет при запуске в период когда нет соединения с сервером message ("Error: no connection") sleep (5000) -- стоим на месте InitEveryDay () end -- если значения полей пустые
end -- конец функции
function main() -- Задерживаем старт main до исполнения кода onInit (после его завершения флаг stopMain будет переведен в false - см. выше) message ("main - stopMain ") while stopMain do -- старт стопора while sleep (500) message ("main stopMain is "..tostring(stopMain)) end -- конец стопора while end
---------------------------
Проблема в том, что при неблагоприятных условиях, в частности если скрипт стартуется при отсутствии соединения с сервером, скрипт виснет и вешает Квик, приходится аварийно перезагружать программу. Эта схема рекурсии с паузой и оператором sleep, как я увидел, неработоспособна и во всех других случаях, когда она регулярно (многократно) срабатавает. В чем проблема? И как реализовать данную задачу корректно: скрипт многократно пытается инициироваться, пока не наступят благоприятные условия.
Функции обратного вызова обрабатываются в основном потоке терминала QUIK. Поэтому пользователю необходимо оптимизировать время исполнения таких функций.
В связи с чем любой бесконечный или долгий цикл в любом колбеке может подвесить терминал. Надо перенести проверку в функцию main
Функции обратного вызова обрабатываются в основном потоке терминала QUIK. Поэтому пользователю необходимо оптимизировать время исполнения таких функций.
В связи с чем любой бесконечный или долгий цикл в любом колбеке может подвесить терминал. Надо перенести проверку в функцию main
Правильно ли я понимаю, что в таком случае рекурсия в принципе недопустима ни в одной из функций скрипта Lua? Корректная последовательность приблизительно такова: функция должна быть исполнена до конца, вернуть условное негативное значение, после чего, с некоторой паузой должен произойти ее повторный вызов из main, в моем случае видимо из блока while stopMain do...
Коллеги, можете ли поделиться примерами ежедневной реинициализации скрипта?
Иван Ру написал: Правильно ли я понимаю, что в таком случае рекурсия в принципе недопустима ни в одной из функций скрипта Lua?
Во первых, как уже было сказано, любой бесконечный или долгий цикл, в том числе и рекурсия, а не только рекурсия. Во вторых, как уже было сказано, нежелательно их использовать только в колбеках, а значит, если та же функция вызывается в main такой проблемы не будет.
Цитата
Иван Ру написал: Коллеги, можете ли поделиться примерами ежедневной реинициализации скрипта?
Все зависит от того что для Вас "реинициализация", и вообще почему выбран именно OnInit, а не скажем OnConnected. И почему "if dDT.year == nil", а не функция isConnected() И что мешает все тоже самое сделать в цикле "while stopMain do", а в колбеках оставить только переменные?
"Во вторых, как уже было сказано, нежелательно их использовать только в колбеках, а значит..." Насколько я понимаю, функцию InitEveryDay, где имеется рекурсия не относится к коллбэкам.
ВОПРОС: "Все зависит от того что для Вас "реинициализация"" ОТВЕТ: ежедневное обновление ключевых параметров скрипта, это не OnInit, которая является коллбэком, а функция InitEveryDay. Она вызывается из OnInit, а также из еще одной функции (не колл-бэка) скрипта, которая фиксирует смену даты.
ВОПРОС: "И что мешает все тоже самое сделать в цикле "while stopMain do", а в колбеках оставить только переменные?" ОТВЕТ: Так все-таки не получится. Этот блок проходится (функционирует) единожды -- когда скрипт инициирован (запущен), но не завершена его инициация. InitEveryDay вызывается ежедневно, в том числе, когда этот блок пройден. Я рассчитываю что мой скрипт должен работать много дней без перезапуска, собственно для этого и нужна функция InitEveryDay
Иван Ру написал: "Во вторых, как уже было сказано, нежелательно их использовать только в колбеках, а значит..." Насколько я понимаю, функцию InitEveryDay, где имеется рекурсия не относится к коллбэкам.
А вызываете Вы её как раз в коллбэке - в OnInit
Цитата
Иван Ру написал: function OnInit() ... InitEveryDay () -- ежедневная инициализация end
Павел Bosco написал: напоминает шутку про индийского программиста, который чтобы узнать завтрашнюю дату, делал в программе sleep на сутки.зачем вообще рекурсия?сделайте в main while dDT.year == nil do InitEveryDay () sleep(xxx) if stopped then break endendwhile not stopped do-- основной циклendа в InitEveryDay уберите и рекурсию и sleep, там это не нужно.не нужно из колбэков вызывать тяжёлую и долгую логику, будет подвисать весь квик
Коллеги, просьба вести диалог в уважительном тоне, или хотя бы читать внимательнее! При предложенном Вами исполнении, Павел Bosco, функция InitEveryDay будет вызваться в лучшем случае единожды за все время работы скрипта -- тогда когда будет впервые установлено соединение с сервером. Выше несколько раз было сказано, что необходима ежедневная реинициализация параметров, т.е. многократное обращение к функции InitEveryDay , собственно, это даже из ее названия понять можно! Принципиальное решение мне понятно -- функция вызывается при соблюдении двух обязательных и достаточных условий - соединения с сервером и смены даты. При этом ее вызов должен происходить один раз (нужен флаг сбрасываемый при смене даты). Что касается замечания vgi, еще раз отмечу, принципиально то, что рекурсия в данном случае находится не в коллбэке. Не имеет значение откуда вызывается функция с рекурсией, она у меня может вызываться разными способами, в т.ч. и не из коллбэка, но результат плачевный в любом случае. Поэтому я и задал важный уточняющий вопрос для Sergey Gorokhov -- я вижу что рекурсия приводит к подвисанию независимо от того в коллбэке она или нет.
Иван Ру написал: то, что рекурсия в данном случае находится не в коллбэке
Вы категорически не правы, Вам уже несколько человек об этом сказали. Вызывая функцию в колбеке, Вы вызываете ее в колбеке, это же очевидно разве нет?
Цитата
Иван Ру написал: function OnInit() theDate = 0 -- инициируем переменную, которая будет содержать текущую дату InitBeforSession = false -- инициация перед началом сессии InitEveryDay () -- ежедневная инициализация end
Уберите InitEveryDay из OnInit и не будет зависать
Иван Ру написал: При предложенном Вами исполнении, Павел Bosco, функция InitEveryDay будет вызваться в лучшем случае единожды за все время работы скрипта -- тогда когда будет впервые установлено соединение с сервером. Выше несколько раз было сказано, что необходима ежедневная реинициализация параметров, т.е. многократное обращение к функции InitEveryDay
В Вашем изначальном варианте InitEveryDay точно так же вызывается единожды при старте скрипта.
Если требуется "ежедневная переинициализация" в InitEveryDay ● проверяйте isConnected() ● проверяйте дату торгов getInfoParam ("TRADEDATE ") и если она измененилась по сравнению с предыдущей запомненной делайте, что Вам требуется ● если работаете из одного и того же часового пояса, проверяйте локальное время, почему нет! ● откажитесь от рекурсии, она вводит Вас в заблуждение. Сейчас у Вас в коде рекурсия ради рекурсии, а не достижения цели. Обычный while вполне сгодится.
на самом деле в теме написана не рекурсия, а бесконечный цикл вызова функции с ожиданием события подключения. Это новон слово в программировании. Я бы так не догадался сделать цикл.
Спасибо. В принципе я так уже и сделал. Функция вызывается из нескольких мест, в примере видно только одно, в частности она вызывается при смене даты. Рекурсию надо убрать конечно и в принципе нигде ее не допускать, в данном исполнении она действительно может превратиться в бесконечный цикл с запуском бесконечного количества экземпляров функции.
Иван Ру написал: При предложенном Вами исполнении, Павел Bosco, функция InitEveryDay будет вызваться в лучшем случае единожды за все время работы скрипта
да мой пример даже и не компилируется. потому что это пример. я предложил вам вариант переписывания функции. выбросите оттуда sleep, выбросите вызов самой функции из себя, и всё у вас получится. откуда и как её вызывать - дело ваше. то что к моему предложению надо было добавить вызов из нескольких колбеков, не делает его неправильным.
раз уж просите помощи на форуме, то не надо отвечать всем, что вы правы, и вас никто не понял. по-поводу шутки, у меня сложилось впечатление, что вы не понимаете, что такое рекурсия. и даже слабо понимаете, что такое цикл.
ещё, позволю себе совет, почитайте что такое синхронные и асинхронные вызовы. вы пытаетесь через InitEveryDay устроить синхронную функцию, у которой на выходе будет гарантированный результат. отсюда и попытки работы с рекурсией. я считаю что это вам не нужно. работайте с InitEveryDay так, что иногда она не возвращает результат сразу, тогда надо просто подождать и снова её вызвать. причём при ожидании продуктивнее будет делать какую-то полезную работу, а не просто sleep. это меняет логику построения программы, но зависать ничего не будет. синхронная логика - это узкое горлышко производительности.