Здесь показаны различия между двумя версиями данной страницы.
Both sides previous revision Предыдущая версия Следущая версия | Предыдущая версия | ||
help:dynamical [2013/11/21 19:18] aleks_versus |
help:dynamical [2024/10/14 12:17] (текущий) aleks_versus |
||
---|---|---|---|
Строка 1: | Строка 1: | ||
[[help:service_locations|Назад: Служебные локации]] | [[help:service_locations|Назад: Служебные локации]] | ||
- | =====Динамический код===== | ||
- | * **DYNAMIC ** //[$код]//**,**//[параметр 1]//**,**//[параметр 2]//**, ...** - выполняет код, указанный в виде строки текста. Выполнение кода //[$код]// здесь аналогично оператору GS. | + | ====== Динамический код ====== |
- | * Переданные параметры хранятся в массиве ARGS. Максимальное количество параметров - **9**((10 включая код)). | + | |
- | * После выполнения старые параметры ARGS восстанавливаются. | + | ===== Лирическое вступление, поясняющее, для чего может пригодиться динамический код ===== |
- | * Затем продолжение выполнения программы со следующей команды после DYNAMIC. | + | |
- | * //Примеры://<sxh qsp> | + | Из-за своей линейности язык **QSP** лишён некоторых плюшек, которые есть в других языках программирования. Например, если вам нужно вывести на печать массив, вы делаете это с помощью цикла: |
- | DYNAMIC '$a="string<<$b>>"' | + | |
- | DYNAMIC '$a' | + | <sxh qsp> |
- | DYNAMIC 'if $a="string":''text!''' | + | ! массив mass создан заранее |
- | DYNAMIC " | + | loop local i,size=0,arrsize('mass') while i<size step i+=1: |
- | $args[0] | + | *pl mass[i] |
- | addobj $args[1] | + | end |
- | ",'Текст','Вилка' | + | |
</sxh> | </sxh> | ||
- | * В качестве значения переменной, подставляемого интерпретатором в строку вместо имени переменной в угловых скобках, будет использоваться значение переменной до DYNAMIC. | + | |
- | * //Пример://<sxh qsp> | + | Но, что если вам нужно вывести на печать десять массивов? Писать на каждый массив по циклу? |
+ | |||
+ | Гораздо проще написать одну универсальную локацию-функцию (см. раздел [[help:organizing|"Пользовательские функции и процедуры"]]), передавать в неё название массива и выводить одним и тем же циклом на печать любой массив. | ||
+ | |||
+ | И вот для этого нам потребуется использовать динамический код. | ||
+ | |||
+ | (на самом деле у этой задачи есть решение и без применения оператора ''%%DYNAMIC%%'', но сейчас нас интересует именно динамический код, поэтому продолжим в рамках нужного для примера решения). | ||
+ | |||
+ | Динамический код — это код, который мы не знаем заранее. Мы пишем лишь его составные части, а уже при выполнении программы этот код составляется из этих частей в нужные комбинации. | ||
+ | |||
+ | Прежде всего стоит спросить себя, из чего состоит любой код? И, кажется, что ответ очевиден: код состоит из команд. Это верно, но чем по сути являются команды? А вот тут ответ очевиден не для всех: команды — это строки текста. | ||
+ | |||
+ | Мы с вами прекрасно умеем работать со строками. Если не умеем, то быстренько читаем раздел [[help:strings|"Строки"]] и учимся. | ||
+ | |||
+ | Строки можно склеивать, в них можно встраивать подвыражения, из них можно вырезать фрагменты. Вот и с кодом, по сути, можно делать то же самое. Так давайте запишем наш цикл в виде строки: | ||
+ | |||
+ | <sxh qsp> | ||
+ | "loop local i,size=0,arrsize('mass') while i<size step i+=1: | ||
+ | *pl mass[i] | ||
+ | end" | ||
+ | </sxh> | ||
+ | |||
+ | Что мы тут видим? А видим мы, что в этой строке прописано название массива ''%%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' | ||
- | DYNAMIC '$a="<<$args[0]>>" | + | $code = ' |
- | *pl $a | + | *pl "<<$args[0]>>" |
- | $a=$args[0] | + | *pl $args[0] |
- | *pl $a','fghjk' | + | ' |
- | ! соответственно сначала будет выведена строка "qwerty", потом "fghjk" | + | |
- | </sxh> * **DYNEVAL(**//[$код]//**,**//[параметр 1]//**,**//[параметр 2]//**, ...)** - выполнение кода //[$код]// аналогично оператору FUNC, но код выполняется из строки. | + | dynamic $code,'asdfg' |
- | * Переданные параметры хранятся в массиве ARGS. Максимальное количество параметров - **9**((10 включая имя локации)). | + | |
- | * Результат функции равен значению //$RESULT// при возврате строкового значения или //RESULT// при возврате числового значения. | + | |
- | * Если при обработке локации были установлены и //RESULT//, и //$RESULT//, то предпочтение отдаётся строковому значению. | + | |
- | * После обработки локации предыдущие значения ARGS и RESULT восстанавливаются. | + | |
- | * **Внимание:** при использовании в коде //[$код]// операторов GS/DYNAMIC переменные RESULT и $RESULT могут перезаписываться кодом, выполняемым этими операторами. | + | |
- | * Исправлено в более новых версиях интерпретатора. | + | |
- | * //Примеры://<sxh qsp> | + | |
- | DYNEVAL('result = 3+4') | + | |
- | PL DYNEVAL('$result = mid("abcd",2,1)+"qwerty"') | + | |
- | PL DYNEVAL($test + ' + val("<<$test>>")') | + | |
- | проход=DYNEVAL("result = ($args[0] <> 'текст')", 'строка') | + | |
</sxh> | </sxh> | ||
- | * Фигурные скобки - третий вид кавычек используемый специально для написания динамического кода | ||
- | * Поддерживается вложенность скобок | ||
- | * //Примеры://<sxh qsp> | ||
- | $somecode={ | ||
- | p 'sdasdasd' | ||
- | er=3454 | ||
- | $d='sdsd' | ||
- | 'asdad' | ||
- | if args[0]=23:pl 'asdasdasd' | ||
- | } | ||
- | dynamic $somecode, 23 | + | В этом случае при задании переменной **$code** будет вычислено подвыражение, поэтому когда ''%%DYNAMIC%%'' выполнит код, первой строкой выведется 'qwerty', второй строкой выведется 'asdfg'. |
+ | |||
+ | Фигурные скобки - третий вид кавычек используемый специально для написания динамического кода. Здесь поддерживается вложенность скобок, а подвыражения не вычисляются: | ||
+ | |||
+ | <sxh qsp> | ||
+ | $args[0]='qwerty' | ||
+ | $code = { | ||
+ | *pl "<<$args[0]>>" | ||
+ | *pl $args[0] | ||
+ | } | ||
+ | |||
+ | dynamic $code,'asdfg' | ||
</sxh> | </sxh> | ||
- | ---- | + | В этом случае будут выведены две строки '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|Вперёд: Всплывающее меню]] |