Создание объектов
Область видимости объекта != время жизни объекта
У объекта имя есть всегда, даже тогда, когда сам объект физически еще не создан. Область видимости имени определяется его расположением в программном коде (блок кода, пространство имен, модуль и т.д.). Объект может существовать, но быть не доступен из текущей области видимости, например, перекрыт другим объектом с таким же именем.
Время жизни локального/автоматического (т.е. временного) объекта всегда ограничено областью видимости текущего блока кода, а время жизни статического объекта ограничено временем жизни модуля, в котором он определен или временем жизни программы, если объект глобальный. Но инициализация начального занчения статического объекта происходит в соответствии с выполняемым программным кодом, (т.е. при выполнении кода с оператором создания статического объекта).
Создания объектов
Для создания объектов в NewLang используется несколько операторов:
-
“::=” или “::-” - всегда создает новый объект (переменную, функцю, тип или класс). Если объект с таким именем уже был определён ранее, то новый объект будет его перекрывать, но нельзя создать два объекта с одинаковым именем в одной области видимости (блоке кода).
-
“:=” или “:-” - используется для присвоения нового значения уже существующей переменной или создает новую, если переменная с указанным имененм отсуствует.
Не применим для создания типов функций или методов классов.
Операторы “::-” и “:-” используются при создании переменных, значения которых должны быть вычислены на этапе компиляции, а так же для создания чистых функций.
Присвоение нового значения
Оператор “=” применяется для присвоения нового значения уже существующим объектам.
Если объект с указанным именем отсутствует, то возникает ошибка компиляции.
Если переопределяется функция или метод класса,
то старое значение не удаляется, а создается стек переопределенных имен (своего рода таблицу вирутальных имен времени компиляции),
и к старому (предыдущему) объекту можно обратиться из новой функции по системному имени “$$”.*
Оператор обмена значениями
Оператор “:=:” (swap) не создает новых переменных, а только обменивает их значения между собой.
Переменные должны иметь одинаковые/совместимые типы данных, либо иметь тип :None
, т.е. "_
".
Используется для реализации идиомы копирования и замены (copy-and-swap idiom), так как при выполнении оператора не может быть ошибок.
Оператор добавления элемента
Оператор “:[]=” добавляет новый элемент к словарю / тензору / строке, увеличивая их размер на единицу. Правый объект оператора должен быть совместим с типом единичного элемента левого объекта.
Например, при определении класса :NewClass2
:
:NewClass ::= :Class() { # Базовый класс
@::static ::= 0; # Однократная инициализация статического объекта класса
filed1 ::= 1; # Создание поля класса
filed2 ::= 2; # Создание поля класса
method1() ::= {}; # Регистрация метода
method2() ::= {}; # Регистрация метода
};
:NewClass2 ::= :NewClass() {
filed1 ::= 2; # Будет ошибка, т.к. field1 уже есть в базовом классе
filed2 := 2; # Используется поле field2 базового класса (или создается новое)
method1() ::= {}; # Будет ошибка, т.к. method1 уже есть в базовом классе
method1() = {}; # Аналог override, т.к. method должен существовать в базовом классе
};
$var := 99; # Создать переменную var
{
$var := 100; # Новая переменная перекрывает var более высокого уровня
print('%d', $var) # > 100
}
print('%d', $var) # > 99
Присваивание значения сразу нескольким переменным и оператор распаковки словаря
NewLang поддерживает операцию присваивания значения сразу нескольким переменным, которые должны быть перечислены через запятую слева от оператора присвоения.
С правой стороны от оператора присвоения может находится только одно значение,
а для обмена занчениями двух переменных, вместо записи a,b = b,a;
нужно использовать оператор обмена:
a :=: b;
В качестве правого операнда в операторе присвоения допускается использовать оператор распаковки словаря … (многоточие), который можно использовать и при передаче аргументов в функцию.
args := (arg1=1, arg2=2, 3, 4,);
call(arg=0, ... args);
# Что равносильно вызову
call(arg=0, arg1=1, arg2=2, 3, 4);
Словарь может быть указан и с левой стороны от оператора присвоения.
Таким образом можно записать простой способ перебора всех его элементов за счет использования синтаксической конструкции:
item, dictionary := ... dictionary;
, т.е. первый элемент словаря сохраняется в переменную item
, а из самого словаря удаляется.
Пример реализации цикла foreach для суммирования всех элементов словаря (или одномерного тензора) с использованием оператора раскрытия словаря (списка):
summa := 0;
dictionary := (1,2,3,4,5,);
@while( dictionary ) {
# Первый элемент словаря перемещается в item
item, dictionary := ... dictionary;
summa += item;
};
Заполнение размерных тензороов
При заполнении данными размерных тензоров существует краткий способ указания их начальных значений. Для этого используется многоточие в следующих вариантах:
-
:Tensor[10](2,3, ...)
- повторение всех предыдущих данных до полного заполнения тензора, что равносильно записи:Tensor[10](2,3, 2,3, 2,3, 2,3, 2,3)
. -
:Tensor[10](2, 3, ... 42 ...)
- заполнение значением42
до конца тензора, что равносильно записи:Tensor[10](2,3, 42,42,42,42,42,42,42,42)
. -
:Tensor[10](2, 3, ... rand() ...)
- заполнение до конца тензора значением, которое будет возвращать функция. Указанная функция будет вызываться для каждого элемента до полного заполнения тензора (8 раз). -
:Tensor[10]( rand(), rand(), ...)
- заполнение тензора двумя произвольными заначениями, то есть функцияrand()
будет вызвана дважды только для двух первых элементов, после чего эти значения будут использоваться до полного заполнения тензора (как в первом примере). -
:Tensor[10]( ... 0..0.99..0.1 )
- заполнение тензора элементами диапазона, что равносильно записи
:Tensor[10](0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9)
. Раскрытие диапазона создает фиксированное количество значений. Поэтому в данном примере размерность тензора можно не указывать, так как она будет создана автоматически. -
:Tensor[15]( ... 0..5 , ... )
- заполнение тензора элементами диапазона и их повторение до заданного размера тензора::Tensor(0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4)
Заполнение данными в словарях и аргументах функций
Предыдущие способы заполнения данными возможны только для тензоров фиксированного размера, так как позволяют получить произвольное количество значений, однако их можно использовать только в конце списка значения для инициализации.
Операторы раскрытия словаря или диапазона создают ограниченное количество значений, но за счет этого их можно использовать не только для инициализации размерных тензоров, но и как начальные значений у словарей или аргументы при вызове функций, причем в произвольном месте, а не только в конце списка инициализации:
-
:Tensor( ... 0..0.99..0.1 )
- заполнение тензора без указания его размера элементами из диапазона будет равносильно записи:Tensor(0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9)
-
funtcion( 0, ... dict, 42 )
- раскрытие словаря как индивидуальных не именованных аргументов функции, к примеру funtcion( 0, 1, 2, 3, 42), если словарьdict
содержал три элемента1
,2
и3
. -
funtcion( ... ... dict, end=4)
- раскрытие словаря как индивидуальных именованных аргументов функции. Если словарь dict будет содержать элементы(1, two=2, three=3,)
, то это будет равносильно вызову функцииfuntcion( 1, two=2, three=3, end=4)
. -
funtcion( ... ... dict, end=4)
- раскрытие словаря как индивидуальных именованных аргументов функции. Если словарь dict будет содержать элементы(1, two=2, three=3,)
, то это будет равносильно вызову функцииfuntcion( 1, two=2, three=3, end=4)
. -
funtcion( ... :Tensor[5](... rand() ...) )
- раскрытие тензора с пятью случайными значениями в качестве аргументов при вызове функции.