- Вывод текста
- Программирование
- Оформление
- Расширенные возможности
- Нюансы написания игр под разные плееры
-
- x AeroQSP
- Утилиты и средства разработки
Это старая версия документа.
В силу своей линейности язык QSP лишён определённых возможностей, присущих другим языкам программирования. Например, если вам нужно вывести на печать массив, вы делаете это с помощью цикла:
! массив mass создан заранее loop local i,size=0,arrsize('mass') while i<size step i+=1: *pl mass[i] end
Но, что если вам нужно вывести на печать десять массивов? Писать на каждый массив по циклу?
Гораздо проще написать одну универсальную локацию-функцию (см. раздел "Пользовательские функции и процедуры"), передавать в неё название массива и выводить одним и тем же циклом на печать любой массив.
И вот для этого нам потребуется использовать динамический код.
(на самом деле у этой задачи есть решение и без применения оператора DYNAMIC
, но сейчас нас интересует именно динамический код, поэтому продолжим в рамках нужного для примера решения).
Динамический код — это код, который мы не знаем заранее. Мы пишем лишь его составные части, а уже при выполнении программы этот код составляется из этих частей в нужные комбинации.
Прежде всего стоит спросить себя, из чего состоит любой код? И, кажется, что ответ очевиден: код состоит из команд. Это верно, но чем по сути являются команды? А вот тут ответ очевиден не для всех: команды — это строки текста.
Мы с вами прекрасно умеем работать со строками. Если не умеем, то быстренько читаем раздел "Строки" и учимся.
Строки можно склеивать, в них можно встраивать подвыражения, из них можно вырезать фрагменты. Вот и с кодом, по сути, можно делать то же самое. Так давайте запишем наш цикл в виде строки:
"loop local i,size=0,arrsize('mass') while i<size step i+=1: *pl mass[i] end"
Что мы тут видим? А видим мы, что в этой строке прописано название массива mass
, аж целых два раза. Название массива — это значение, которое можно помещать в перемнную, а можно извлекать из переменной.
Например, мы поместили название массива в переменную $array_name
. Как нам вставить значение этой переменной в наш код, записанный в виде строки? Используем подвыражения:
$array_name='mass' "loop local i,size=0,arrsize('<<$array_name>>') while i<size step i+=1: *pl <<$array_name>>[i] end"
Если мы запустим получившуюся локацию, мы увидим на экране наш цикл с массивом mass. Если мы пропишем в переменную $array_name
название другого массива, и снова запустим локацию, мы увидим на экране тот же цикл, но с другим массивом. Это значит, что наша строка текста формируется динамически!
Получается, и код цикла формируется динамически. Нам осталось только научиться запускать на выполнение такой код, записанный в виде строки текста.
И вот для этого, как раз, в QSP есть специальный оператор: DYNAMIC
. Мы просто передаём этому оператору наш записанный в виде текста цикл, и оператор DYNAMIC
легко его выполняет, как обычный код QSP:
$array_name='mass' dynamic "loop local i,size=0,arrsize('<<$array_name>>') while i<size step i+=1: *pl <<$array_name>>[i] end"
Таким образом, заменяя значение в переменной $array_name
, мы легко выводим на печать любой массив. А чтобы нам было ещё проще, мы создаём специальную локацию-функцию, и ей, в качестве аргументов, будем передавать названия массивов, которые хотим распечатать. Назовём эту локацию print_array
:
!# print_array dynamic "loop local i,size=0,arrsize('<<$args[0]>>') while i<size step i+=1: *pl <<$args[0]>>[i] end"
И теперь, как мы выведем на печать любой, какой хотим, массив? Проще простого:
@print_array('mass') @print_array('$unit_name') @print_array('unit_count')
Вот так возможность работы с динамическим кодом позволяет нам сделать наш код QSP более гибким и легко читаемым.
DYNAMIC
— выполняет код, переданный в виде строки текста. Общая запись:
DYNAMIC [$код],[аргумент 0],[аргумент 1], ... ,[аргумент 18], где
[$код]
— это обычный код QSP, записанный в виде текста. Выполнение такого кода аналогично выполнению кода оператора GOSUB
. Аргументы [аргумент 0]
, [аргумент 1]
и т.д. могут использоваться внутри [$код]
, их значения автоматически помещаются в ячейки массива ARGS[0]
, ARGS[1]
, и т.д. соответственно. Внутри выполняемого кода DYNAMIC
используется свой собственный массив ARGS
, его значения не пересекаются со значениями ARGS
на локации, из которой DYNAMIC
был вызван. После выполнения кода, переданного оператору DYNAMIC
, продолжается выполнение кода со следующей команды после DYNAMIC
.
Примеры:
! простые вызовы кода, записанного в виде текста dynamic '$a="string<<$b>>"' dynamic '$a' dynamic 'if $a="string":''text!''' ! вызов кода с передачей в него аргументов dynamic " *pl $args[0] addobj $args[1] ",'Вы взяли вилку.','Вилка'Нижеследующая информация справедлива и для функции
DYNEVAL
(см. ниже).
Важно! Если код задан с помощью одинарных (' '
) или двойных (" "
) кавычек, подвыражения вычисляются сразу:
$args[0]='qwerty' $code = ' *pl "<<$args[0]>>" *pl $args[0] ' dynamic $code,'asdfg'В этом случае при задании переменной $code будет вычислено подвыражение, поэтому когда
DYNAMIC
выполнит код, первой строкой выведется 'qwerty', второй строкой выведется 'asdfg'.
Фигурные скобки - третий вид кавычек используемый специально для написания динамического кода. Здесь поддерживается вложенность скобок, а подвыражения не вычисляются:
$args[0]='qwerty' $code = { *pl "<<$args[0]>>" *pl $args[0] } dynamic $code,'asdfg'В этом случае будут выведены две строки 'asdfg'. Так как подвыражение не вычисляется на момент присвоения кода переменной $code, но зато будет вычислено уже при непосредственном выполнении кода оператором
DYNAMIC
.
DYNEVAL
— выполняет код, переданный в виде строки текста, и возвращает результат, если он есть. Общая запись:
$DYNEVAL([$код],[аргумент 0],[аргумент 1], ... ,[аргумент 18]) DYNEVAL([$код],[аргумент 0],[аргумент 1], ... ,[аргумент 18]), где
[$код]
— это обычный код QSP, записанный в виде текста. Выполнение такого кода аналогично выполнению кода функции FUNC
. Аргументы [аргумент 0]
, [аргумент 1]
и т.д. могут использоваться внутри [$код]
, их значения автоматически помещаются в массив ARGS
, в ячейки ARGS[0]
, ARGS[1]
, и т.д. соответственно. Внутри выполняемого кода DYNEVAL
используется свой собственный массив ARGS
, его значения не пересекаются со значениями ARGS
на локации, из которой DYNEVAL
была вызвана. После выполнения кода, переданного функции DYNEVAL
, продолжается вычисление выражения, в котором расположена функция DYNEVAL
.
Чтобы DYNEVAL
возвращала результат, необходимо внутри [$код]
присвоить этот результат переменной RESULT
.
Примеры:
dyneval('result = 3+4') *pl $dyneval('$result = $mid("abcd",2,1)+"qwerty"') проход = dyneval("result = ($args[0] <> 'текст')", 'строка')
DYNEVAL
вернула строковое значение, результат должен быть записан в $RESULT
.DYNEVAL
вернула числовое значение, результат должен быть записан в RESULT
.$RESULT
и RESULT
— это одна и та же переменная, но с разными типами данных. Следует помнить, что новая запись значения затирает предыдущее, какого бы типа данных не было это значение.
Если при выполнении DYNEVAL
она не возвращает значения (RESULT
не инициализируется), и является единственным элементом выражения, передаваемого неявному оператору, плеер ничего не выведет на экран. Т.е. DYNEVAL
будет работать, как DYNAMIC
. Пример:
! неявный оператор выведет на экран 123: 123 ! код в dyneval выполнится, но на экране ! мы ничего не увидим: dyneval("code = 123 + 890") ! неявный оператор выведет на экран 1013: code
DYNAMIC
и DYNEVAL
следует использовать в нескольких случаях:
loop i=1 while i<=10 step i+=1: act "Действие <<i>>": *pl "Действие <<i>>" endВ данном случае будут созданы действия с названиями "Действие 1" … "Действие 10". Неопытный автор ожидает, что каждое из этих действий при щелчке по нему будет выводить собственный номер. Однако, это ожидание не верно. Переменная
i
в данном случае получит значение 10, и в момент выполнения действия именно это значение подставится в строку, выводимую на экран. То есть подвыражение, относящееся к коду действия, не будет раскрыто плеером, пока игрок не кликнет по действию.DYNAMIC
:loop i=1 while i<=10 step i+=1: dynamic 'act "Действие <<i>>": *pl "Действие <<i>>"' endВ этом случае сначала раскроются подвыражения, а затем готовый код будет передан оператору
DYNAMIC
. Таким образом сформируются дейстия с уже готовым уникальным кодом действия.DYNAMIC
и DYNEVAL
очень удобны, когда нужно выполнить некоторый многострочный код в том месте, где невозможно разместить такой многострочный код. Например, в гиперссылках:$CODE={ if no деньги<100: addobj 'Кружка имбирного эля' кружка_эля+=1 деньги-=100 *pl "Я приобрёл куржку имбирного эля." else *pl "Мне не хватает денег на эль." end } *pl "<a href='exec:dynamic $CODE'>Купить кружку имбирного эля</a>"В этом случае переменная
$CODE
обязательно должна быть глобальной, иначе ссылка её не "увидит". Однако, если необходимо использовать такой код в условиях или циклах, вполне можно обойтись и локальной переменной, чтобы код не висел в памяти:! этот код не имеет практической пользы. Просто для примера. 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В своём роде это способ создавать анонимные функции в QSP.
DYNAMIC
очень удобен для разного рода отладчиков, поскольку позволяет выполнять код QSP, введённый в строку ввода безо всяких танцев с бубном.DYNAMIC
позволяет восстанавливать действия, полученные с помощью функции $CURACTS
:$acts = $curacts cla dynamic $acts
Нет смысла использовать динамический код, если вы можете получить тот же результат без его использования. Прежде чем всюду пихать DYNAMIC
хорошенько подумайте, а не целесообразнее было бы написать этот код на отдельной локации так, чтобы не приходилось использовать DYNAMIC
. Динамический код сложнее читать и на него сложнее ссылаться. Он в принципе ухудшает читаемость кода.
Вполне возможно вложение динамического кода в динамический код, то есть DYNAMIC
/DYNEVAL
внутри DYNAMIC
/DYNEVAL
, однако это мало того, что ухудшает читаемость, так ещё и очень часто приводит к багам, которые трудно отловить. Банально, неверная закрытая кавычка внутри кода внутри кода может вызвать ошибку, выбиваемую вообще не понятно в какой строке локации. Тем не менее число вложений не ограничено.