В интерпретаторе «Автор» существует три отличных от других, классических, языков типа данных, про которых я хочу рассказать в этой статье, а именно «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ледует продолжение.

Комментарии (14)