Вычисленные значения нужно где-то сохранять – для чего, как нетрудно догадаться, используются переменные.
Все рассмотренные нами до этого момента выражения – всегда возвращали 100%-но предсказуемое значение. Потому, что все простейшие операнды, которые нам пока известны – это литералы, а все рассмотренные нами числовые и строковые операции (за исключением связанных с генератором случайных чисел) – выдают результат, однозначно определяемый их операндами, и не имеют побочных эффектов (что называется функциональной чистотой). К счастью, имеются ещё и переменные, которые вносят в выполнение программы намного больше непредсказуемости.
Переменные – мутабельны: их значение может меняться в процессе выполнения программы. Athanor – бестиповой язык (точнее говоря, типы в нем имеют значения, но не переменные). Любой переменной может присваиваться значение любого типа (независимо от того, что содержалось в ней раньше). Переменные также могут иметь разные области видимости и действия. Самый тривиальный случай – глобальные переменные: они действуют от места, где они впервые упомянуты, до самого конца программы (или текущей сессии интерпретатора, если мы в интерактивном режиме). По умолчанию, глобальные переменные не нужно явно объявлять: любая переменная, которая не декларирована где-либо еще, автоматически считается глобальной. (Можно включить режим явного декларирования глобальных переменных, но об этом подробнее потом.)
Переменные различаются по своим именам. Имя переменной – это идентификатор. Все идентификаторы допускают латинские заглавные и строчные буквы (они различаются), десятичные цифры и знак подчеркивания (но не могут начинаться с цифры). Вот примеры допустимых идентификаторов:
Возможно, в будущем в идентификаторы можно будет включать и символы локальных кодировок (например, кириллические) – но пока такое не реализовано.
Для изменения переменных – необходимы специальные операции: мутаторы. Наиболее популярная из них – это присваивание. Как и практически все остальное в языке, она реализована как встроенный бинарный функтор (set):
|
V = X
|
set (V,X)
|
присвоить переменной V значение X |
Присваивание также возвращает результат: присвоенное значение X, которое может дальше использоваться в вычислениях. При использовании операторной записи, вокруг присваивания обычно нужно ставить скобки (т.к. приоритет операции '=' – один из самых низких).
До того, как переменной впервые присваивается значение, она не содержит ничего. Впрочем, в языке "ничего" (пустое значение, или же просто пустота) – это тоже специальное значение, которое мы дальше мы будем обозначать просто как (). В C и C++ неинициализированная переменная содержит некое неопределённое значение ("мусор") – в Athanor такое невозможно. Пустое значение нужно чётко отличать от всех других "пустых" значений языка: от целого нуля (0), плавающего нуля (0.0), пустой строки ("") и пр. Всё перечисленное – это скаляры, но вот значение () скаляром не является! Поэтому использование () в любой скалярной операции – это ошибка: результатом обычно тоже будет (), и выдаётся сообщение. Впрочем, во многих нескалярных операциях значение () в качестве аргумента вполне допускается, но подробнее о них позднее.
Ещё одна полезная группа мутаторов – это инкременты и декременты (увеличивающие/уменьшающие свой операнд на единицу):
|
++ V
|
inc (V)
|
Преинкремент: (V = V + 1) |
|
-- V
|
dec (V)
|
Предекремент: (V = V - 1) |
|
V ++
|
inc_p (V)
|
Постинкремент: ((V = V + 1) - 1) |
|
V --
|
dec_p (V)
|
Постдекремент: ((V = V - 1) + 1) |
|
clr (V)
|
Очистка: (V = 0) |
Все эти операции предполагают, что V – мутабельно, и уже содержит значение числового типа (не обязательно целое). Обе операции инкремента ('++') увеличивают его на 1, декремента ('--') уменьшают на 1. Возвращается результат (в префиксной форме – значение V до операции, в постфиксной – после), который может быть использован в дальнейших вычислениях. В отличие от C и Java, значение V может быть и плавающего типа! Еще одна операция – clr(V) – просто присваивает 0 мутабельному V (но с сохранением его типа: если оно было целое, присваивается 0, а если плавающее – 0.0).
Нередко очень полезно иметь возможность совмещать присваивание с выполнением некоторых других операций. Такая возможность тоже есть (хотя синтаксис для этого несколько нестандартный):
равносильно:
с той разницей, что мутабельное V вычисляется только один раз. Вместо {binary_op}, разумеется, должен быть подставлен символ соответствующей бинарной скалярной операции, которой может быть любая из перечисленных:
Пробелы (перед и после знака операции) допустимы, но не требуются (и здесь они только для наглядности). Предполагается, что в V уже содержится скалярное значение, числовое или строковое. Как и обычное присваивание – совмещенное тоже возвращает значение V после операции.
Вот некоторые примеры:
Также можно совмещать присваивание и с унарными скалярными операциями.
Синтаксис требуется такой:
что равносильно:
но мутабельное V также вычисляется только один раз. Конечно, вместо {unary_op} также должен быть подставлен символ соответствующей унарной скалярной операции, одной из перечисленных:
Вот примеры:
В завершение разговора об операциях-мутаторах, упомянем весьма полезную операцию обмена:
|
V :=: W
|
swap (V,W)
|
меняет местами значения V и W |
Здесь, разумеется, оба операнда должны быть мутабельными! В результате выполнения этой операции, значения V и W меняются местами. Их значения могут быть любыми (и не обязательно скалярными, но другие типы мы рассмотрим позже). Также заметим, что все переменные мутабельны, но не все мутабельные выражения – переменные! Они могут быть и более сложными, но о таких разговор тоже будет позже. Но в сложных случаях, тот факт, что все перечисленные операции вычисляют свой мутабельный операнд только один раз, может быть существенным.
Наконец (чтобы завершить нашу беседу о переменных) упомянем ещё полезный функтор x_vars. Вызов его без параметров – x_vars() – выдаёт полный список глобальных переменных, определённых и доступных к данному моменту, вместо с их значениями. (А с параметрами – и не только глобальных переменных, но об этом также мы будем говорить позднее.)