Задача состоит в том, чтоб дать ответ, является ли заданное число простым. На первый взгляд ничего особенного. Но проблема заключается в том, что для того, чтобы дать правильный ответ, нужно найти заданное число в ряде простых чисел. Другого способа просто не существует. А этот ряд бесконечный. Разумеется, мы можем задать в памяти только ограниченный отрезок ряда, и получается, что как не крути, а заданное число может превышать ограничение. Конечно, вы скажите, что ряд легко продолжить. Да, вы абсолютно правы. Но с точки зрения оптимизации работы программы, по времени, неудобно каждый раз вычислять одно и тоже, а лучше сохранять массив простых чисел, каждый раз при дополнении его. Собственно в этом и заключается мера обучения этой классической задачи. С точки зрения классического программирования нам нужно организовать хранилище для содержания массива известных простых чисел, например в отдельном файле, или в базе данных. Но опираясь на возможность языка «Автор» вносить изменения программ в собственный код можно сделать такое хранилище прямо внутри кода программы.
// 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/
Добавить комментарий