“Throwing pebbles into the water, look at the circles they form; otherwise, such a throwing will be empty fun. "
K. PrutkovOne day, aimlessly wasting work time and money of the employer through surfing the Internet, I came across a description of the language
Whenever and for some time I was fascinated. The language is striking in its insane simplicity. Its principles are as follows:
1) The lines of the program code will necessarily be executed sometime, but the order of their execution is in no way connected with the order in which they are written.
2) Variables? We do not even have control over the order of execution, we do not need any variables.
3) Data structures? Yes, you're kidding!
')
That is, the program is treated as a set (pool) of lines for execution and the interpreter selects a line from there at random, executes its commands and throws it out of the pool. And so long as there is nothing left in the pool. It must be admitted that the author of this madness almost sustained the concept. Almost, because you can still organize the execution order in the program, just as you can start variables using the ability to add rows to the pool of executables.
So, the language has the following constructions:
View
code lineline-number statement;
Expressions are composed of commands, separated by commas. Among them:
Line numberline-number#number-of-times-to-add/remove
For example, the command in line 1
1 4,5#3,-6;
Adds 1 row with number 4 to line of execution pool, 3 rows with number 5 and removes row 6 from pool.
Note that a program with an infinite loop will look concise and therefore brilliant
1 1;
The interpreter will select the first line from the pool (no other one is there anymore) and add it back to the pool. By the way, a very useful design, as will be seen later.
Output on displayJust a print command that prints what is in brackets to the screen.
In addition to these commands, there are the following constructions:
deferIf the expression in parentheses returns “true”, the command will not be executed, and the string will be returned to the pool. for example
1 defer (2) 3;
As long as row 2 is in the pool, the command to add row 3 to the pool will not be executed.
againIf the expression in brackets returns "true", the command will be executed, but the string will return to the pool. For example:
1 again (2) 3;
Line 3 will always be added to the pool, but if there is line 2 in the pool, line 1 will not be deleted from the pool after that.
I note that expression (2) is equivalent to (N (2)> 0), where N () is a function that returns the number of rows of the name number in the pool.
forgetIf the expression in brackets returns "true", the command will not be executed and the string will be removed from the pool. For example:
1 forget(2) 3;
The author of the language threw this expression out of the language, but I had to return it, as described below.
again, defer & forget can be combined, but each of the modifiers should occur only once in a string.
Actually everything, the whole language. There are two programs on the site describing the language:
99 bottles of beer
1 defer (4 || N(1)<N(2) && N(2)<N(3)) print(N(1)+" bottles of beer on the wall, "+N(1)+" bottles of beer,"); 2 defer (4 || N(1)==N(2)) print("Take one down and pass it around,"); 3 defer (4 || N(2)==N(3)) print(N(1)+" bottles of beer on the wall."); 4 1#98,2#98,3#98;
and Fibonacci Numbers (calculates the Fibbonacci series from 1 to the 100th element ... well ... if you wait):
1 again (1) defer (3 || N(1)<=N(2) || N(7)>99) 2#N(1),3,7; 2 again (2) defer (3 || N(2)<=N(1) || N(7)>99) 1#N(2),3,7; 3 defer (5) print(N(1)+N(2)); 4 defer (5) print("1"); 5 4,-3,7; 6 defer (4) 3; 7 7; 8 defer (N(7)<100) -1#N(1),-2#N(2),-7#100,-3; 9 defer (3 || 6) 1,3;
If the first program is pure self-indulgence, then the second one does something meaningful and this is surprising. It would seem that, by definition, nothing can get out of such pampering. However, this is it.
And I wanted to do something with it. The result was somewhat unexpected, but first things first.
It immediately catches the eye that in these programs it is inconvenient: in order to calculate the Fibonacci series not up to the hundredth, but, say, up to the 20th element, in the program you need to change the text in four places. This is somehow annoying. Let's make so that the program could accept input parameters. For example:
1 again (1) defer (3 || N(1)<=N(2) || N(7)>( @1 - 1 )) 2#N(1),3,7; 2 again (2) defer (3 || N(2)<=N(1) || N(7)>( @1 - 1 )) 1#N(2),3,7; 3 defer (5) print(N(1)+N(2)); 4 defer (5) print("1"); 5 4,-3,7; 6 defer (4) 3; 7 7; 8 defer (N(7)<@1) -1#N(1),-2#N(2),-7#100,-3; 9 defer (3 || 6) 1,3;
Save the program text in the file fib.src. The program call will look like this:
java -cp Whenever.jar Whenever fib.src 20
Already good. To consolidate success, I wrote a program to find the greatest common factor of two positive integers, here it is:
1 defer(2) again(N(3) - (N(3)/N(4))*N(4) != 0) 6#N(3),-3#N(3),3#N(4),-4#N(4),4#(N(6) - (N(6)/N(3))*N(3)),-6#N(6); 2 3#( @1 - 1 ),4#( @2 - 1 ),-6; 3 3; 4 4; 5 defer(1) print("NOD of " + @1 + " and " + @2 + " is " + N(4)),-3#N(3),-4#N(4); 6 6;
When you call a program, you need to specify two numbers, and more, and more. We analyze this program;
Lines 3 and 4 are used as memory cells to store two numbers. More precisely, how the variables use the number of entries in the pool of the corresponding rows. Line 6 is used as an intermediate variable in the same perverted way (the language leaves no other).
Line 2 starts the whole process. It adds N rows 3 and M rows 4 to the pool and deletes row 6 from the pool. Then it is deleted from the pool permanently.
Line 1 itself does all the work. The execution of line 1 is postponed until the moment line 2 is present in the pool (defer (2)). In addition, the string is NOT thrown out of the pool until the NOD (again) is found. The expression in brackets will give the remainder of dividing the number of lines 3 by the number of lines 4 (using integer mathematics). Actually actions in line 1 are performed from left to right:
- As many rows 6 are added to the pool as at this step in the row pool 3
- Removed from the pool all lines 3
- As many lines are added as there are 4 lines at a time (these two commands can be collapsed into one -3 # (N (3) - N (4))
- Remove all lines 4
- Add lines 4 in the number == the remainder of dividing a larger number by a smaller number. Again, these two teams can be slammed into one.
- We remove from the pool all lines 6 (the violinist is not needed)
Line 5 displays the result and cleans up after itself, deleting from the pool all that is left there. Its execution is postponed until line 1 is present in the pool, and it will remain there until the moment the GCD is located. I note that in the original version of the language, print cannot be combined with other commands, it would be necessary to write there
5 defer(1) print("NOD of " + @1 + " and " + @2 + " is " + N(4)); .... 7 defer(5) -3#N(3),-4#N(4);
Yeah, I thought. And let's do it so that when you add a string to the pool, it can pass parameters. Since the parentheses and commas are already used in the syntax and I didn’t want to rewrite everything very much, I used the square brackets and the percent sign% for the parameter separator.
Here there is one subtlety. When adding a line to the pool, in fact, another line should be created with the new number, and not the number of executions for an existing one increase. (Actually, for “purity”, it is necessary to increase the number of executions for lines with the same parameters, but I have not yet implemented this).
Then I suddenly had another dimension, another variable - the current line number. In order to be able to use it, I entered another keyword - self.
Hooray! Can I do something useful now? For example, rewrite the same Fibonacci series:
1 forget(self>@3) print("iter: " + self + " value: " + @1 ),1[@1+@2%@1%@3];
Three parameters are transferred to the program - 1, 0 and the number of the number to which it is necessary to calculate. For good, only the third parameter is significant and the program can be rewritten so that it only accepts it. Then, by the way, you can get rid of self, but the program will grow horribly to two whole lines, and this is not very beautiful.
Remarks: I had to 1) restore the forget function and 2) make it so that print was executed in a number of other commands (in the original there could only be a print or other commands).
And here is the GCD:
1 forget( @2 == 0 ) 1[@2%(@1 - (@1 / @2) * @2)],2[@2%(@1 - (@1 / @2) * @2)]; 2 forget( @2 > 0 ) print("Nod is: " + @1 );
Again, you can make it so that the execution does not depend on which number is the larger parameter, but I'm lazy.
And for a snack, check whether the number is simple:
1 defer(5) forget( @2 == 0 || @2 == 1 ) 4[@1%(@2 - 1)],2[@1%(@2 - 1)],3[@1%(@2 - 1)]; 2 defer(5) forget( @2 == 0 || @1 != (@1 / @2) * @2 || @1 == @2 || @2 == 1 ) print( "Number " + @1 + " is not prime"); 3 defer(5) forget( @2 != 1 ) print( "Number " + @1 + " is prime"); 4 defer(5) forget( @2 == 0 || @1 == (@1 / @2) * @2 ) 1[@1%@2]; 5 1[@1%@1];
Here I use another trick: if the parameter is not specified, it becomes zero.
These are the pies with kittens. Something tells me that you can try to solve combinatorial puzzles with the help of this garbage. But to come up with solutions is too lazy. Although…