Найпростіші самонавчальні алгоритми мовою «Автор»

На жаль, всі самонавчальні алгоритми досить складні і об'ємні. Логіка самонавчання алгоритму, як і раніше, залишається завданням програміста. Але, не дивлячись на це, я наведу приклад класичного завдання на самонавчання.


Завдання полягає в тому, щоб дати відповідь, чи є вказане число простим. На перший погляд нічого особливого. Але проблема полягає в тому, що для того, щоб дати правильну відповідь, потрібно знайти задане число в ряді простих чисел. Іншого способу просто не існує. А цей ряд нескінченний. Зрозуміло, ми можемо задати в пам'яті тільки обмежений відрізок ряду, і виходить, що як не крути, а задане число може перевищувати обмеження. Звичайно, ви скажіть, що ряд легко продовжити. Так, ви абсолютно праві. Але з точки зору оптимізації роботи програми, за часом, незручно кожен раз обчислювати одне і теж, а краще зберігати масив простих чисел, кожен раз при доповненні його. Власне в цьому і полягає міра навчання цього класичного завдання. З точки зору класичного програмування нам потрібно організувати сховище для вмісту масиву відомих простих чисел, наприклад в окремому файлі, або в базі даних. Але спираючись на можливість мови «Автор» вносити зміни програм у власний код можна зробити таке сховище прямо всередині коду програми.

//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 (getThisFunc^ Name ());
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 :-|

Цікаво (?) | Слід продовжувати