В интерпретаторе «Автор» существует три отличных от других, классических, языков типа данных, про которых я хочу рассказать в этой статье, а именно «digit», «graf» и «program». Они отвечают, соответственно, за астрономические числа без плавающей точки, универсальный граф и дерево операторов и вызовов функций с переменными и константами.

Следующий пример демонстрирует основное отличие типа «digit» от типа «int».

// fact.txt
var fact(n){return n<2?1:n*fact(n-1);}

void main(){
	trace(fact(10));
	trace(fact(50));
	trace(fact((digit)50));
	x=(digit)1;
	x.setAccuracy(63);
	trace(x/3.123);
	getstring();
}


3628800
0
30414093201713378043612608166064768844377641568960512000000000000
0.320204931155939801472942683317323086775536343259686199167467178


В первом случае вычисление прошло правильно. Во втором произошло переполнение памяти для типа «int». В третьем вычисление прошло без проблем. Это получилось по тому, чт�� тип «digit» умеет выделять память для экспоненты числа по мере необходимости. Для мантиссы числа следует указать ограничение для избегания утечки памяти при делении, скажем, на три. Метод типа «x.setAccuracy(63);» указывает ограничение мантиссы числа в десятичных знаках. У типа есть и другие полезные методы.

Теперь расскажу немного про универсальный граф. Графом называют множество узлов и связей между ними. Для типа «graf» узел существует только тогда, когда у него есть хотя бы одна связь. Каждая связь имеет два номера узла, соответствующие им значение наличия входных стрелок и имя связи. Кроме связей между узлами, в универсальном графе, также есть связь узла с маркером. Маркером является строка текста. Каждая связь с маркером имеет один номер узла, имя связи и имя маркера. Также имеется логические значения про наличие входной стрелки в узел и в маркер.
Следующая программа демонстрирует возможности универсального графа.

// UniversalGraf.txt
void main(){
	//Задаём граф (конверт).
	G=(graf)0;
	G.NET(0,1,"дорого",1,1);
	G.NET(0,1,"дорого",1,4);
	G.NET(1,1,"",1,2);
	G.NET(1,1,"",1,4);
	G.NET(1,1,"",1,3);
	G.NET(2,1,"",1,3);
	G.NET(2,1,"",1,4);
	G.NET(3,1,"",1,4);
	G.MARKER(0,0,"",0,"Марс");
	G.MARKER(1,0,"",0,"Винница");
	G.MARKER(2,0,"",0,misto="Гайворон");
	G.MARKER(3,0,"",0,"Москва");
	G.MARKER(4,0,"",0,"Гайсин");
	G.MARKER(0,1,"жизни",0,"нет");

	trace(G.getMARKER(0,#,#,0,#).export());
	trace(G.getNET(4,#,#,#,#).export());

	tracex(misto);
	stop=n=G.getMARKER(#,0,"",0,misto)[0][0];
	do{
		tracex("-->");
		n=G.getNET(n,#,#,1,#)[#][2];
		tracex(G.getMARKER(n,0,"",0,#)[0][0]);
		}while(stop!=n);

	getstring();
}


{{0,"","Марс"},{1,"жизни","нет"}}
{{1,"дорого",1,0},{1,"",1,1},{1,"",1,2},{1,"",1,3}}
Гайворон-->Москва-->Винница-->Москва-->Гайсин-->Гайворон


Для создания переменной типа «graf» нужно воспользоваться оператором приведения типов. Далее задаём сам граф с формой подобной открытому конверту.Метод «G.getMARKER(0,#,#,0,#)» производит поиск среди связей узла с маркером по заданной маске и возвращает массив найденных элементов. Каждый элемент является массивом с данными запрашиваемыми в маске. В поисковой маске должен быть хотя бы одна неизвестная деталь связи, которая указывается символом «#». В языке «Автор», символ «#» возвращает значение «void», которое можно получить, например, от функции без «return». Метод массива «.export()» преобразит вложенные массивы в строку. Функция «trace()» выводит строку на экран. Метод графа «G.getNET(4,#,#,#,#)» осуществляет поиск связей между узлами графа с учётом симметрии поиска и работает аналогично поиску связей с маркерами. При попадании типа «void» в оператор индекса массива «{3,5,9}[#]» интерпретатор вернёт одно из значений элементов этого массива, случайно равновероятно.
И того получается программа, которая ищет пути путешествия «вокруг света».
Тип графа также имеет и другие полезные методы.
Для наглядной демонстрации возможностей фундаментального типа «program» приведу следующий пример.

// demo.txt
var main(nirvana,paradise,skazka){
	if(isset(nirvana))return paradise+nirvana+skazka;
	f=getFunction(getThisFunctionName());
	pos=f.Up(f.Root());
	command=command2=f.getCommand(pos);
	trace((string)command + "==" + eval(command));
	trace(command.getSub({}));
	trace(command.getSub({0}));
	trace(command.getSub({0,0}));
	trace(command.getSub({0,1}));
	trace(command.getSub({1}));
	trace("-----------------");
	pos=f.Up(pos);
	command=f.getCommand(pos);
	trace((string)command + "==" + eval(command));
	trace(command.getSub({}));
	trace(command.getSub({0}));
	trace(command.getSub({0,2}));
	trace(command.getSub({0,4}));
	trace(command.getSub({0,4,0}));
	trace(command.getSub({0,4,3}));
	trace(command.getSub({0,4,3,0}));
	trace(command.getSub({0,4,3,1}));
	trace(command.getSub({1}));
	trace("-----------------");
	proga=PROGRAM("X=#+#");
	trace(proga);
	proga.setSub({1,0},command);
	proga.setSub({1,1},command2);
	trace(proga);
	eval(proga);
	trace("X=="+X);
	getstring();
	return;
	{0,1,2,3,main(100,20,7-3)}[4];
	(1+9)*100;
}


(1+9)*100==1000
(1+9)*100
1+9
1
9
100
-----------------
{0,1,2,3,main(100,20,7-3)}[4]==124
{0,1,2,3,main(100,20,7-3)}[4]
{0,1,2,3,main(100,20,7-3)}
2
main(100,20,7-3)
main
7-3
7
3
4
-----------------
X=# +#
X={0,1,2,3,main(100,20,7-3)}[4]+(1+9)*100
X==1124

Первая строчка алгоритма функции указывает терминальную ветку рекурсии. Далее переменная «f» получает доступ к функции, в которой находится. Затем переменная «pos» получает уникальный идентификатор последнего узла в (блок)схеме алгоритма, в которую превратилась функция на момент исполнения. Последний узел находится выше нулевого узла. Далее получаем копии команды, которая находится в последнем узле схемы алгоритма. Собственно «команда» и является объектом типа «program». Она являет собой дерево операторов и вызовов функций с переменными и константами. Любую команду можно как преобразовать в текст, так и исполнить. С помощью метода типа «command.getSub({})» можно скопировать ветку с дерева задав путь доступа, в данном случае скопируется всё дерево.
Функция «PROGRAM()» преобразует строку текста программы в тип «program».
Тип имеет и другие полезные методы.

еще | Cледует продолжение.