📜 ⬆️ ⬇️

The simplest self-learning algorithms in the language "Author"

Unfortunately, all self-learning algorithms are quite complex and voluminous. The logic of the self-learning algorithm is still the task of the programmer. But, despite this, I will give an example of a classic task for self-study.
The problem is to answer whether the given number is simple. At first glance, nothing special. But the problem is that in order to give the correct answer, you need to find a given number in a number of prime numbers. There is simply no other way. And this series is endless. Of course, we can set in memory only a limited segment of the series, and it turns out that how not to twist, and a given number can exceed the limit. Of course, you say that the series is easy to continue. Yes, you are absolutely right. But from the point of view of optimizing the work of the program, in time, it is inconvenient to calculate the same thing every time, and it is better to save an array of prime numbers, each time it is supplemented. Actually, this is the measure of learning this classic problem. From the point of view of classical programming, we need to organize a repository to hold an array of known prime numbers, for example, in a separate file, or in a database. But based on the ability of the “Author” language to make changes to programs in your own code, you can make such storage right inside the program code.
 // prosto.txt
 // simplest self-learning program to search for primes.
 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 ("Number" + n + (ok? "": "not") + "simple.");
 }

The whole task is to call the function “isProsto (n)” with the specified number for analysis. Actually, the “m” variable contains our storage. To replace a command in the algorithm, the function “f.setComand (pos,“ ​​m = {2,3} ”);” is used, which must be called from the function object. The first parameter is to specify the node identifier with the command in the graph-diagram of the algorithm, which (command) should be replaced, and the second object of the command (operator tree). The second parameter can also be a string of text that is implicitly transformed (parsed). In order to obtain the node identifier, we use the fact that the array / storage is located on the first node from the beginning of the algorithm of the function. The function “f.Root ()” will return the identifier of the first and last node of the scheme, so to speak the node of the beginning of the algorithm. From it (the node) you can go to, guaranteed, one, the first node. But rising up from the first and last node (“f.Up (pos)”) it is possible to obtain a set (array of identifiers) of nodes with which the algorithm ends. The fact is that at the end of the algorithm there can be a conditional operator with a branch leading to the beginning node.
Let's see what our function has become after the launch of the program.
 // 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;
 }

In the "Author" language, there is also the possibility of using labels, by which one can find the identifiers of the corresponding nodes in the algorithm scheme. Each label contains a number that must be unique within the function.
Consider the following problem. In programs, it happens that you need to perform some complex calculations with constants that take time and that is enough to be done only once, say when writing a program, so as not to waste time each time you start a new run on the same calculations. Let's see how you can use the language feature, transform a script, to solve this problem.
 // one.txt
 void main () {
	 trace ("Helloy World !!!");
	 <label: 10>
	 if (1) {
		 x = 1 + 1;  // complex calculations
		 f = getFunction (getThisFunctionName ());
		 pos = f.getLabel (10);  // look for the label
		 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 ();
 }

In this case, the program, of course, will display "2". But let's see how the file looks like a duplicate of the code from which the interpreter wakes up to take the program code after the first launch.
 // 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: - |

Of course, everything can be organized differently, depending on the complexity of the task. I just wanted to demonstrate the perspectives that open up with the ability of programs to change their own code.
Also in the language there is a special system function "Spirit ();", which itself is removed when it is first performed. It takes the name of the function and the arguments to it. So it turns out that the specified function will be called only once and there will be no trace of this.
 // 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 ();
 }

This program will display “k = 200” and that’s what it will become.
 // 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: - |


Interesting (?) | Should be continued

')

Source: https://habr.com/ru/post/277145/


All Articles