Pull to refresh

Простейшие самообучающиеся алгоритмы на языке «Автор»

Reading time4 min
Views11K
К сожалению, все самообучающиеся алгоритмы достаточно сложны и объёмны. Логика самообучения алгоритма по-прежнему остаётся задачей программиста. Но, не смотря на это, я приведу пример классической задачи на самообучение.
Задача состоит в том, чтоб дать ответ, является ли заданное число простым. На первый взгляд ничего особенного. Но проблема заключается в том, что для того, чтобы дать правильный ответ, нужно найти заданное число в ряде простых чисел. Другого способа просто не существует. А этот ряд бесконечный. Разумеется, мы можем задать в памяти только ограниченный отрезок ряда, и получается, что как не крути, а заданное число может превышать ограничение. Конечно, вы скажите, что ряд легко продолжить. Да, вы абсолютно правы. Но с точки зрения оптимизации работы программы, по времени, неудобно каждый раз вычислять одно и тоже, а лучше сохранять массив простых чисел, каждый раз при дополнении его. Собственно в этом и заключается мера обучения этой классической задачи. С точки зрения классического программирования нам нужно организовать хранилище для содержания массива известных простых чисел, например в отдельном файле, или в базе данных. Но опираясь на возможность языка «Автор» вносить изменения программ в собственный код можно сделать такое хранилище прямо внутри кода программы.
// prosto.txt
// простейшая самообучающаяся программа поиска простых чисел.
var isProsto(n){
	m={2,3,5,7};
	if(n<0)return #;
	ok=0;
	if(n==0 || n==1)ok=1;
	for(i=0;i<m.size();++i){
		if(n==m[i]){ok=1;break;}
		if(n<m[i])break;
		}
	if(i==m.size()){
		u=m[i-1];
		oknew=0;
		do{
			++u;
			uok=1;
			for(i=0;i<m.size();++i)if(!(u%m[i])){uok=0;break;}
			if(uok){
				oknew=1;
				m.push(u);
				if(u==n)ok=1;
				}
			}while(n>u);
		if(oknew){
			f=getFunction(getThisFunctionName());
			pos=f.Root();
			pos=f.Next(pos);
			f.setComand(pos,"m="+m.export());
			}
		}
	return ok;
}

void main(){
	ok=isProsto(n=200);
	trace("Число "+n+(ok?"":" не")+" простое.");
}

Вся задача заключается в вызове функции «isProsto(n)» с заданным числом для анализа. Собственно в переменной «m» и содержится наше хранилище. Для замены команды в схеме алгоритма используется функция «f.setComand(pos,«m={2,3}»);» которую нужно вызвать от объекта функции. Первым параметром нужно указать идентификатор узла с командой в граф-схеме алгоритма, которую(команду) следует заменить, а вторым объект команды (дерева операторов). Вторым параметром может быть и строка текста, которая неявно преобразится (распарсится). Для того, чтоб получить идентификатор узла воспользуемся тем фактом, что массив/хранилище находится на первом узле от начала алгоритма функции. Функция «f.Root()» вернёт идентификатор первого и последнего узла схемы, так сказать узел началоконца алгоритма. Из него(узла) можно перейти на, гарантировано, один, первый узел. А вот поднимаясь вверх из первого и последнего узла («f.Up(pos)») возможно получить множество (массив идентификаторов) узлов, которыми заканчивается алгоритм. Дело в том, что в конце алгоритма может располагаться условный оператор с ветвлением, ведущим в узел началоконца.
Посмотрим, во что превратилась наша функция после запуска программы.
// prosto.code
void isProsto(var n){
	m={
		2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,
		67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,
		139,149,151,157,163,167,173,179,181,191,193,197,199
		};
	if(n<0)return # ;
	ok=0;
	if(n==0||n==1)ok=1;
	i=0;
	for(;i<m.size();++i){
		if(n==m[i]){
			ok=1;
			break;
			}
		if(n<m[i])break;
		}
	if(i==m.size()){
		u=m[i-1];
		oknew=0;
		do{
			++u;
			uok=1;
			i=0;
			for(;i<m.size();++i)if(!u%m[i]){
				uok=0;
				break;
				};
			if(uok){
				oknew=1;
				m.push(u);
				if(u==n)ok=1;
				}
			}while(n>u);
		if(oknew){
			f=getFunction(getThisFunctionName());
			pos=f.Root();
			pos=f.Next(pos);
			f.setComand(pos,"m="+m.export());
			}
		}
	return ok;
}

В языке «Автор» также есть возможность использования меток, по которым можно найти идентификаторы соответствующих им узлов в схеме алгоритма. Каждая метка содержит номер, который должен быть уникальный в приделах функции.
Рассмотрим следующую задачу. В программах бывает так, что нужно выполнить какие-то сложные вычисления с константами, которые занимают время, и которые достаточно сделать только один раз, скажем при написании программы, чтоб не тратить время каждый раз при новом запуске на одни и те же вычисления. Посмотрим, как можно использовать возможность языка, трансформировать скрипт, для решения этой задачи.
// one.txt
void main(){
	trace("Helloy World!!!");
	<label:10>
	if(1){
		x=1+1; // сложные вычисления
		f=getFunction(getThisFunctionName());
		pos=f.getLabel(10); // ищем метку
		pos=f.insertDown(pos);
		f.setCommand(pos,"x="+x);
		pos=f.Down(pos);
		command=f.getCommand(pos);
		command.setSub({0},PROGRAM("0"));
		f.setCommand(pos,command);
		}
	trace(x);
	getstring();
}

В данном случае, программа, конечно же, выдаст на экран «2». Но посмотрим, как выглядит файл дубликат кода, из которого интерпретатор будит брать код программы, уже после первого запуска.
// one.code
void main(){
	trace("Helloy World!!!");
	<label:10>
	x=2;
	if(0){
		x=1+1;
		f=getFunction(getThisFunctionName());
		pos=f.getLabel(10);
		pos=f.insertDown(pos);
		f.setCommand(pos,"x="+x);
		pos=f.Down(pos);
		command=f.getCommand(pos);
		command.setSub({0},PROGRAM("0"));
		f.setCommand(pos,command);
		}
	trace(x);
	getstring();
}
// one.code	:-|

Конечно же, всё можно организовать и по другому, в зависимости от сложности задачи. Я просто хотел продемонстрировать перспективы, которые открываются с возможностью программ изменять собственный код.
Также в языке есть особая системная функция «Spirit();», которая само удаляется при первом своём исполнении. Она принимает имя функции и аргументы к ней. Так что получается, что указанная функция вызовется только однажды и от этого не останется никаких следов.
// Spirit.txt
firstprocess(namef,n){
	x=100*(1+1);
	f=getFunction(namef);
	pos=f.getLabel(n);
	f.setCommand(pos,PROGRAM("k="+x));
	return 1;
}

void main(){
	Spirit("firstprocess",getThisFunctionName(),10);
	<label:10>
	trace("k="+k);
	getstring();
}

Эта программа выведет на экран «k=200» и вот во что превратится.
// Spirit.code
int firstprocess(var namef,var n){
	x=100*(1+1);
	f=getFunction(namef);
	pos=f.getLabel(n);
	f.setCommand(pos,PROGRAM("k="+x));
	return 1;
}

void main(){
	k=200;
	trace("k="+k);
	getstring();
}
// Spirit.code	:-|


Интересно(?) | Следует продолжение
Tags:
Hubs:
-11
Comments25

Articles