Подпрограммы в Lua

{title}

{title}

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

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

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

основы


Все функции, относящиеся к сопрограммам в Lua, находятся в таблице сопрограмм, где функция create () позволяет нам их создавать, имеет простой аргумент, и это функция с кодом, который будет запускаться подпрограммой, при которой возвращается это значение типа потока, которое представляет новую corrutina. Даже аргумент для создания corrutina иногда является анонимной функцией, как в следующем примере:
 co = coroutine.create (function () print ("Hello Solvetic") end) 
Сопрограмма может иметь четыре различных состояния:
  • подвешенный
  • бегущий
  • мертвый
  • нормальный

Когда мы создаем его, он начинается в приостановленном состоянии, что означает, что сопрограмма не запускается автоматически при ее первом создании. С состоянием corrutina можно ознакомиться следующим образом:

 печать (coroutine.status (co)) 
Где запустить нашу corrutina, мы должны использовать только функцию resume (), которая внутренне меняет свой статус с приостановленного на запущенный.
 coroutine.resume (со) 
Если мы соберем весь наш код и добавим дополнительную строку, чтобы проверить дополнительный статус нашей подпрограммы после обобщения, мы сможем увидеть все состояния, через которые он проходит:
 co = coroutine.create (function () print ("Hello Solvetic") end) print (co) print (coroutine.status (co)) coroutine.resume (co) print (coroutine.status (co)) 
Мы идем в наш терминал и выполняем наш пример, давайте посмотрим вывод нашей программы:
 lua corrutinas1.lua thread: 0x210d880 Приостановлено Привет Solvetic dead 
Как мы видим, первое впечатление о corrutina - это значение потока, затем у нас есть состояние приостановки, и это нормально, так как это первое состояние при создании создания, затем со сводкой мы запускаем corrutina, с которой он печатает сообщение. и после этого его состояние мертво, так как он выполнил свою миссию.

На первый взгляд сопрограммы могут показаться сложным способом вызова функций, однако они намного сложнее. Его сила в значительной степени падает на функцию yield (), которая позволяет приостановить выполняемую подпрограмму, чтобы иметь возможность подвести итоги своей работы позже, давайте рассмотрим пример использования этой функции:

 co = coroutine.create (function () для i = 1.10 do print ("обобщение corrutina", i) coroutine.yield () end end) coroutine.resume (co) coroutine.resume (co) coroutine.resume (co ) coroutine.resume (со) 
Это будет работать до первого выхода, и независимо от того, есть ли у нас цикл for, он будет печатать только в соответствии с тем, сколько у нас есть сводок для нашей подпрограммы, чтобы закончить, давайте посмотрим выход через терминал:
 lua corrutinas1.lua 1 2 3 4 
Это будет выход через терминал.

фильтры


Одним из наиболее ярких примеров, объясняющих сопрограммы, является случай потребителя и генератора информации. Предположим, что у нас есть функция, которая непрерывно генерирует некоторые значения для чтения файла, а затем у нас есть другая функция, которая читает их, давайте посмотрим на иллюстративный пример того, как эти функции могут выглядеть:
 Функция Generator (), в то время как true, делает локальный x = io.read () send (x), конец end Функция потребителя (), в то время как true, делает локальный x = receive () io.write (x, "\ n") end end 
В этом примере и потребитель, и генератор работают без отдыха, и мы можем остановить их, когда больше нет информации для обработки, однако проблема здесь заключается в способе синхронизации функций send () и receive (), так как каждый один из них имеет свой собственный цикл, и предполагается, что другой является службой, которая может быть вызвана.

$config[ads_text5] not found

Но с сопрограммами эта проблема может быть решена быстро и легко, используя двойную функцию суммирования / выхода, мы можем заставить наши функции работать без проблем. Когда подпрограмма вызывает функцию yield, она не вводит новую функцию, а возвращает ожидающий вызов и может покинуть это состояние только при использовании возобновления.

Точно так же, когда вызывается резюме, новая функция не запускается, она возвращает ожидающий вызов, чтобы подвести итог, суммируя этот процесс - это то, что нам нужно для синхронизации функций send () и receive () . Применив эту операцию, мы должны были бы использовать receive (), применяя summaze к генератору, чтобы сгенерировать новую информацию, а затем send () применить yield для потребителя, давайте посмотрим, как наши функции работают с новыми изменениями:

 функция receive () локальное состояние, value = coroutine.resume (генератор) возвращаемое значение end функция send (x) coroutine.yield (x) end gen = coroutine.create (function (), в то время как true делает локальный x = io.read () отправить (х) конец конец) 
Но мы все еще можем улучшить нашу программу дальше, и она использует фильтры, которые являются задачами, которые функционируют как генераторы и потребители одновременно, делая процесс преобразования информации довольно интересным.

$config[ads_text6] not found

Фильтр может суммировать генератор для получения новых значений, а затем применять доходность для преобразования данных для потребителя. Давайте посмотрим, как мы можем добавить фильтры простым способом к нашему предыдущему примеру:

 gene = generator () fil = фильтр (ген) потребитель (fil) 
Как мы видим, это было чрезвычайно просто, когда помимо оптимизации нашей программы мы зарабатывали очки для удобства чтения, что важно для будущего обслуживания.

Подпрограммы как итераторы


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

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

 функция print_result (var) для i = 1, #var do io.write (var [i], "") end io.write ("\ n") end 
Теперь мы полностью изменили этот процесс, сначала изменим print_result () на yield, давайте посмотрим на изменение:
 функция permgen (var1, var2) var2 = var2 или # var1, если var2 <= 1, тогда coroutine.yield (var1) else 
Это иллюстративный пример, демонстрирующий работу итераторов, однако Lua предоставляет нам функцию с именем wrap, которая аналогична созданию, однако она не возвращает сопрограмму, она возвращает функцию, которая при вызове суммирует подпрограмму. Таким образом, чтобы использовать обтекание, мы просто должны использовать следующее:
 перестановки функций (var) возвращают coroutine.wrap (function () permgen (var) end) end 
Обычно эту функцию гораздо проще использовать, чем создавать, поскольку она дает нам именно то, что нам нужно, то есть суммировать ее, однако она менее гибкая, поскольку она не позволяет нам проверять состояние сопрограммы, созданной с помощью переноса.

Corrutinas в Lua - чрезвычайно мощный инструмент для работы со всем, что касается процессов, которые должны выполняться рука об руку, но в ожидании завершения того, кто предоставляет информацию, мы также можем увидеть его использование для решения сложных проблем в отношении процессов генератор / потребитель, а также оптимизация построения итераторов в наших программах.