Программирование на Athanor - 3 (переменные и мутаторы)

Цикл: "Программирование на Athanor": Часть 3 (Переменные и операции их изменения)
16 декабря 2025, вторник 09:55
trilirium для раздела Блоги

Переменные, присваивания и другие способы их изменения

Вычисленные значения нужно где-то сохранять – для чего, как нетрудно догадаться, используются переменные.

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

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

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

  • A
  • ABC
  • xyz
  • average_value
  • DataLink_123
  • ParagraphCounter
  • CP_1251

Возможно, в будущем в идентификаторы можно будет включать и символы локальных кодировок (например, кириллические) – но пока такое не реализовано.

Для изменения переменных – необходимы специальные операции: мутаторы. Наиболее популярная из них – это присваивание. Как и практически все остальное в языке, она реализована как встроенный бинарный функтор (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} : N

равносильно:

  • V = V {binary_op} N

с той разницей, что мутабельное V вычисляется только один раз. Вместо {binary_op}, разумеется, должен быть подставлен символ соответствующей бинарной скалярной операции, которой может быть любая из перечисленных:

  • '+' '-' '*' '/' '%' '%%' '<<' '>>' '?<' '?>' '&' '|' '~' '+$' '*$'

Пробелы (перед и после знака операции) допустимы, но не требуются (и здесь они только для наглядности). Предполагается, что в V уже содержится скалярное значение, числовое или строковое. Как и обычное присваивание – совмещенное тоже возвращает значение V после операции.

Вот некоторые примеры:

  • V =+: 20
    увеличить V на 20
  • V =*: 1.5
    умножить V на 1.5
  • V =~: \xAAAA
    инвертировать в V набор битов \xAAAA
  • V =+$: "..."
    добавить в конец строки V многоточие "..."

Также можно совмещать присваивание и с унарными скалярными операциями.

Синтаксис требуется такой:

  • V =: {unary_op}

что равносильно:

  • V = {unary_op} V

но мутабельное V также вычисляется только один раз. Конечно, вместо {unary_op} также должен быть подставлен символ соответствующей унарной скалярной операции, одной из перечисленных:

  • '-' '+' '~'

Вот примеры:

  • V =:+
    заменить V на его модуль
  • V =:-
    заменить V на - V
  • V =:~
    инвертировать в V все биты

В завершение разговора об операциях-мутаторах, упомянем весьма полезную операцию обмена:

V :=: W
swap (V,W)
меняет местами значения V и W


Здесь, разумеется, оба операнда должны быть мутабельными! В результате выполнения этой операции, значения V и W меняются местами. Их значения могут быть любыми (и не обязательно скалярными, но другие типы мы рассмотрим позже). Также заметим, что все переменные мутабельны, но не все мутабельные выражения – переменные! Они могут быть и более сложными, но о таких разговор тоже будет позже. Но в сложных случаях, тот факт, что все перечисленные операции вычисляют свой мутабельный операнд только один раз, может быть существенным.

Наконец (чтобы завершить нашу беседу о переменных) упомянем ещё полезный функтор x_vars. Вызов его без параметров – x_vars() – выдаёт полный список глобальных переменных, определённых и доступных к данному моменту, вместо с их значениями. (А с параметрами – и не только глобальных переменных, но об этом также мы будем говорить позднее.)

Теги