Это 5 глава перевода оригинального руководства по Dream Maker от разработчиков.

Остальные главы руководства и статьи о программировании на коде BYOND от любителей на русском.

Оригинальная 5 глава руководства на английском языке

Глава 5: Переменные

Часы вашей жизни - это крылья, пронзающие пространство с каждым взмахом. -- Халиль Джебран, Пророк.

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

Глобальные переменные

До сих пор вы видели только объектные переменные и переменные аргумента. Есть еще два места, где переменные могут задаваться: внутри процедуры, или внутри ничего, так называемые, глобальные переменные.

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

var/weather = "Looks like another beautiful day!"

mob/verb/look_up()
   usr << weather

mob/DM/verb/set_weather(txt as text)
   weather = txt

Этот пример состоит из трех частей: инициализация глобальной переменной погоды, действие для игроков, которое позволяет проверить погоду, и действие для DM, чтобы погоду изменять. Стоит обратить внимание на процесс создания переменной. Это происходит под тегом var, который находится в корне программы (если мы задаем глобальную переменную). В этом примере для переменной погоды мы задали начальное значение, но это делать не обязательно.

Объектные переменные

Позиция, в которой задают переменную, определяет ее область действия. Переменная, которую задали в корне (верхнем уровне кода), применима в любом месте программы. В то же время, если переменную задали внутри некоторого объекта, то она может применяться только внутри него или внутри его потомков.

Заметим, что когда мы говорим верхний уровень кода, то подразумеваем корень дерева программы. Это не первые строчки вашего файла.

Инициализация объектной переменной

Вы уже использовали объектные переменные, вроде name и icon, но они уже были заданы. Вы можете добавить свои собственные переменные, отличные от встроенных, для достижения разных целей. Например, вы можете ввести переменную для хранения информации о стоимости предмета.

obj
   var/value
   stone
      value = 1
   ruby
      value = 50
   diamond
      value = 100

Когда переменная определяется впервые, она записывается под тегом var. Потом можно изменить значение переменной, не определяя ее заново. Процесс изменение и определения переменных схож с действиями. Синтаксис специально построен так, чтобы предотвратить случайные ошибки.

Получение доступа к объектной переменной

В описании процедуры можно получить доступ к объектной переменной. Мы уже видели это в случаях, когда действие изменяет свойства своего источника. Например:

obj/verb/set_value(v as num)
   set src in view()
   value = v

Однако, что если мы хотим установить новое значение, только при помощи DM? Это можно сделать с помощью нового действия с измененными свойствами. Вместо того чтобы прикреплять действие к объекту (obj), мы можем присоединить его к DM. Однако в таком случае нам нужно получить доступ к значению переменной объекта из действия DM. Для этого нам нужно указать путь до переменной. Вот, как это можно сделать:

mob/DM/verb/set_value(obj/O,v as num)
   O.value = v

Первая переменная объявляется как obj/O, это говорит нам о том, что О является объектом, поэтому мы можем работать с переменными этого объекта. Чтобы получить доступ к конкретной переменной, мы используем оператор “точка”. Объект О пишется слева от точки, а справа имя нужной переменной, к которой мы хотим получить доступ. Оператор “точка” позволяет нам получить доступ к переменной, принадлежащей объекту O.

Посмотрите еще раз на синтаксис первой процедуры. Мы можем написать obj/O as obj in view(), но так как мы указали путь, все остальное предусматривается по умолчанию. Впрочем, длинная версия лучше воспринимается при чтении кода.

Указание пути до переменной

В DM переменной, которую вы объявляете, может быть присвоен тип её значений, это может быть, например, строка или число. Если программисту нужно получить доступ к переменной объекта, то тогда необходимо сообщить компилятору путь, до объекта, к которому относится переменная. Это можно сделать, определив путь перед именем переменной. Это может быть obj/O или что-то более длинное, вроде obj/scroll/O. В общем, нужно указать полный путь до переменной. Например, obj/O даст нам доступ к O.name, O.icon и другим переменным этого объекта. Переменная, которая определена только для свитка, скажем O.duration, требует более точного пути: obj/scroll/O.

Переменные usr и src

Две переменные, которые вы уже видели, могут быть использованы сразу с точкой, потому что путь до их объекта определен автоматически. Переменная usr объявляется как var/mob/usr, поэтому она всегда относится к нужному существу. Переменная src, очевидно, работает также, только с объектами.

Так как получение доступа к переменной источника довольно-таки частая задача, вы можете делать это напрямую. Например, вы можете использовать value, вместо src.value. Это одно и то же.

Переменная usr довольно полезная, когда src и usr обозначают разные объекты. Например, мы можем сделать предмет маскировки, который изменяет внешний вид носителя.

obj/disguise
   verb/wear()
      usr.icon = icon  //zing!

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

obj/disguise
   var/old_icon
   verb
      wear()
         old_icon = usr.icon
         usr.icon = icon
      remove()
         usr.icon = old_icon

Кстати, интересная мысль. Что, если кто-то отключит маскировку другому игроку!? Вот такие причудливые вещи могут устроить игроки. Никогда не доверяйте им! В следующей главе мы рассмотрим некоторые инструменты, которые помогут это предотвратить.

Переменные процедуры

Переменные аргумента это специальный уровень переменных процедур. Встроенные переменные usr и src также находятся на этом уровне. Вы можете объявить свою собственную переменную внутри процедуры, используя такой же синтаксис, как и при объявлении любой другой переменной.

Предположим, что вы хотите поменять два объекта местами – несколько другой способ маскировки. В этом примере, свиток дает владельцу возможность стать свитком, в то время как свиток будет выглядеть, как его владелец (только не пытайтесь его использовать в туалете, иначе кто-то совершит ужасную ошибку).

obj/mirror_scroll
   verb/cast()
      var/usr_icon = usr.icon
      usr.icon = icon
      icon = usr_icon

Промежуточная переменная usr_icon помогает двум объектам обменяться своими изображениями. Мы могли бы объявить ее отдельно, но ее инициализация в действии лишает нас некоторых проблем.

Жизнь переменных

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

Глобальные переменные появляются в самом начале и существуют до конца выполнения программы. Объектные переменные появляются вместе с объектом и существуют до тех пор, пока есть объект. У двух одинаковых объектов есть по своей копии одной и той же переменной, в отличие от глобальной переменной, которая существует одна для всех. Самый низкий уровень: переменные процедуры, они появляются, когда процедура начинает свою работу, и исчезают в конце процедуры. Их часто называют локальными переменными.

Уровень переменной определяет ее порядок среди других переменных. Предположим такую ситуацию: пусть у переменной процедуры будет такое же имя, как и у объектной.

mob/verb/call_me(name as text)
   name = name     //очевидно, не то, что мы хотим
   src.name = name //а это уже то

Мы объявили переменную процедуры (или аргумент), которая называется name, но есть переменная существа, которая называется также. Переменные уровня процедуры имеют приоритет над объектными переменными. Чтобы получить доступ к объектной переменной, нам нужно написать src.name, а не просто name.

Аналогично, глобальные переменные имеют более низкий приоритет, чем объектные или локальные переменные. В этом случае конфликт может быть решен путем использования global.name. Ошибки с конфликтами имен в некоторых случаях сложно заметить.

В ваших интересах избежать этой проблемы. Иногда полезно иметь переменную, которая ведет себя как глобальная, но определяется на более низком уровне. Например, переменная может представлять интерес только в части кода, но вы хотите иметь ее постоянную копию. Вы этого достигните, если зададите ее как глобальную, но на требуемом уровне (многие языки используют термин “статичная” вместо “глобальная”, когда говорят о переменной, которую задают в нужном месте, но с постоянным существованием - это одно и то же).

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

obj/magic_paper
   var/global/msg

   verb
      write(txt as text)
         msg = txt
      read()
         usr << "[src] says, '[msg]'"

То, что мы определили переменную сообщения внутри объекта magic_paper, помогает нам избежать захламления глобального пространства имен ненужным мусором, который потребуется только в конкретном месте. Просто нужно вставить тег global сразу после var в пути переменной. Это поможет избежать содержания в каждой отдельной волшебной бумаге отдельной переменной для одного и того же текста. Это также позволяет освободить имя msg, чтобы использовать его в любом другом месте кода.

Константы

Другой тег, который может применяться при определении переменной это const. Это метка для переменных, которые после инициализации никогда не будут изменяться. Мы называем их постоянными переменными (здесь намеренно есть оксюморон).

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

Например, вы можете сделать двойника некоторого объекта:

obj/doppelganger
   var/const/init_icon = 'doppel.dmi'
   icon = init_icon

   verb
      clone()
         set src in view()
         icon = usr.icon
      revert()
         set src in view()
         icon = init_icon

Конечно, вы можете использовать везде "doppel.dmi", вместо init_icon, но если вы захотите использовать другую картинку, то вам придется вручную изменить ее по всему коду (это верный путь запутаться и сделать кучу ошибок).

Память и Переменные

Теперь поговорим о памяти. Наверное, многие из вас уже задумывались о том, как программы DM хранят информацию. Например, когда мы присваиваем спрайт одной переменной другой, то сам файл дублируется? К счастью, ответ “нет”, иначе многие операции работали бы менее эффективно.

В DM переменные могут хранить только два типа данных: цифры и ссылки. Цифры это просто. Когда вы присваиваете переменной число, то это число копируется и “кладется” в переменную. Если вы изменяете эту переменную, то ничего не меняется, кроме числа, которое переменная содержит.

Все переменные, отличные от цифровых, хранят ссылку, которая указывает, где хранится информация (программисты на C, знают ссылки как указатели). Таким образом, эти переменные хранят только ссылку к требуемой информации, будь то спрайт, существо, строка или что-то еще. Когда содержимое переменной копируется другой, то изменяется только ссылка. Сами данные не зависят от этих операций.

Программисты, которые знакомы с управлением и распределением памяти, отметят тот факт, что DM заботится о “мусоре”. Это значит, что когда на какую-то информацию (например, сообщение внутри действия say) больше не ссылается ни одна переменная, она автоматически удаляется из памяти, чтобы освободить место для новой информации. Это делает работу с множеством различной информации: текстом, картинками, звуками и другим – значительно легче. При этом вам даже не требуется беспокоиться об этом.