Инструменты пользователя

Инструменты сайта


help:dynamical

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Both sides previous revision Предыдущая версия
Следущая версия
Предыдущая версия
help:dynamical [2022/05/19 08:13]
aleks_versus [Динамический код]
help:dynamical [2024/10/14 12:17] (текущий)
aleks_versus
Строка 1: Строка 1:
 [[help:​service_locations|Назад:​ Служебные локации]] [[help:​service_locations|Назад:​ Служебные локации]]
-=====Динамический код===== 
  
-  * **DYNAMIC ** //[$код]//​**,​**//​[параметр 1]//​**,​**//​[параметр 2]//**...** - выполняет кодуказанный в виде строки текста. Выполнение кода ​//[$код]// здесь аналогично оператору GS: +====== Динамический ​код ​====== 
-    * Код, переданный ​оператору ​**dynamic**, выполняется в виде отдельного блока кодав который можно передавать параметры (аргументы). + 
-    * Переданные ​параметры хранятся в массиве ARGS. Максимальное количество параметров ​- **9** ((10 включая код)). +===== Лирическое вступление, поясняющее, для чего может пригодиться динамический ​код ​===== 
-    * После выполнения ​старые ​параметры ARGS восстанавливаются. То есть внутри выполняемого кода **dynamic** используется свой собственный ​массив ​ARGS, его значения не пересекаются со значениями ARGS на локации, из которой **dynamic** был вызван. + 
-    * Затем продолжается выполнения программы ​со следующей команды после **dynamic**. +Из-за своей линейности язык ​**QSP** лишён ​некоторых плюшек, которые есть в других языках программирования. Например, если вам нужно вывести на печать массив, ​вы делаете это с помощью ​цикла
-    * //Примеры://<​sxh qsp> + 
-DYNAMIC '$a="​string<<​$b>>"​' +<sxh qsp> 
-DYNAMIC ​'$a' +массив mass создан ​заранее 
-DYNAMIC 'if $a="​string"​:''​text!'''​ +loop local i,size=0,arrsize('mass') while i<size step i+=1
-DYNAMIC " +  *pl mass[i
-$args[0+end
-addobj $args[1] +
-",'​Вы взяли вилку.','​Вилка'​+
 </​sxh>​ </​sxh>​
-  * **DYNEVAL(**//​[$код]//**,**//​[параметр 1]//​**,​**//​[параметр 2]//**, ...)** - выполняет код, указанный в виде строки текстас получением результата. Выполнение ​кода //[$код]// ​здесь аналогично оператору FUNC: + 
-    * Код, переданный функции **dyneval**, ​выполняется в виде ​отдельного блока кода, в который можно передавать параметры (аргументы). +Но, что ​если вам нужно ​вывести на печать десять массивов? Писать на каждый массив ​по циклу
-    * Переданные параметры хранятся в массиве ARGS. Максимальное количество параметров - **9** ((10 включая код)). + 
-    * Результат функции равен значению //$RESULT// при возврате строкового значения, или //RESULT// при возврате числового значения.  +Гораздо проще написать одну универсальную локацию-функцию (см. раздел [[help:​organizing|"​Пользовательские функции и процедуры"​]]), передавать в неё название массива ​и выводить одним и тем же циклом на печать любой массив
-    * Если при выполнении ​кода были установлены и //​RESULT//, ​и //​$RESULT//, ​то предпочтение отдаётся ​строковому значению.  + 
-    * После выполнения кода предыдущие значения ARGS и RESULT восстанавливаются. То есть внутри выполняемого ​кода **dyneval** используется свои собственные массивы ARGS и RESULT, ​и их значения не пересекаются со значениями ARGS и RESULT на локациииз которой **dyneval** был вызван.  +И вот для этого ​нам потребуется использовать динамический код. 
-    * **Внимание:** при использовании ​в коде //[$код]// операторов GS/DYNAMIC переменные RESULT и $RESULT могут перезаписываться ​кодом, ​выполняемым этими операторами. + 
-      * Исправлено в более новых версиях интерпретатора. +(на самом деле у этой задачи есть решение и без применения оператора ''​%%DYNAMIC%%'', ​но сейчас нас интересует ​именно динамический код, поэтому продолжим в рамках нужного для примера решения). 
-    * //Примеры://<sxh qsp> + 
-DYNEVAL('​result = 3+4'​) +Динамический код — это код, который мы не знаем заранее. Мы пишем ​лишь его составные части, а уже ​при выполнении ​программы этот код составляется ​из этих частей в нужные комбинации. 
-PL DYNEVAL('​$result = mid("​abcd",2,1)+"​qwerty"'​) + 
-PL DYNEVAL($test + ' ​+ val("<<​$test>>"​)') +Прежде всего стоит спросить себя, из чего состоит любой код? И, кажется, что ответ очевиден: код состоит из команд. Это верно, но чем по сути являются ​команды? А вот тут ответ очевиден не для всех: команды — это ​строки текста. 
-проход=DYNEVAL("​result = ($args[0<> '​текст'​)", '​строка'​)+ 
 +Мы с вами прекрасно умеем ​работать ​со строками. Если не умеем, то быстренько читаем раздел [[help:​strings|"​Строки"​]] ​и учимся. 
 + 
 +Строки можно склеивать, в них можно встраивать ​подвыражения, из них можно вырезать фрагменты. Вот и с кодом, по сутиможно делать то же самоеТак давайте запишем наш цикл ​в виде строки: 
 + 
 +<sxh qsp> 
 +"loop local i,size=0,arrsize('mass'​) ​while i<size step i+=1: 
 +  *pl mass[i] 
 +end"
 </​sxh>​ </​sxh>​
-  ​* **Важно:** если код ​задан с помощью ​одинарных (%%' '%%) или двойных ​(%%" "%%) кавычек,​ в тексте вычисляются подвыражения. + 
-    * //Пример:/​/<sxh qsp>+Что мы тут видим? А видим мы, что в этой строке прописано название массива ''​%%mass%%'',​ аж целых два раза. Название массива — это значение,​ которое можно помещать в перемнную,​ а можно извлекать из переменной. 
 + 
 +Например,​ мы поместили название массива в переменную ''​%%$array_name%%''​. Как нам вставить значение этой переменной в наш код, записанный в виде строки?​ Используем подвыражения:​ 
 + 
 +<sxh qsp> 
 +$array_name='​mass'​ 
 +"loop local i,​size=0,​arrsize('<<​$array_name>>'​) while i<size step i+=1: 
 +  ​*pl <<​$array_name>>​[i] 
 +end" 
 +</​sxh>​ 
 + 
 +Если мы запустим получившуюся локацию,​ мы увидим на экране наш цикл с массивом ​**mass**. Если мы пропишем в переменную ''​%%$array_name%%''​ название другого массива,​ и снова запустим локацию,​ мы увидим на экране тот ​же цикл, ​но с другим массивом. Это значит,​ что наша строка текста формируется ​**динамически**! 
 + 
 +Получается, ​и код ​цикла формируется ​динамически. Нам осталось только ​научиться запускать на выполнение такой код, записанный в виде строки текста
 + 
 +И вот для этого, как ​раз, в **QSP** есть специальный оператор:​ ''​%%DYNAMIC%%''​. Мы просто передаём этому оператору наш записанный в виде текста цикл, и оператор ​''​%%DYNAMIC%%''​ легко его выполняет,​ как обычный код QSP: 
 + 
 +<sxh qsp> 
 +$array_name='​mass'​ 
 +dynamic "loop local i,​size=0,​arrsize('<<​$array_name>>'​while i<size step i+=1: 
 +  *pl <<​$array_name>>​[i] 
 +end" 
 +</​sxh>​ 
 + 
 +Таким образом,​ заменяя значение в переменной ''​%%$array_name%%'',​ мы легко выводим на печать любой массив. А чтобы нам было ещё проще, мы создаём специальную локацию-функцию,​ и ей, в качестве аргументов, будем передавать названия массивов,​ которые хотим распечатать. Назовём эту локацию ''​%%print_array%%'':​ 
 + 
 +<sxh qsp> 
 +!# print_array 
 +dynamic "loop local i,​size=0,​arrsize('<<​$args[0]>>'​while i<size step i+=1: 
 +  *pl <<​$args[0]>>​[i] 
 +end" 
 +</​sxh>​ 
 + 
 +И теперь, ​как мы выведем на печать любой, какой хотим, массив?​ Проще простого:​ 
 + 
 +<sxh qsp> 
 +@print_array('​mass'​) 
 +@print_array('​$unit_name'​) 
 +@print_array('​unit_count'​) 
 +</​sxh>​ 
 + 
 +Вот так возможность работы с динамическим кодом позволяет нам сделать наш код QSP более гибким и легко читаемым. 
 + 
 +===== DYNAMIC ===== 
 + 
 +''​%%DYNAMIC%%''​ — выполняет кодпереданный ​в виде строки ​текста. Общая запись:​ 
 + 
 +<sxh qsp> 
 +DYNAMIC [$код],​[аргумент 0],​[аргумент 1], ... ,​[аргумент 18] 
 +</​sxh>​ 
 + 
 +, где ''​%%[$код]%%''​ — это обычный код QSP, записанный ​в виде текста. Выполнение такого кода аналогично выполнению кода оператора ''​%%GOSUB%%''​. Аргументы ''​%%[аргумент 0]%%'',​ ''​%%[аргумент 1]%%''​ и т.д. могут ​использоваться внутри ''​%%[$код]%%'',​ их значения автоматически помещаются ​в ячейки массива ''​%%args%%'':​ в ''​%%ARGS[0]%%'',​ ''​%%ARGS[1]%%'',​ и т.д. соответственно. Внутри выполняемого кода ''​%%DYNAMIC%%''​ используется свой собственный массив ''​%%ARGS%%'',​ его значения не пересекаются со значениями ''​%%ARGS%%''​ на локации,​ из которой ''​%%DYNAMIC%%''​ был вызван. После выполнения кода, переданного оператору ''​%%DYNAMIC%%'',​ продолжается выполнение кода со следующей команды после ''​%%DYNAMIC%%''​
 + 
 +Примеры: 
 + 
 +<sxh qsp> 
 +! простые вызовы кода, записанного в виде текста 
 +dynamic '​$a="​string<<​$b>>"'​ 
 +dynamic '​$a'​ 
 +dynamic 'if $a="​string":''​text!'''​ 
 +! вызов кода с передачей в него аргументов 
 +dynamic " 
 +  *pl $args[0] 
 +  addobj $args[1] 
 +",'​Вы взяли вилку.','​Вилка'​ 
 +</sxh> 
 + 
 +Нижеследующая информация справедлива и для функции ''​%%DYNEVAL%%''​ (см. ниже). 
 + 
 +**Важно!** Если код задан с помощью одинарных (''​%%'​ '​%%''​) или двойных (''​%%"​ "​%%''​) кавычек,​ подвыражения вычисляются сразу:​ 
 + 
 +<sxh qsp>
 $args[0]='​qwerty'​ $args[0]='​qwerty'​
 $code = ' $code = '
-*pl "<<​$args[0]>>"​ +  ​*pl "<<​$args[0]>>"​ 
-*pl $args[0]' +  *pl $args[0] 
-$args[0]='​12345'+'
  
-DYNAMIC ​$code,'​asdfg'​ +dynamic ​$code,'​asdfg'​
-! при задании переменной $code будет вычислено подвыражение +
-! поэтому первой строкой выведется '​qwerty'​ +
-! второй строкой выведется ​'​asdfg'​+
 </​sxh>​ </​sxh>​
-  ​* Фигурные скобки - третий вид кавычек используемый специально для написания динамического кода + 
-    * Поддерживается вложенность скобок +В этом случае при задании переменной ​**$code** будет вычислено подвыражение,​ поэтому когда ''​%%DYNAMIC%%''​ выполнит код, первой строкой выведется '​qwerty',​ второй строкой выведется '​asdfg'​. 
-    * Перед выполнением кода ​не вычисляются ​подвыражения. + 
-    * //​Примеры://​<sxh qsp>+Фигурные скобки - третий вид кавычек используемый специально для написания динамического кода. Здесь поддерживается вложенность скобок, а подвыражения не вычисляются
 + 
 +<sxh qsp>
 $args[0]='​qwerty'​ $args[0]='​qwerty'​
 $code = { $code = {
Строка 53: Строка 128:
 } }
  
-DYNAMIC ​$code,'​asdfg'​ +dynamic ​$code,'​asdfg'​
-! будет выведено две строки ​'​asdfg'​+
 </​sxh>​ </​sxh>​
-  * Вполне допустимо использовать dynamic/​dyneval внутри dynamic/​dyneval,​ однако это сильно усложняет отладку кода и поиск ошибок. 
  
----- +В этом случае будут выведены две строки '​asdfg'​. Так как подвыражение не вычисляется на момент присвоения кода переменной **$code**, но зато будет вычислено уже при непосредственном выполнении кода оператором ''​%%DYNAMIC%%''​. 
-[[help:main|Вперёд:​ Основное окно описания]]+ 
 +===== DYNEVAL ===== 
 + 
 +''​%%DYNEVAL%%''​ — выполняет код, переданный в виде строки текста,​ и возвращает результат,​ если он есть. Общая запись:​ 
 + 
 +<sxh qsp> 
 +$DYNEVAL([$код],[аргумент 0],​[аргумент 1], ... ,​[аргумент 18]) 
 +DYNEVAL([$код],​[аргумент 0],​[аргумент 1], ... ,​[аргумент 18]) 
 +</​sxh>​ 
 + 
 +, где ''​%%[$код]%%''​ — это обычный код QSP, записанный в виде текста. Выполнение такого кода аналогично выполнению кода функции ''​%%FUNC%%''​. Аргументы ''​%%[аргумент 0]%%'',​ ''​%%[аргумент 1]%%''​ и т.д. могут использоваться внутри ''​%%[$код]%%'',​ их значения автоматически помещаются в массив ''​%%ARGS%%'',​ в ячейки ''​%%ARGS[0]%%'',​ ''​%%ARGS[1]%%'',​ и т.д. соответственно. Внутри выполняемого кода ''​%%DYNEVAL%%''​ используется свой собственный массив ''​%%ARGS%%'',​ его значения не пересекаются со значениями ''​%%ARGS%%''​ на локации,​ из которой ''​%%DYNEVAL%%''​ была вызвана. После выполнения кода, переданного функции ''​%%DYNEVAL%%'',​ продолжается вычисление выражения,​ в котором расположена функция ''​%%DYNEVAL%%''​. 
 + 
 +Чтобы ''​%%DYNEVAL%%''​ возвращала результат,​ необходимо внутри ''​%%[$код]%%''​ присвоить этот результат переменной ''​%%RESULT%%''​. 
 + 
 +Примеры: 
 + 
 +<sxh qsp> 
 +dyneval('​result = 3+4'​) 
 +*pl $dyneval('​$result = $mid("​abcd",​2,​1)+"​qwerty"'​) 
 +проход = dyneval("​result = ($args[0] <> '​текст'​)",​ '​строка'​) 
 +</​sxh>​ 
 + 
 +  * Чтобы ''​%%DYNEVAL%%''​ вернула строковое значение,​ результат должен быть записан в ''​%%$RESULT%%''​. 
 +  * Чтобы ''​%%DYNEVAL%%''​ вернула числовое значение,​ результат должен быть записан в ''​%%RESULT%%''​. 
 +  * Чтобы ''​%%DYNEVAL%%''​ вернула несколько значений,​ результат нужно записать в ''​%%%RESULT%%''​ как кортеж значений. 
 +  * ''​%%%RESULT%%'',​ ''​%%$RESULT%%''​ и ''​%%RESULT%%''​ — это одна и та же переменная,​ но с разными типами данных. **Следует помнить**,​ что новая запись значения затирает предыдущее,​ какого бы типа данных не было это значение. 
 + 
 +Если при выполнении ''​%%DYNEVAL%%''​ она не возвращает значения (''​%%RESULT%%''​ не инициализируется),​ и является единственным элементом выражения,​ передаваемого неявному оператору,​ плеер ничего не выведет на экран. Т.е. ''​%%DYNEVAL%%''​ будет работать,​ как ''​%%DYNAMIC%%''​. Пример:​ 
 + 
 +<sxh qsp> 
 +! неявный оператор выведет на экран 123: 
 +123 
 +! код в dyneval выполнится,​ но на экране 
 +! мы ничего не увидим:​ 
 +dyneval("​code = 123 + 890"​) 
 +! неявный оператор выведет на экран 1013: 
 +code 
 +</​sxh>​ 
 + 
 +===== Области применения DYNAMIC и DYNEVAL ===== 
 + 
 +''​%%DYNAMIC%%''​ и ''​%%DYNEVAL%%''​ следует использовать в нескольких случаях:​ 
 + 
 +  * Когда нет иного способа получить необходимый функционал. Как пример,​ очень распространённый случай генерации действий с помощью цикла: <sxh qsp> 
 +loop i=1 while i<=10 step i+=1: 
 +  act "​Действие <<​i>>":​ *pl "​Действие <<​i>>"​ 
 +end 
 +</​sxh> ​В данном случае будут созданы действия с названиями "​Действие 1" ... "​Действие 10". Неопытный автор ожидает, что каждое из этих действий при щелчке по нему будет выводить собственный номер. Однако,​ это ожидание не верно. Переменная ''​%%i%%''​ в данном случае получит значение **10**, и в момент выполнения действия именно это значение подставится в строку,​ выводимую на экран. То есть подвыражение,​ относящееся к коду действия,​ не будет раскрыто плеером,​ пока игрок не кликнет по действию.\\ 
 +Чтобы действительно сгенерировать действия,​ которые будут выводить свой правильный номер, раскрытие подвыражения должно происходить в момент создания действия. Как раз для этого и подойдёт оператор ''​%%DYNAMIC%%'':​ <sxh qsp> 
 +loop i=1 while i<=10 step i+=1: 
 +  dynamic 'act "​Действие <<​i>>":​ *pl "​Действие <<​i>>"'​ 
 +end 
 +</​sxh>​ В этом случае сначала раскроются подвыражения,​ а затем готовый код будет передан оператору ''​%%DYNAMIC%%''​. Таким образом сформируются дейстия с уже готовым уникальным кодом действия. 
 +  * Также ''​%%DYNAMIC%%''​ и ''​%%DYNEVAL%%''​ очень удобны,​ когда нужно выполнить некоторый многострочный код в том месте, где невозможно разместить такой многострочный код. Например,​ в гиперссылках<sxh qsp> 
 +$CODE = { 
 +  if no деньги<​100:​ 
 +    addobj '​Кружка имбирного эля'​ 
 +    кружка_эля+=1 
 +    деньги-=100 
 +    *pl "Я приобрёл куржку имбирного эля."​ 
 +  else 
 +    *pl "​Мне не хватает денег на эль."​ 
 +  end 
 +
 +*pl "<a href='​exec:​dynamic $CODE'>​Купить кружку имбирного эля</​a>"​ 
 +</​sxh>​ В этом случае переменная ''​%%$CODE%%''​ обязательно должна быть глобальной,​ иначе ссылка её не "​увидит"​. ​Однако, если необходимо использовать такой код в условиях или циклах,​ вполне можно обойтись и локальной переменной, чтобы ​код не висел в памяти:​ <sxh qsp> 
 +! этот код не имеет ​практической пользы. Просто для примера. 
 +local $code = { 
 +  if args[0]+args[1]+args[2]+args[3]=0:​ 
 +    result=1 
 +  elseif args[0]+args[1]+args[2]+args[3]=4:​ 
 +    result=1 
 +  else: 
 +    result=0 
 +  end 
 +
 +loop while dyneval($code,​a,​b,​c,​d):​ 
 +  a = rand(0,1) 
 +  b = rand(0,1) 
 +  c = rand(0,1) 
 +  d = rand(0,1) 
 +end 
 +</​sxh>​ В своём роде это способ создавать ​анонимные функции в QSP. 
 +  * Ещё ''​%%DYNAMIC%%''​ очень удобен для разного рода [[help:​debugger|отладчиков]], поскольку позволяет выполнять код QSP, введённый в строку ввода безо всяких танцев с бубном. 
 +  * ''​%%DYNAMIC%%''​ позволяет восстанавливать действия,​ полученные с помощью функции ''​%%$CURACTS%%'',​ и предметы,​ полученные с помощью функции ''​%%$CUROBJS%%'':​ <sxh qsp> 
 +$acts = $curacts 
 +cla 
 +dynamic $acts 
 +</​sxh>​ 
 + 
 +Нет смысла использовать динамический код, если вы можете получить тот же результат без его использования. Прежде чем всюду пихать ''​%%DYNAMIC%%''​ хорошенько подумайте,​ а не целесообразнее было бы написать этот код на отдельной локации так, чтобы не приходилось использовать ''​%%DYNAMIC%%''​. Динамический код сложнее читать и на него сложнее ссылаться. Он в принципе ухудшает читаемость кода. 
 + 
 +Вполне возможно вложение динамического кода в динамический код, то есть ''​%%DYNAMIC%%''/''​%%DYNEVAL%%''​ внутри ''​%%DYNAMIC%%''/''​%%DYNEVAL%%'',​ однако это мало того, что ухудшает читаемость,​ так ещё и очень часто приводит к багам, которые трудно отловить. Банально,​ неверная закрытая кавычка внутри кода внутри кода может вызвать ошибку,​ выбиваемую вообще не понятно в какой строке локации. Тем не менее число вложений не ограничено. 
 + 
 +[[help:​menu|Вперёд:​ Всплывающее меню]] 
help/dynamical.1652948015.txt.gz · Последние изменения: 2022/05/19 08:13 — aleks_versus