Translated using translate google. The original language is Russian. Original page https://hi-aga.ru/index.php/homepage/begin-21-30
Before solving the next example, let's look at how variables are declared and used in SP-Forth. Since operations with a large amount of data on the stack becomes extremely difficult, they will be useful to us. To do this, use the reserved words VARIABLE and FVARIABLE. The first is for integers, the second is for real numbers. If anyone doesn't know what a variable is, it's simply a piece of memory into which a value (a number for integer variables, text for string variables, or a combination of both for structures) is written, read, or modified. In fact, any data, no matter what: simple variables, structures or even files, they are all encoded exclusively as numbers, and in binary format (zeros and ones).
Let's create two variables
FVARIABLE FVAR \ FVAR variable of real type
VARIABLE VAR \ VAR variable of integer type
Now let’s initialize these variables, that is, assign an initial value.
1234E-2 FVAR F!
Ok
4552249 VAR!
Ok
The code “1234E-2” is already familiar to us, it simply pushes the number “1234E-2” onto the real stack, FVAR leaves the address of a real variable with this name, and as a result “F!” - writes the value to the address. Integer assignment looks simpler, but the essence is the same. First the number goes onto the stack. The word VAR also leaves the address of an integer variable on the stack. And the operator “!” writes the value to the address. - Exclamation point. And it reads “F@” and “@” respectively. Now we count and display the values of the variables created and initialized above.
VAR @ .
4552249 Ok
Operator "." - dot, prints an integer number on the screen, and “F.” - real. You can notice the Forth logic in the names of the operators; by adding a capital "F", many of the operations become applicable to real operands. Let us demonstrate the above using the example of outputting the value of a real variable.
FVAR F@F.
12.340000 Ok
VAR and FVAR are just names, they can be anything - it's just a convenient Forth style notation. Now we can move on to the next task.
Example 21. Using the coordinates of three points forming a triangle, calculate its perimeter and area. First, let's create variables for the coordinates and sides of the triangle.
FVARIABLE FX1
FVARIABLE FY1
FVARIABLE FX2
FVARIABLE FY2
FVARIABLE FX3
FVARIABLE FY3
FVARIABLE FA
FVARIABLE FB
FVARIABLE FC
Since here we are using only real variables, “F” can be omitted and everything can be rewritten in one line, not forgetting about spaces, but we will not do this in order to follow the uniform style of naming variables, for educational purposes.
FVARIABLE FX1 FVARIABLE FY1 FVARIABLE FX2 FVARIABLE FY2 FVARIABLE FX3 FVARIABLE FY3
FVARIABLE FA FVARIABLE FB FVARIABLE FC
: B21 ( X1 Y1 X2 Y2 X3 Y3 -> P S ) \ P=(A+B+C)/2 S=SQRT{P*(P-A) *(P-B) *(P-B)}
FY3 F! FX3 F! FY2 F! FX2 F! FY1 F! FX1 F! \FX1 FY1 FX2 FY2 FX3 FY3 ->
FX1 F@ FY1 F@ FX2 F@ FY2 F@ B20 FDUP FA F! \A
FX2 F@ FY2 F@ FX3 F@ FY3 F@ B20 FDUP FB F! \A B
FX1 F@ FY1 F@ FX3 F@ FY3 F@ B20 FDUP FC F! \A B C
F+ F+ FDUP \ A+B+C=P P
2E F/ \ P (A+B+C)/2=p
FDUP FA F@ F-\P p p-A
FOVER FDUP FB F@ F- \ P p p-A p p-B
FSWAP FC F@ F- \ P p p-A p-B p-C
F* F* F* FSQRT \ P SQRT{p*(p-A)*(p-B)*(p-C)}=S
;
Line No. 1 is the name of the word with comments.
The second is storing coordinates in the corresponding variables.
From the third to the fifth - calculating the sides of a triangle and storing them in variables A, B, C. Here we do not calculate the distances between points (sides of the triangle), but use the previous problem, in which this problem was solved by simply calling it with the parameters of problem No. 21.
Sixth line: calculating the perimeter and duplicating it
Seventh calculation of semi-perimeter.
From the eighth to the tenth - calculating the factors in the area formula.
Eleventh – calculation of area.
Let's check the work of the word on the coordinates: (1.1; 1.1) (6.1; 1.1) (6.1; 4.1). This is a right triangle with legs 3 and 5.
11E-1 11E-1 61E-1 11E-1 61E-1 41E-1 B21 F. F.
7.5000000 13.830952 Ok
S=3*5/2=15/2=7.5. The hypotenuse is equal to sqrt(3^2+5^2)= sqrt(9+25)= sqrt(34)= 5.83095. P= 5.83095+3+5= 13.83095.
What is true is that the task is programmed correctly.
If you have problems or errors, enter the code in the sequence below.
S" lib\include\float2.f" INCLUDED
: B20 ( X1 Y1 X2 Y2->R )
FROT F- FDUP F*
FSWAP FROT F- FDUP F*
F+ FSQRT ;
FVARIABLE FX1 FVARIABLE FY1 FVARIABLE FX2 FVARIABLE FY2 FVARIABLE FX3 FVARIABLE FY3
FVARIABLE FA FVARIABLE FB FVARIABLE FC
: B21 ( X1 Y1 X2 Y2 X3 Y3 -> P S ) FY3 F! FX3 F! FY2 F! FX2 F! FY1 F! FX1 F! FX1 F@ FY1 F@ FX2 F@ FY2 F@ B20 FDUP FA F! FX2 F@ FY2 F@ FX3 F@ FY3 F@ B20 FDUP FB F! FX1 F@ FY1 F@ FX3 F@ FY3 F@ B20 FDUP FC F! F+ F+ FDUP S" 2E" >FLOAT DROP F/ FDUP FA F@ F- FOVER FDUP FB F@ F- FSWAP FC F@ F- F* F* F* FSQRT ;
11E-1 11E-1 61E-1 11E-1 61E-1 41E-1 B21 F. F.
There are no comments in the code to avoid copying errors. The first line connects the library for working with real numbers (type float2). Next is the code from the previous example that we are using (in other languages this is called a function). Then variables are declared where we store the coordinates and calculated lengths of the sides. And finally, a compressed word code that calculates the perimeter and area of a triangle, and calls it with pre-prepared parameters.
Example 22. A fairly classic task of exchanging content between two variables. In Forth, creating variables in this case is not even necessary.
: B22 (A B -> B A) SWAP ;
5 4 B22
Ok (4 5)
For real arguments.
: B22 (A B -> B A) FSWAP ;
Now let's write with variables:
VARIABLE A
VARIABLE B
: B22 ( -> ) \ exchange the contents of two variables A and B
A @ B @ A ! B! ;
How to check the work?
15 A! 50 B! \ First we initialize the variables. A=15, B=50
Ok
B22 \ Calling the variable exchange function
Ok
A@. B@. \ Print the contents of the variables after the exchange
50 15 Ok
Exchange of real variables.
FVARIABLE FA
FVARIABLE FB
: B22 ( -> ) \ content exchange for two variables A and B
FA F@ FB F@ FA F! FB F! ;
Let's check. We initialize the variables, call the word and print the result. Everything is according to the standard scheme.
35E-1 FA F! \ A=3.5
Ok
77E-1 FB F! \B=7.7
Ok
B22
Ok
FA F@ F. FB F@ F.
7.7000000 3.5000000 Ok \ A=7.7 B=3.5
We ended up with four words with the same names, and each one replaces the previous one, which the Forth system – SP-Forth – warns us about with the message “B22 isn’t unique ()”. If you are going to use several variants of these words, then you need to call them by different names. For example, as mentioned earlier, adding "F" to the beginning of words that are intended to work with real arguments. Of course, in this example, the first two options (without using variables), in which the body consists of only one operator, do not need to be formatted into separate Forth words. Just remember about the possibility of such manipulations on the stack - this is one of the strengths of Forth.
Example 23. Exchange the values of three variables A, B, C according to the following scheme: A -> B -> C -> A.
VARIABLE A VARIABLE B VARIABLE C
If you have all the examples in one file, then you don’t need to declare variables every time. Add only a new one, in this case it is “VARIABLE C”. Otherwise there will be messages from the Fort system
A isn't unique()
B isn't unique()
This is not critical, SP-Forth will continue to work, but, firstly, new variables will be created with the same names, which will simply increase memory consumption uselessly, and secondly, if you needed the previous value, they will no longer be available by name.
: B23 ( -> ) \ stack notation is still empty
A @ B @ C @ \ -> A B C
A! C! B! \ C=A B=C A=B
;
1 A! 2 B! 3C! B23 A @ . B@. C@.
3 1 2 Ok
A=3, B=1, C=2.
A real argument requires corresponding variables.
FVARIABLE FA FVARIABLE FB FVARIABLE FC
: B23 ( -> ) \ stack notation is still empty
FA F@ FB F@ FC F@ \ -> A B C
FA F! FC F! FB F! \ C=A B=C A=B
;
1E FA F! \FA=1
Ok
2E FB F! \FB=2
Ok
3E FC F! \FC=3
Ok
B23
Ok
FA F@ F. FB F@ F. FC F@ F.
3.0000000 1.0000000 2.0000000 Ok \ FA=3 FB=1 FC=2
Example 24. Similar to the previous one with content moving according to the scheme: A -> C-> B -> A.
We use the variables from the previous example.
: B24 ( -> ) \ stack notation is empty
A @ B @ C @ \ -> A B C
B! A! C! \ B=C A=B C=A
;
1 A! 2 B! 3C! \ initialization A=1 B=2 C=3
Ok
B24\ call our function
Ok
A@. B@. C@. \ A=2 B=3 C=1
2 3 1 Ok
For a real argument.
: B24 ( -> ) \ stack notation is empty
FA F@ FB F@ FC F@ \ -> A B C
FB F! FA F! FC F! \ B=C A=B C=A
;
Let's check it using similar data.
1E-1 FA F! \FA=0.1
Ok
2E-1 FB F! \FB=0.2
Ok
3E-1 FC F! \FC=0.3
Ok
B24
Ok
FA F@ F. FB F@ F. FC F@ F. \ FA=0.2 FB=0.3 FC=0.1
0.2000000 0.3000000 0.1000000 Ok
Example 25: Calculate the value of a function given an argument. Let’s omit the integer version; you can write it yourself by analogy.
: B25 ( X -> F[X] ) \ F(X)=3*X^6-6*x^2-7
F**2\X -> X^2
FDUP -6E F* \ X^2 -> X^2 -6*X^2
-7E F+ \ X^2 {-6*X^2} -> X^2 {-6*X^2-7}
FSWAP \ X^2 {-6*X^2-7} -> {-6*X^2-7} X^2
3E F** \ {-6*X^2-7} X^2 -> {-6*X^2-7} {X^2}^3=X^6
3E F* F+ \ {-6*X^2-7}+{3*X^6}
;
1E B25 F.
-10.000000 Ok \ F(1)=-10
1E-3 B25 F.
-7.0000060 Ok \ F(0.001)=- 7.0000060
But if the argument is equal to zero, it gives an error (raising zero to a power):
0E B25 F.
EXCEPTION! CODE:C0000090 ADDRESS:0055384E WORD:F**
USER DATA: 007005BC THREAD ID: 00002120 HANDLER: 0019EF98
STACK: (0) 5BF752DB 00328000 76F066DD 0019FFDC 74C30400 00328000 [74C30419]
RETURN STACK:
0019EF84 : 0056DC53 B25
…………………………………………………………
0019EFB4: 0056BC66 (INIT)
END OF EXCEPTION REPORT
S" 0E >FLOAT DROP B25 F.
^0xC0000090L FLOAT_INVALID_OPERATION
But the second time I got a different answer:
0E B25 F.
infinity Ok
After this, it is better to restart the system or reconnect libraries for working with real numbers, otherwise it will not recognize them:
1E B25 F.
1E B25 F.
^ -2003 WORD OR FILE NOT FOUND
0E B25 F.
0E B25 F.
^ -2003 WORD OR FILE NOT FOUND
1E-3 B25 F.
1E-3 B25 F.
^ -2003 WORD OR FILE NOT FOUND
Example 26. Twin brother of the previous one. The difference is in the formula. We will also consider only the real argument.
: B26 ( X -> F[X] ) \ F[X]=4*{X-3}^6-7*{X-3}^3+2
3E F-\X -> X-3
3E F**\X-3 -> (X-3)^3
FDUP -7E F* \ (X-3)^3 -> (X-3)^3 {-7*(X-3)^3}
FSWAP F**2 \ (X-3)^3 {-7*(X-3)^3} -> {-7*(X-3)^3} [(X-3)^3]^2
4E F* \ {-7*(X-3)^3} (X-3)^6 -> {-7*(X-3)^3} 4*(X-3)^6
2E F+ F+ \ {-7*(X-3)^3} 4*(X-3)^6 -> {-7*(X-3)^3}+4*(X-3)^6+ 2
;
4E B26 F.
-1.0000000 Ok
Be careful when exponentiating, it is a dangerous operation and causes many errors.
Example 27. In Forth, an additional variable is not needed here. For those who like to do everything strictly according to the instructions, the task is to rewrite the code yourself.
: B27 (A -> A^2 A^4 A^8)
DUP *\A -> A^2
DUP DUP *\A^2 -> A^2 A^4
DUP DUP *\A^2 A^4 -> A^2 A^4 A^8
;
Examples of word work:
2 B27
Ok (4 16 256)
3 B27
Ok (9 81 6561)
No comments.
With a real argument the task is no more difficult. We replace all operators with the appropriate ones by simply adding “F”.
: B27 ( A -> A^2 A^4 A^8 )
FDUP F*\A -> A^2
FDUP FDUP F* \ A^2 -> A^2 A^4
FDUP FDUP F* \ A^2 A^4 -> A^2 A^4 A^8
;
2E B27 F. F. F.
256.00000 16.000000 4.0000000 Ok \ Printing order is reversed
3E B27 F. F. F.
6561.0000 81.000000 9.0000000 Ok
Example 28. Similar to the previous problem and a little more complicated.
: B28 ( A -> A^2 A^3 A^5 A^10 A^15 )
DUP DUP *\A -> A A^2
SWAP OVER *\A A^2 -> A^2 A^3
OVER OVER * \ A^2 A^3 -> A^2 A^3 A^5
DUP DUP * \ A^2 A^3 A^5 -> A^2 A^3 A^5 A^10
OVER OVER * \ A^2 A^3 A^5 A^10 -> A^2 A^3 A^5 A^10 A^15
;
2 B28
Ok (4 8 32 1024 32768)
3 B28
Ok (9 27 243 59049 14348907)
Do not forget that the power function grows very quickly and with a large base, overflow will quickly occur, as a result the answer will be incorrect. So, for example, when A=10, already the 10th power is not calculated correctly.
10 B28
Ok ( 100 1000 100000 1410065408 2764472320(-1530494976) )
For a real argument.
: B28 ( A -> A^2 A^3 A^5 A^10 A^15)
FDUP FDUP F* \ A -> A A^2
FSWAP FOVER F* \ A A^2 -> A^2 A^3
FOVER FOVER F* \ A^2 A^3 -> A^2 A^3 A^5
FDUP FDUP F* \ A^2 A^3 A^5 -> A^2 A^3 A^5 A^10
FOVER FOVER F* \ A^2 A^3 A^5 A^10 -> A^2 A^3 A^5 A^10 A^15
;
2E B28 F. F. F. F. F.
32768.000 1024.0000 32.000000 8.0000000 4.0000000 Ok \ again reverse order when printing
3E B28 F. F. F. F. F.
14348907. 59049.000 243.00000 27.000000 9.0000000 Ok
Apparently the word works correctly.
Example 29. Convert degrees to radians.
The integer version will be 100 times larger, so its code is not given. If necessary, you can get it from the code for the real argument.
: B29 ( A{DEG} -> X{RAD} ) \ X=A*Pi/180
314E-2 F*\A -> 3.14*A
180E F/ \ 3.14*A -> 3.14*A/180
;
The tests below are correct.
90E B29 F.
1.5700000 Ok
360E B29 F.
6.2800000 Ok
Example 30. The reverse problem of converting from radians to degrees.
: B30 ( A{RAD} -> X{DEG} ) \ 180*A/Pi
180E F*\A -> 180*A
314E-2 F/ \ 180*A -> 180*A/Pi
;
Word tests B30.
314E-2 B30 F.
180.00000 Ok
628E-2 B30 F.
360.00000 Ok
Small tricks to optimize work with the SP-forth programming system.
If you plan to use the last two words in your work, then you can name them more practically and add them to your Forth extension file.
For example, RAD>DEG and DEG>RAD (the “>” sign means translate, transfer depending on the context of use).
An example of the contents of such a file:
\ Connecting a library for working with real numbers
S" lib\include\float2.f" INCLUDED
VARIABLE A VARIABLE B VARIABLE C \ Frequently used variables
FVARIABLE FA FVARIABLE FB FVARIABLE FC
: DEG>RAD ( A{DEG} -> X{RAD} ) \ X=A*Pi/180 - degrees to radians
314E-2 F*\A -> 3.14*A
180E F/ \ 3.14*A -> 3.14*A/180
;
: RAD>DEG ( A{RAD} -> X{DEG} ) \ 180*A/Pi – radians to degrees
180E F*\A -> 180*A
314E-2 F/ \ 180*A -> 180*A/Pi
;
: Arithmetic_mean ( A B -> [A+B]/2 ) F+ 2E F/ ; \ average
: Geometric_mean ( A B -> SQRT[A*B] ) F* FSQRT ; \ geometric mean
: R2D ( X1 Y1 X2 Y2-> R ) \ R= Square_Root((X2-X1)^2+(Y2-Y1)^2)
\calculating the distance between two points through their coordinates on the plane
FROT F- FDUP F* \ X1 Y1 X2 Y2-> X1 X2 (Y2-Y1)^2
FSWAP FROT F- FDUP F* \ X1 X2 (Y2-Y1)^2 -> (Y2-Y1)^2 (X2-X1)^2
F+ FSQRT \ (Y2-Y1)^2 (X2-X1)^2 -> R
;
Name the file as you like, but be sure to have the extension “.F”, then you can run it and it will open in SP-Forth, provided that you have it installed. Then you won't have to perform the routine tasks that you include in this file every time.
To simplify the interface for interacting with written words, for example, displaying calculation results for words with many output parameters, you can apply the following technique, described below.
Let's look at example No. 28.
2E B28 F. F. F. F. F.
Here "B28 F. F. F. F. F." replace with a word
: B28. ( A^2 A^3 A^5 A^10 A^15 -> ) B28 F. F. F. F. F. ;
This option is preferable not only because of simplification, but also based on the fact that over time you will forget how a word works, and it is not reasonable to understand how a word works every time, and why overload your brain with useless little things? Operator "." - a point in Forth is always associated with printing on the screen, so “Name.” - logically means printing the results of the word “Name”. Just don’t forget to write the corresponding word before executing it; words are not written automatically in Forth. You can also include a verbal description of the results, for example,
: B28. ( A^2 A^3 A^5 A^10 A^15 -> ) B28
.” A^15= “ F. CR \ CR is the newline operator
.” A^10= “ F. CR
.” A^5= “ F. CR
.” A^3= “ F. CR
.” A^2= “ F. CR
;
The final call to word B28 will look, taking into account simplification, like this:
2E B28.
This is how we share the algorithm of the word with its primitive interface for displaying results. In this case, if it is necessary to modify something, one will not interfere with the other and, of course, the code becomes more concise and understandable.