Translated using translate google. The original language is Russian. Original page https://hi-aga.ru/index.php/homepage/begin-1-10

We will begin our programming practice with problems from the book by M. E. Abrahamyan “1000 programming problems Part I Scalar data types, control operators, procedures and functions” 2004. The author writes that you can get the problem book by e-mail: mabr@math .rsu.ru or for details refer to the web resource ptaskbook.com. I will not provide the text of the problems in order to exclude plagiarism. I think an explanation of the code with a description of the words (functions-programs) should be enough, otherwise, refer to the original source for the text of the problems.

Example 1. So, let's begin (for simplicity, at first we will consider all input parameters as integers, then we will rewrite the code for real arguments, where this is indicated in the problem statement). Here is the solution to the first problem:

: B1 ( A -> P ) 4 * ; \P=4*A

B is an abbreviation for BEGIN, which denotes the first group of tasks (we will continue to use this type of name in subsequent groups of tasks), then the example number is written together. Immediately after the name, a stack notation comment is written in parentheses. This is how it is done in Fort. In this case, Function Word B1 takes one parameter A (a four-byte integer) and leaves another of the same type. A is the side of the square - the function input, P is its perimeter - the value returned by the function. The body is very short, it simply multiplies the number at the top of the stack by 4 (don’t forget that Forth has reverse Polish notation, the operands come first, then the operation).

Now, to use our word, for example, to calculate the perimeter of a square with side 3, we use the following code:

3 B1.

12 Ok

Dot "." is a standard fort word that prints the number on top of the stack to the screen.

Let us recall the general form of the definition of a new word in Forth:

: Name-Words ( stack before execution -> after ) Code-Body-Functions ; \ a comment

Example 2. We need to calculate the area of ​​a square:

: B2 (A -> S) DUP *; \S=A^2

We simply duplicate the contents of the number on top of the stack (for which we use the Forth language operator - DUP) and multiply it by ourselves. This example can be formatted more beautifully for use in your future programs:

: SQR ( A -> A^2 ) DUP * ; \ A^2 – calculation of the square of a number, or

: ^2 ( A -> A^2 ) DUP * ; \ or

: **2 ( A -> A^2 ) DUP * ; \ difference is only in the name

You can use whichever one you like. Or all at once, that’s also possible.

Example 3. Using the sides of a rectangle, you need to calculate its Area and Perimeter:

: B3 ( A B -> S P ) \ ( S=A*B P=2*(A+B) )

    2DUP ( A B -> A B A B ) \ Word 2DUP, duplicates two numbers at once

    * ( A B A B -> A B A*B=S ) \ The area is calculated - it is simply the product of the sides

    ROT ROT ( A*B=S A B ) \ ROT operator pulls the 3rd parameter from the top to the top

            \applying it twice at the top we get A B and the Area calculated underneath

    + 2* ; \ add A and B, and multiply by 2 with the operator 2*, we get the perimeter

The word "2*" does the same thing as two words "2*", only shorter and simpler.

As a result, we get Area and Perimeter on the stack. To print the results on the screen from the examples, you just need to enter a period from the keyboard “.” and then press "Enter". First, the vertex will be printed, i.e. the perimeter, in this example, then the area will be repeated. To change the printing order, you can type the word SWAP, which swaps 2 numbers at the top of the stack ( A B -> B A), i.e., for example, to calculate the area and perimeter of a rectangle with sides 1 and 2, we enter the following:

1 2 B3 SWAP . .

2 6 Ok

The area is 1*2=2, and the perimeter is 2*(1+2)=6. The word works correctly and the area and perimeter are calculated according to the stack notation, and are output according to the conditions of the problem.

Example 4. You need to calculate the length of a circle knowing its diameter:

: B4 (D -> L) 314 *; \ L=Pi*D*100

The answer will be 100 times larger for integer data, so we get rid of the fractional part. Let's rewrite the code so that we can work with real numbers. To do this, you need to include the appropriate libraries in SP-Forth. Copy and paste the following two lines:

S" lib\include\float.f" INCLUDED

S" lib\include\float2.f" INCLUDED

But only the second line is possible.

Now, to enter a real number, say 0.5, you need to type the following on the keyboard:

5E-1

Before E is the mantissa (number), after the exponent (power). The mantissa and exponent can be either positive (no sign required) or negative (in this case the power is -1, which means 10 to the minus first power).

Once entered, the real number is placed on its corresponding stack, so we don't see it after printing the word Ok in parentheses, since it's a different stack for integers. To see it you need to enter “F.”. So, to check that everything works as it should, let's enter the code:

5E-1 F.

In response we will see:

0.5000000 Ok

The word "F." is the same as "." displays a number on the screen, but not from the integer stack, but from the real one.

Now we can rewrite example 4 for real arguments:

: B4 ( D -> L ) \ L=Pi*D

    314E-2 F* ;

Let's calculate the length of a circle with a diameter of 0.5 by typing the following:

5E-1 B4 F. \ call up a word that counts the length and “F.” prints the answer

1.5700000 Ok

Let's rework the first 3 examples in the same way for the case with real arguments, making them more universal.

Example 1:

: B1 ( A -> P ) 4E F* ; \P=4*A

The “*” sign is replaced by “F*”, the four is entered as a real number (the “F*” operation, unlike “*”, performs an operation on real numbers on the real stack). Now let’s check and calculate the perimeter of a square with a side of 0.5:

5E-1 B1 F.

2.0000000 Ok

Answer 2 (0.5*4=2) which is true.

This example can also be transformed by writing in the style:

: B1 ( A -> P ) \ P=4*A

    4E F*

;

But it’s so small and primitive that it’s hardly necessary; it’s easier and more concise to leave everything on one line. In more complex and large examples, the code must be written in a structured, understandable and, of course, uniform style.

Example 2:

: B2 ( A -> S ) FDUP F* ; \S=A^2

Again DUP turns into FDUP, multiplication as in the first case. Let's check how the word works. Let's calculate the area of ​​a square with side 0.5:

5E-1 B2 F.

0.2500000 Ok \ 0.5*0.5 = 0.25

Example 3:

: B3 ( A B -> S P ) \ ( S=A*B P=2*(A+B) )

    FOVER FOVER \ A B -> A B A B

                       \ The word FOVER, duplicates the word under the top of the stack to its top i.e. (A B -> A B A)

                       \ Repeating it 2 times we get ( A B -> A B A B )

    F* F. \ A B A B -> A B A*B=S

                       \ The area is calculated - it is simply the product of the sides

    F+ 2E F* F. ; \ add A and B, and multiply by 2 with the operator F*, we get the perimeter

Let's check the operation of word B3:

2E-1 3E-1 B3

0.0600000 1.0000000 Ok

As you can see below everything works correctly:

S = 0.2*0.3=0.06

P=2*(0.2+0.3)=2*0.5=1

0.2 and 0.3 can also be entered in the following form: 0.2E and 0.3E. You can verify for yourself that the word “F.” will display the same value.

A universal version of the same example, if you do not want to immediately print the processing results in a word:

: B3 ( A B -> S P ) \ ( S=A*B P=2*(A+B) )

    FOVER FOVER \ A B -> A B A B

    F*\A B A B -> A B A*B=S

                       \ The area is calculated - it is simply the product of the sides

    FROT FROT \ A B A*B=S -> A*B=S A B

    F+ 2E F* ; \ add A and B, and multiply by 2 with the operator F*, we get the perimeter

Let's check. Let's calculate the area and perimeter of a rectangle with sides 0.2 and 0.3:

2E-1 3E-1 B3

 Ok

F.F.

1.0000000 0.0600000 Ok

First it prints the perimeter then the area, to change the order as indicated in the stack notation you need to type the FSWAP command before printing the results, that is:

2E-1 3E-1 B3 FSWAP F. F.

0.0600000 1.0000000 Ok

The results are still correct.

You may ask why such difficulties? The code becomes universal, we separate the calculated part from the method of displaying data on the screen, it can be included in your libraries, and used in other tasks as a separate function.

As you may have already noticed, one remarkable property of Forth is that its function words not only accept any number of arguments, but also leave the desired number of results on the stack; not every language can boast of this.

Example 5. Here the volume of a cube and the area of ​​its lateral surface are calculated. First, let's work with an integer argument.

: B5 ( A -> V S ) DUP 2DUP * * SWAP DUP * 6 * ; \ V=A^3 S=6*A^2

Let's explain the code:

DUP 2DUP ( A -> A A A A )

2DUP, unlike DUP, duplicates 2 upper elements at once

* * ( A A A A -> A A*A*A=A^3 )

applying the multiplication operation twice results in a cube

SWAP ( A A^3 -> A^3 A )

SWAP simply swapped the top two elements on the stack

DUP * (A^3 A -> A^3 A*A )

squared the number at the top of the stack

6 * (A^3 A*A -> A^3 6*A^2)

and multiply it by 6, the number of sides of the cube

Let's call the written word with parameter 15 (side of the cube):

15 B5

 Ok (3375 1350)

3375=15*15*15 and 1350=6*15*15, that’s right, the word works correctly.

Same thing for real numbers:

: B5 ( A -> V S ) \ V=A^3 S=6*A^2

    FDUP FDUP FDUP\A -> A A A A

                     \ 2FDUP SP-Forth does not understand

    F* F* \ A A A A -> A A*A*A=A^3

    FSWAP\A A^3 -> A^3 A

    FDUP F* \ A^3 A -> A^3 A*A

    6E F* ; \ A^3 A*A -> A^3 6*A^2

Let's check the written code, take a cube with a side of 1.5:

15E-1 B5 F.F.

13.500000 3.3750000 Ok \ 6*1.5^2 = 13.5 1.5^3 = 3.375

Remember that the "F." operator prints what is on top of the stack. If you need a different order, you can use FSWAP, so if you need to display the volume first, as in stack notation, you can type the following:

15E-1 B5 FSWAP F.F.

3.3750000 13.500000 Ok

Example 6. Here it is necessary to calculate the volume and surface area of ​​a rectangular parallelepiped, through its edges.

: B6 ( A B C -> S V ) \ S=2*(A*B+B*C+A*C) V=A*B*C )
                

    DUP 2OVER\A B C -> A B C C A B

    DUP 2OVER \ A B C C A B -> A B C C A B B C A

    ROT * \ A B C C A B B C A -> A B C C A B C A*B

    ROT ROT * + \ A B C C A B C A*B -> A B C C A (A*B+B*C)

    ROT ROT * \ A B C C A A*B+B*C -> A B C (A*B+B*C) C*A

    + 2* \ A B C (A*B+B*C) C*A -> A B C (A*B+B*C+C*A)*2

    SWAP 2SWAP \ A B C (A*B+B*C+C*A)*2 -> (A*B+B*C+C*A)*2 C A B

    * * ; \ (A*B+B*C+C*A)*2 (C*A*B)

Where (A*B+B*C+C*A)*2 is the surface area, and (C*A*B) is the volume.

In this example, 3 parameters appear, which does not complicate the task too much, and as before we will not use variables explicitly, manipulating only the numbers on the stack.

In the code for real numbers, the number of elements must not exceed the maximum; due to its limitations, an error will occur. Let's check how much our system can accommodate, to do this we will type the following commands:

FDEPTH \ This word returns the number of elements in the real stack

 Ok ( 0 ) \ 0 elements

5E-1 FDEPTH \ enter the 1st number

Ok ( 0 1 ) \ 1 element on real stack

5E-1 FDEPTH \ enter the 2nd number

Ok ( 0 1 2 ) \ 2 elements

5E-1 FDEPTH \ enter the 3rd number

Ok (0 1 2 3)\3

5E-1 FDEPTH \ enter the 4th number

Ok (0 1 2 3 4)\4

5E-1 FDEPTH \ enter the 5th number

Ok ( [6].. 1 2 3 4 5 )

5E-1 FDEPTH \ enter the 6th number

Ok ( [7].. 2 3 4 5 6 )

5E-1 FDEPTH \ enter the 7th number

 Ok ( [8].. 3 4 5 6 7 )

5E-1 FDEPTH\error!!!

If after an error you enter “F.” we get:

infinity Ok

After an error, it is better to restart SP-Forth. Also, do not forget about connecting libraries again to work with real numbers. There is a DEPTH word for a regular stack, which also leaves the number of its elements, not counting the return parameter.

Now let's rewrite Example 6 for real numbers.

: B6 ( A B C -> S V ) \ S=2*(A*B+B*C+A*C) V=A*B*C )

    FOVER FOVER F+ \ A B C -> A B C (B+C)

FROT FROT F* \ A B C (B+C) -> A (B+C) B*C

FROT \ A (B+C) B*C -> (B+C) B*C A

FOVER FOVER F* \ (B+C) B*C A -> (B+C) B*C A B*C*A

F. \ 1st result – volume

FROT F* F+ 2.E F* \ (B+C) B*C A -> B*C+A*(B+C)

F. \ 2nd result S=2*(A*B+B*C+A*C)

;

Now you can check how the written word works:

1E-1 2E-1 3E-1 B6

0.0060000 0.2200000 Ok

The volume of a rectangular parallelepiped is 0.006=0.1*0.2*0.3 and its surface area is 0.22=2*(0.1*0.2+0.2*0.3+0.1*0.3 ).

Example 7. Knowing the radius of a circle, let's calculate its length and area.

: B7 ( R -> L S) \ L=2*Pi*R and S=Pi*R^2

    DUP 2* 314 * \ R -> R R*2*314=L

    SWAP\R L -> L R

    DUP 314 * * \ L R -> L R*R*314=S

 ;

The integer option takes an integer radius value and produces a result 100 times larger. I hope from the comments of the stack notation the work of the word is clear (it is quite trivial).

Code for real argument:

: B7 ( R -> L S) \ L=2*Pi*R and S=Pi*R^2

    FDUP 2E F* 314E-2 F* \ R -> R 2*Pi*R=L

    FSWAP\R L -> L R

    FDUP 314E-2 F* F* \ L R -> L R*R*3.14=S

;

Let's calculate the circumference and area of ​​a circle with radius 0.1:

1E-1 B7 F. F.

0.0314000 0.6280000 Ok

0.0314000=0.1*0.1*3.14 and 0.6280000= 2*3.14*0.1. The test results are correct.

Example 8. A simple problem to calculate the arithmetic mean of two integers: : B8 ( A B -> [A+B]/2 ) + 2/ ; 1 3 B8 OK (2) The mini code works correctly (1+3)/2=2. Below is the code for the real argument: : B8 ( A B -> [A+B]/2 ) F+ 2E F/ ; 1E-1 2E-1 B8 F. 0.1500000 Ok 0.15 = (0.1+0.2)/2 – TRUE

Example 9. The geometric mean of two numbers is the square root of their product. Let's immediately write the code for the real argument, since there is no possibility of extracting the root for integers in the SP-Forth system; to do this, you will have to convert the integer into a real number, extract the square root, then convert it back to an integer form, so such efforts are not justified here, but if somewhere you need it, then this is possible.

: B9 ( A B -> SQRT[A*B] )

    F* FSQRT ;

Very short and clear code, which we test below:

3E-1 75E-1 B9 F.

1.5000000 Ok \ 1.5 = Root_Square_of(0.3*7.5) – TRUE

This and the previous examples can be formatted beautifully for later use in mathematical calculations or in other programs, like your library functions.

: Arithmetic_mean ( A B -> [A+B]/2 ) F+ 2E F/ ;

: Geometric_mean ( A B -> SQRT[A*B] ) F* FSQRT ;

I can’t vouch for correct English names.

Example 10. Input is two numbers not equal to zero. Let's calculate the sum, difference, product and quotient of their squares, that is:

: B10 ( A B -> A^2+B^2 A^2-B^2 A^2*B^2 A^2/B^2 )

    SWAP DUP * SWAP DUP * \ A B ->A^2 B^2

    2DUP + \ A^2 B^2 -> A^2 B^2 (A^2+B^2)

    ROT ROT 2DUP – \ A^2 B^2 (A^2+B^2) -> (A^2+B^2) A^2 B^2 (A^2-B^2)

    ROT ROT 2DUP * \ (+) A^2 B^2 (-) -> (+) (-) A^2 B^2 (A^2*B^2)

    ROT ROT / \ (+) (-) A^2 B^2 (*) -> (+) (-) (*) (A^2/B^2 )

;

Let's test on numbers 4 and 2.

4 2 B10

 Ok (20 12 64 4)

Everything is correct, check it yourself. In the comments, I reduced the sum, difference, and product of squares to the corresponding operations in parentheses. The numbers are specially selected so that the result of division is an integer, but this is not necessary - the code for real arguments will save us from such inconveniences:

: B10 ( A B -> A^2+B^2 A^2-B^2 A^2*B^2 A^2/B^2 )

    FSWAP FDUP F* \ A B -> B A^2

FSWAP FDUP F* \ B A^2 -> A^2 B^2

FOVER FOVER F+ \ A^2 B^2 -> A^2 B^2 (A^2+B^2)

FROT FROT FOVER FOVER F- \ A^2 B^2 (A^2+B^2) -> (A^2+B^2) A^2 B^2 (A^2-B^2)

FROT FROT FOVER FOVER F* \ (+) A^2 B^2 (-) -> (+) (-) A^2 B^2 (A^2*B^2)

FROT FROT F/ \ (+) (-) A^2 B^2 (*) -> (+) (-) (*) (A^2/B^2)

;

Test example 10:

1E-1 2E-1 B10 F. F. F. F.

0.2500000 0.0004000 -0.0300000 0.0500000 Ok

Do not forget that the operator F. Prints the number from the top of the stack, so first the quotient will be printed, then the product, then the difference and finally the sum.

0.25 = 0.01/0.04; 0.0004 = 0.01*0.04; -0.03 = 0.01-0.04; 0.05 = 0.01+0.04.

If you need a different order for displaying results, then solve this problem yourself.