📜 ⬆️ ⬇️

Schizoid programming language of self-learning algorithms "Author"

I want to tell the world a fundamentally new programming language, which has no analogues in the whole world.
At one time I was obsessed with the idea of ​​artificial intelligence. When I became a programmer, I realized that everything is not so simple, and it can even be said much more complicated than it seemed. I did not stop working on the programs and mastered all the programming languages ​​known today. I tried to shift as much of my own thinking processes to the machine as possible, and it turned out that they occupied large amounts of code and knew very little themselves. I tried to force the program to learn, draw conclusions, and use them in the future. But all programming languages ​​have the same disadvantage - they cannot consider algorithms as data. Programs cannot learn from the fact that they do not have access to themselves.
So I came to the conclusion that “C” needs to make such an interpreter, the programs on which will be able to access themselves. The idea is simple - to learn, you need to change, and for this you need to have access to your own code. Now learning becomes at least possible. After any intervention in the body of the algorithm, it becomes different from its source code of the program and these changes need to be saved. The interpreter becomes the author of a new program. Therefore, the language uses the following culture. The file with the code "* .txt" has its own duplicate "* .code", which is created automatically. Such a pair is called a module. When loading the module, the “Author” selects, from a pair, the file recorded later, and in the absence of one of the pair, it is not necessary to choose. Thus, the source code always remains intact, and all changes that have occurred will be visible to the programmer in a duplicate file. The start module must have an entry point (main) and you need to drag the file to “author.exe” to start. Any module can request reloading of other modules (#include <me.txt>). Modules can also be loaded and unloaded during code execution with the include (“me.txt”) and uninclude (“me.txt”) commands. Upon completion of the algorithm, the “Author” unloads the starting module. Each module has a counter for the number of modules that requested it, and if it reaches zero, the module unloads its code into a duplicate file. Before overwriting the duplicate file of a module, it comes only when at least one change occurred in it (the module) or there was no duplicate at all. Thus, it is possible to prescribe the connection of modules as you like, without thinking about what the connection tree will be, the module will always be in memory in one instance. The main thing is not to connect yourself.
The syntax of the C language is similar. The module contains many functions. In the files, the program is presented as text. But when loading, there is a transformation and development of the code to the dynamic structure of the algorithm scheme. For each function, a stream graph is created and the program already accesses it. When the module is unloaded, the inverse transformation takes place. The “author” even tries to adhere to the programming style, so that the code is not in one line. Thus, it is possible, for example, to make a function that can count the number of conditions or cycles in a specified function, for example, in itself. I suppose it will be interesting for the reader to look at such a function; therefore, despite the moderator’s ban, I will give it:
 // noproblem.txt
 // Program to calculate your own cycles and conditions
 void main () {
	 f = getFunction (getThisFunctionName ());  // access to yourself
	 tree = f.export ();  // convert to algorithm tree
	 counterIF = 0;
	 counterWHILE = 0;
	 counterDO = 0;
	 counterFOR = 0;
	 // organize traversal of the tree of nested loops and conditions
	 access = {};
	 do {
		 n = tree.getRowSize (access);
		 access.push (n);
		 while (access.size ()) {
			 n = access.pop ();
			 --n;
			 if (n <0) continue;
			 access.push (n);
			 sub = tree.getSub (access);
			 type = "";
			 if (typeof (sub) == "program") type = sub.typeof ();
			 if (type == "if") ++ counterIF;
			 if (type == "while") ++ counterWHILE;
			 if (type == "do") ++ counterDO;
			 if (type == "for") ++ counterFOR;
			 break;
			 }
		 } while (access.size ());
	 trace ("The function algorithm" + f.getName (). export () + "contains:");
	 trace ("Conditional Branches:" + counterIF);
	 trace ("while cycles:" + counterWHILE);
	 trace ("do:" cycles + counterDO);
	 trace ("Cycles for:" + counterFOR);
	 getstring ();
 }

 The algorithm of the function "main" contains:
 Conditional Branches: 6
 While loops: 1
 Cycles do: 1
 For cycles: 0
Variables in a language do not have a type binding like PHP and JS. They do not even have to declare. The type specified during the declaration is nothing more than a comment. A variable is also created when it is used in a write construct (a = 0;). When read from an unknown variable, the type “void” is returned. The value of any variable can be of the type: void, int, float, double, digit, char, string, interval, vector (set), set (unique set), map (associative array), program (algorithm tree), function (algorithm scheme), graf, module. Pointers are also present in the language, but due to the schizoid feature, they are organized as a string with data for accessing the variable. A variable can contain a pointer to itself (p = & p; p = **** p;). You can take a string with the name of the value type. “Typeof (typeof (#)) ==” string ”” is always true.
In the language, you can use the special operand "#". It returns the “void” type and serves as a symbolic designation of a hidden block, which, like any other operand, can be exchanged for an operator tree structure of any complexity. When the value of “void” falls into the condition of a ternary operator, the interpreter, at each execution time, is randomly determined between the truth and the bed. The expressions "#? 1: 0" and "rand ()% 2? 1: 0" are similar. But there is a serious difference between "if (#);" and "if (#? 1: 0);".
When a value of type “void” falls into a condition, schizophrenia occurs. The current execution point of the algorithm, at this moment, is divided into two. One passes in truth, the other in a lie. In further interpretation, as necessary, divided into two and the entire memory card. Thus, it can be said that each execution point has its own separate memory card.
 // Example schizoid program:
 main () {
	 n = 0;
	 if (#) n = 1 + 1;
	 n + = 10;
	 trace (n);
 }

 ten
 12
Since the console does not divide, the output to it is carried out in turn in an undefined order.
The division of the point of execution can be done with the special function “rozpad ()”. It takes a set of values ​​and returns each of them at its point of execution. Thus, the expressions "if (#);" and "if (rozpad ({1,0}));" are similar. The expression "n = rozpad ({1,2,3}) + rozpad ({10,150});" will cause schizoid disintegration into six processes, with different values ​​of the variable "n": 11, 12, 13, 151, 152 and 153.
To determine between all variants of processes at random, you need to call the function "define ();". Thus it is possible to reduce all points of performance into one.
The time has come to focus on the schizoid features of the language. So I call the ability to process ambiguous data. All modern programming languages ​​use variables to store a variety of data, but all their values ​​are in a single version. "Author" also allows you to store ambiguous values ​​in any variable. Thus, the algorithms are written in this language, ready at any time, will face the emergence of ambiguity in the process of solving the problem. But, often, it happens that the task itself, in human language sounds ambiguous. For example, such a task: "Given an array of 4 - 5 numbers, you need to sort it."
The ambiguity of the solution algorithm proceeds from the very formulation of the problem. First, the size of the array is not specified unambiguously, but rather its size is specified in two versions. Suppose we can determine the values ​​of the numbers of arrays. Secondly, it is possible to sort an array in ascending and descending order. Here is the program for solving this problem:
 // sort.txt
 main () {
	 if (#) m = {6,2,9,1};  else m = {3,7,21,45,8};
	 m.sort ();
	 if (#) m.reverse ();
	 trace ("Result:" + m.export ());
 }

 Result: {9,6,2,1}
 Result: {45,21,8,7,3}
 Result: {1,2,6,9}
 Result: {3,7,8,21,45}
Operand “#” returns the value “void”. When it gets into the conditional branch structure, of which, by the way, any cycle consists, the execution point of the algorithm is divided into two. One passes through the branch of truth, the other through the branch of lies. For each point of execution there is its own memory card, that is, with respect to one interpretation position, all variables contain single-valued values. Thus, after the execution of the first line of the program, the computer is divided into two. They both continue to execute the same program, but with different values ​​of the variable “m”. In the second line of the program, each computer sorts its array in ascending order. Next comes the division of each computer for another two. One pair goes through the command to reverse the array, and the other does not. Then all computers report to their shared screen.
Now consider the schizoid division in another example. It is necessary to find which combination of the values ​​of the variables “a” and “b” will total the number 20, while “a” may be one of the set {5,3,7}, “b” may be one of the set {15, 17}. The programmer will immediately see two nested loops here. And here is what the program on the Author looks like:
 void main () {
	 a = rozpad ({5,3,7});
	 b = rozpad ({15,17});
	 if (a + b! = 20) OFF;
	 trace ("a + b ==" + a + "+" + b);
 }

 a + b == 5 + 15
 a + b == 3 + 17
In the first line of the program, the execution point is divided into three parallel, in each of which the function “rozpad ()” returns its value, one of the specified set. In the second line there is a similar division of each point of execution into two. Thus, the variables will get their combination of values ​​from the specified sets. Next comes the condition that all “computers” in which “a + b! = 20” go to the “OFF” command, by which they disappear. Thus, only those “computers” that reach “a + b == 20” and the value of their variables are displayed on a shared screen will reach the report command.
Often you have to make a choice and choose one of the alternatives. But in order to make any choice, you need to have one, clearly formulated, arbitrarily complex criterion. And what if the selection criterion is unknown or not specified? Random, equiprobable choice. Here is a universal and simplest criterion. To indicate in the algorithm the need to select one process variant with its own data, there is a built-in function / “define ()” command in the language. It selects one point of execution from the existing set at the time of its execution. How it works. The current process reaches the command “define ()” and stops. Another process that is not stopped is active from the list of parallel processes. When all the processes in the list are stopped, it means that the time has come to choose one of them. One execution point is selected from the list, and all other processes are closed. The selected process is launched for the further execution of the algorithm. Thus, regardless of the order of processing parallel processes, after the execution of the "define ()" command objectively, only one execution point is guaranteed to remain with its version of the data.
 main () {
	 trace ("Famous greetings:");
	 string str = rozpad ({"Hello.", "Hello.", "Good day."}));
	 trace (str);
	 define ();
	 trace ("I chose:" + str);
 }

 Famous greetings:
 Hey.
 Hello.
 Good day.
 I chose: Hi.
The following program determines one negative and one positive number from a given set of numbers:
 void main () {
	 i = rozpad ({- 3, -2, -1,0,1,2,3});
	 if (i <0) define ();  else define ();
	 trace (i);
	 define (-1);
	 getstring ();  // waiting for "Enter"
 }

 -3
 one
The first line of the code is divided into variants. In the second - the division into two groups and the definition of one option for each of the conditional groups. Next, we display the result and wait for pressing “enter”. The function “define ()” can take one numeric value. It defines the priority of definitions. The first occurrence is the definition that will get a larger number. The default is zero. If the order of definition is unimportant, you can use the same number. Here is an example of code for which the order of determination is important:
 void main () {
	 int n = rozpad ({- 2, -1,0,1,2,3});
	 if (n> = 0) define (1);
	 trace (n);
	 define (2);
	 trace ("OK");
 }

 -2
 -one
 Ok
 2
 Ok
It should be continued.

')

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


All Articles