Програми в Луа

А съпрограма Той е подобен на нишка, това е ред за изпълнение със собствен стек, свои собствени локални променливи и собствен указател за инструкциите, но с особеността, че споделя глобални променливи и всеки друг елемент с другите съпрограми.

Но трябва да изясним, че има разлики между нишки и съпрограми, основната разлика е, че програма, която използва нишки, ги изпълнява едновременно, съпрограми от друга страна, те са съвместни, където програма, която използва съпрограми, изпълнява само една от тях, а спирането им се постига само ако е изрично поискано.

The съпрограми Те са изключително мощни, нека видим какво обхваща тази концепция и как можем да ги използваме в нашите програми.

Основни понятия


Всички функции, свързани с съпрограми в Луа се намират в таблицата на съпрограмите, където функцията създавам () ни позволява да ги създаваме, тя има прост аргумент и е функцията с кода, който ще изпълнява съпрограмата, където връщането й е стойност на типа нишка, която представлява новата съпрограма. Дори аргументът за създаване на съпрограма понякога е анонимна функция, както в следния пример:
 co = coroutine.create (function () print ("Hello Solvetic") край)
А съпрограма може да има четири различни състояния:
  • спряно
  • бързам
  • мъртъв
  • нормално

Когато го създаваме, той започва в състоянието прекратено, което означава, че съпрограмата не се изпълнява автоматично, когато се създава за първи път. Състоянието на съпрограма може да бъде проверено по следния начин:

 печат (coroutine.status (co))
Където можем да изпълняваме нашата програма, трябва само да използваме функцията на обобщава (), което това, което прави вътрешно, е да промени статуса си от спряно на работещо.
 coroutine.resume (co)
Ако съберем целия си код и добавим допълнителен ред, за да попитаме допълнителното състояние на нашата програма след това обобщава можем да видим всички състояния, през които преминава:
 co = coroutine.create (function () print ("Hello Solvetic") end) print (co) print (coroutine.status (co)) coroutine.resume (co) print (coroutine.status (co))
Отиваме до нашия терминал и изпълняваме нашия пример, нека видим резултата от нашата програма:
 lua coroutines1.lua конец: 0x210d880 Окачен Hello Hello Solvetic мъртъв
Както можем да видим първото впечатление от съпрограмата е стойността на нишката, тогава имаме състояние спряно, и това е добре, тъй като това е първото състояние при създаване, след това с обобщава Изпълняваме съпрограмата, с която тя отпечатва съобщението и след това нейният статус е мъртъвтъй като изпълни мисията си.

На пръв поглед съпрограмите може да изглеждат като сложен начин за извикване на функции, но те са много по -сложни от това. Силата на същото е в голяма част от функцията доходност () което позволява преустановяване на програма, която се изпълнява, за да се възобнови работата й по -късно, нека видим пример за използването на тази функция:

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

Филтри


Един от най -ясните примери, които обясняват съпрограмите, е случаят с консуматор Y генератор на информация. Да предположим тогава, че имаме функция, която непрекъснато генерира някои стойности от четене на файл и след това имаме друга функция, която ги чете, нека видим илюстративен пример за това как могат да изглеждат тези функции:
 генератор на функции () докато true прави локално x = io.read () изпраща (x) край край функция Consumer () докато вярно прави локално x = получава () io.write (x, "\ n") край край
В този пример и потребителят, и генераторът работят без никакъв вид почивка и можем да ги спрем, когато няма повече информация за обработка, но проблемът тук е как да синхронизираме функциите на Изпрати () Y получавам (), тъй като всеки от тях има свой собствен цикъл, а другият се приема за услуга, която може да се извиква.

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

По същия начин при обаждане обобщава също не стартира нова функция, връща извикващо извикване на добив, обобщавайки този процес, трябва да синхронизираме функциите на Изпрати () Y получавам (). Прилагайки тази операция, ще трябва да използваме получавам () Приложи обобщава към генератора, за да генерира новата информация и след това Изпрати () Приложи добив За потребителя нека видим как изглеждат функциите ни с новите промени:

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

А филтър мога да направя обобщава от генератор, за да получите нови стойности и след това да приложите добив за трансформиране на данни за потребителя. Нека да видим как лесно можем да добавим филтри към предишния ни пример:

 ген = генератор () фил = филтър (ген) потребител (фил)
Както виждаме, това беше изключително просто, където в допълнение към оптимизирането на нашата програма спечелихме точки за четимост, важни за бъдещата поддръжка.

Програми като итератори


Един от най -ясните примери за генератора / потребителя е итератори присъстващи в рекурсивни цикли, където итератор генерира информация, която ще бъде консумирана от тялото в рамките на рекурсивния цикъл, така че не би било неразумно да се използват съпрограми за писане на тези итератори, дори съпрограмите имат специален инструмент за тази задача.

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

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

Корутините в Луа Те са изключително мощен инструмент за справяне с всичко, свързано с процеси, които трябва да се изпълняват ръка за ръка, но в очакване на завършването на този, който предоставя информацията, също бихме могли да видим тяхната употреба за решаване на сложни проблеми по отношение на процесите генератор / потребител както и оптимизиране на изграждането на итератори в нашите програми.

wave wave wave wave wave