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

от автора

К сожалению, все самообучающиеся алгоритмы достаточно сложны и объёмны. Логика самообучения алгоритма по-прежнему остаётся задачей программиста. Но, не смотря на это, я приведу пример классической задачи на самообучение.
Задача состоит в том, чтоб дать ответ, является ли заданное число простым. На первый взгляд ничего особенного. Но проблема заключается в том, что для того, чтобы дать правильный ответ, нужно найти заданное число в ряде простых чисел. Другого способа просто не существует. А этот ряд бесконечный. Разумеется, мы можем задать в памяти только ограниченный отрезок ряда, и получается, что как не крути, а заданное число может превышать ограничение. Конечно, вы скажите, что ряд легко продолжить. Да, вы абсолютно правы. Но с точки зрения оптимизации работы программы, по времени, неудобно каждый раз вычислять одно и тоже, а лучше сохранять массив простых чисел, каждый раз при дополнении его. Собственно в этом и заключается мера обучения этой классической задачи. С точки зрения классического программирования нам нужно организовать хранилище для содержания массива известных простых чисел, например в отдельном файле, или в базе данных. Но опираясь на возможность языка «Автор» вносить изменения программ в собственный код можно сделать такое хранилище прямо внутри кода программы.

 // 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	:-| 

Продолжение следует.

ссылка на оригинал статьи https://habrahabr.ru/post/277145/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *