Перед решением очередного примера рассмотрим, как объявляются и используются переменные в SP-Forth. Так как операции с большим количеством данных на стеке становится крайне затруднительным, нам они пригодятся. Для этого используется зарезервированные слова VARIABLE и FVARIABLE. Первое для целых чисел, второе для вещественных. Если кто-то не знает, что такое переменная, то это просто участок памяти, в которое записывается значение (число для целых переменных, текст для строковых или их комбинация для структур), считывается или изменяется. На самом деле любые данные, неважно что: простые переменные, структуры или даже файлы, все они кодируется исключительно числами, причем в двоичном формате (нуликами и единицами).

Создадим две переменные

FVARIABLE FVAR     \ FVAR переменная вещественного типа
VARIABLE  VAR      \ VAR переменная целого типа

Теперь инициализируем эти переменные, то есть присвоим начальное значение.

1234E-2 FVAR F!
 Ok
4552249 VAR !
 Ok

Код «1234E-2» нам уже знаком, он просто переносит число «1234E-2» в вещественный стек, FVAR оставляет адрес вещественной переменной с этим именем, и в итоге «F!» - записывает значение в адрес. Целочисленное присвоение выглядит по проще, но суть та же. Сначала число идет в стек. Слово VAR также оставляет адрес целочисленной переменной на стеке. А записывает значение по адресу оператор «!» - восклицательный знак. А считывает – «F@» и «@» соответственно. Теперь считаем и выведем на экран значения созданных и инициализированных выше переменных.

VAR @ .
4552249  Ok

Оператор «.» - точка, печатает на экран целочисленное число, а «F.» - вещественное. Вы можете заметить логику Форта по названию операторов, добавив большую букву «F», многие операции становятся применимы к вещественным операндам. Покажем вышесказанное на примере вывода значения вещественной переменной.

FVAR F@ F.
12.340000  Ok

VAR и FVAR – это просто названия, они могут быть любыми – это просто удобное обозначение в стиле Форта. Теперь можем приступить к очередной задачке.

Пример 21. По координатам трех точек, образующих треугольник вычислить его периметр и площадь. Сначала создадим переменные для координат и сторон треугольника.

FVARIABLE FX1
FVARIABLE FY1
FVARIABLE FX2
FVARIABLE FY2
FVARIABLE FX3
FVARIABLE FY3
FVARIABLE FA
FVARIABLE FB
FVARIABLE FC

Так как здесь мы используем только вещественные переменные, то «F» можно опустить и переписать все в одну строку, не забывая о пробелах, но мы этого делать не будем, чтобы следовать единой стилистике обозначения переменных, в учебных целях.

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
;

  1. Строка №1 название слова с комментариями.
  2. Вторая – сохранение координат в соответствующих переменных.
  3. С третьей по пятую – вычисление сторон треугольника с сохранением в переменных A, B, C. Здесь мы не высчитаем расстояния между точками (стороны треугольника), а пользуемся предыдущей задачей, в которой эта проблема решена, просто вызвав ее с параметрами задачи №21.
  4. Шестая строка вычисление периметра и его дублирование
  5. Седьмая вычисление полупериметра.
  6. С восьмой по десятую – вычисление сомножителей в формуле площади.
  7. Одиннадцатая – вычисление площади.

Проверим работу слова на координатах: (1,1; 1,1) (6,1; 1,1) (6,1; 4,1). Это прямоугольный треугольник с катетами 3 и 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. Гипотенуза равна sqrt(3^2+5^2)= sqrt(9+25)= sqrt(34)= 5,83095. P= 5,83095+3+5= 13,83095.

Что является истиной, то есть задачка запрограммирована корректно.

Если у вас будут проблемы и ошибки введите код в нижеприведенной последовательности.

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.

Здесь нет комментариев в коде, чтобы избежать ошибок при копировании. Первая строка подключение библиотеки для работы с вещественными числами (тип float2). Далее идет код предыдущего примера, который мы используем (в других языках это называется функция). Затем объявляются переменные где мы сохраняем координаты и вычисляемые длины сторон. И, наконец сжатый код слова, производящий вычисления периметра и площади треугольника, и его вызов с заранее подготовленными параметрами.

Пример 22. Довольно классическая задача по обмену содержимым между двумя переменными. В Форте создание переменных в данном случае даже не обязательна.

: B22 ( A B -> B A) SWAP ;
5 4 B22
 Ok ( 4 5 )

Для вещественных аргументов.

: B22 ( A B -> B A) FSWAP ;

Теперь напишем с переменными:

VARIABLE A
VARIABLE B
: B22 ( -> )        \ обмен содержимым двух переменных A и B
A @ B @ A ! B ! ;

Как проверить работу?

15 A ! 50 B !      \ Сначала инициализируем переменные. A=15, B=50
 Ok
B22                 \ Вызов функции обмена переменных
 Ok
A @ . B @ .         \ Печатаем содержимое переменных после обмена
50 15  Ok

Обмен вещественных переменных.

FVARIABLE FA
FVARIABLE FB
: B22 ( -> )        \ обмен содержимым для двух переменных A и B
FA F@ FB F@ FA F! FB F! ;

Проверим. Инициализируем переменные, вызовем слово и распечатаем результат. Все по стандартной схеме.

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

У нас получилось четыре слова с одинаковыми именами, и каждое заменяет предыдущее, о чем нас предупреждает Форт система – SP-Forth сообщением «B22 isn't unique ()». Если вы собираетесь использовать несколько вариантов этих слов, то надо их называть разными именами. Например, как уже говорилось ранее, добавив «F» в начало слов, которые предназначены для работы с вещественными аргументами. Разумеется, в этом примере, первые два варианта (без использования переменных), в которых тело состоит всего лишь из одного оператора, в отдельные Форт-слова оформлять не обязательно. Просто помните о возможности таких манипуляций на стеке – это одна из сильных сторон Форта.

Пример 23. Обменять значения трех переменных A, B, C по следующей схеме: A -> B -> C -> A.

VARIABLE A VARIABLE B VARIABLE C

Если у вас все примеры в одном файле, то каждый раз объявлять переменные не надо. Добавьте только новую, в данном случае – это «VARIABLE C». Иначе будут сообщения от Форт системы

A isn't unique ()
B isn't unique ()

Это не критично, SP-Forth будет работать дальше, но, во-первых, будут созданы новые переменные с такими же именами, что просто будет бесполезно увеличивать расходы памяти, во-вторых, если вам нужно было предыдущее значение, то они уже не будут доступны по имени.

: B23 ( -> )      \ стековая нотация по прежнему пуста
    A @ B @ C @    \ -> A B C
    A ! C ! B !    \ C=A B=C A=B
;
1 A ! 2 B ! 3 C ! B23 A @ . B @ . C @ .
3 1 2  Ok

A=3, B=1, C=2.

Для вещественного аргумента нужны соответствующие переменные.

FVARIABLE FA FVARIABLE FB FVARIABLE FC
: B23 ( -> )           \ стековая нотация по прежнему пуста
    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

Пример 24. Аналогичен предыдущему с перемещением содержимого по схеме: A -> C-> B -> A.

Переменные используем с предыдущего примера.

: B24 ( -> )     \ стековая нотация пуста
   A @ B @ C @    \ -> A B C
   B ! A ! C !    \ B=C A=B C=A
;
1 A ! 2 B ! 3 C ! \ инициализация A=1 B=2 C=3
 Ok B24               \ вызов нашей функции
 Ok
A @ . B @ . C @ . \ A=2 B=3 C=1
2 3 1  Ok

Для вещественного аргумента.

: B24 ( -> )          \ стековая нотация пуста
    FA F@ FB F@ FC F@  \ -> A B C
    FB F! FA F! FC F!  \ B=C A=B C=A
;

Проверим на аналогичных данных.

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

Пример 25. Вычислить значение функции, если дан аргумент. Опустим целочисленный вариант, его можно написать по аналогии самостоятельно.

: 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 

А вот при аргументе равным нулю выдает ошибку (возведение нуля в степень):

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

Но во второй раз у меня выдал другой ответ:

0E B25 F.
infinity  Ok

После этого лучше перезапустить систему или заново подключить библиотеки работы с вещественными числами, иначе он их не распознает:

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

Пример 26. Брат близнец предыдущего. Разница в формуле. Так же рассмотрим только вещественный аргумент.

: 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

Будьте осторожны при возведении в степень, это опасная операция, вызывающая много ошибок.

Пример 27. В Форте дополнительная переменная здесь и не понадобится. Для тех, кто любит все выполнять строго по инструкции - задача самостоятельно переписать код.

: 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
;

Примеры работы слова:

2 B27
 Ok ( 4 16 256 )
3 B27
 Ok ( 9 81 6561 )

Без комментариев.

С вещественным аргументом задача ничуть не сложнее. Заменяем все операторы на соответствующие, просто добавив «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     \ Порядок печати обратный
3E B27 F. F. F.
6561.0000 81.000000 9.0000000  Ok

Пример 28. Похож на предыдущую задачу и чуть посложнее.

: 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 )

Не забывайте, что степенная функция растет очень быстро и при большом основании быстро произойдет переполнение, в результате ответ будет некорректным. Так, например при A=10, уже 10-ая степень вычисляется не правильно.

10 B28
 Ok ( 100 1000 100000 1410065408 2764472320(-1530494976) )

Для вещественного аргумента.

: 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 \ опять обратный порядок при печати
3E B28 F. F. F. F. F.
14348907. 59049.000 243.00000 27.000000 9.0000000 Ok

Очевидно слово работает корректно.

Пример 29. Перевести градусы в радианы.

Целочисленный вариант будет в 100 раз больше, поэтому его код не приводится. При необходимости можно его получить из кода для вещественного аргумента.

: 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
;

Нижеприведенные тесты корректны.

90E B29 F.
1.5700000  Ok
360E B29 F.
6.2800000  Ok

Пример 30. Обратная к предыдущему задача перевода из радиан в градусы.

: B30 ( A{RAD} -> X{DEG} ) \ 180*A/Pi
    180E F*                 \ A -> 180*A
    314E-2 F/               \ 180*A -> 180*A/Pi
;

Тесты слова B30.

314E-2 B30 F.
180.00000  Ok
628E-2 B30 F.
360.00000  Ok

Небольшие хитрости для оптимизации работы с системой программирования SP-forth.

Если вы планируете использовать последние два слова в своей работе, то можно их назвать по практичнее и добавить в свой файл Форт-расширения.

Например, RAD>DEG и DEG>RAD (знак «>» означает перевести, перенести в зависимости от контекста использования).

Пример содержания такого файла:

\ Подключение библиотеки для работы с вещественными числами
S" lib\include\float2.f" INCLUDED
VARIABLE A VARIABLE B VARIABLE C       \ Часто используемые переменные
FVARIABLE FA FVARIABLE FB FVARIABLE FC
: DEG>RAD ( A{DEG} -> X{RAD} )         \ X=A*Pi/180 - градусы в радианы
    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 – радианы в градусы
    180E F*                            \ A -> 180*A
    314E-2 F/                          \ 180*A -> 180*A/Pi
;
: Arithmetic_mean ( A B -> [A+B]/2 ) F+ 2E F/ ;  \ среднее арифметическое
: Geometric_mean ( A B -> SQRT[A*B] ) F* FSQRT ; \ среднее геометрическое
: R2D ( X1 Y1 X2 Y2-> R )                        \ R= Квадратный_Корень((X2-X1)^2+(Y2-Y1)^2)
\ вычисление расстояния между двумя точками через их координаты на плоскости
    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
;

Назовите файл как вам нравится, но обязательно с расширением «.F», тогда его можно будет запустить и он откроется в SP-Forth, при условии, что он у вас установлен. Тогда вам не придется каждый раз выполнять рутинные задачи, которые вы включите в этот файл.

Чтобы упростить интерфейс взаимодействия с написанными словами, например, вывод результатов расчета для слов со множеством выходных параметров, можно применить следующую методику, описанную далее.

Рассмотрим пример №28.

2E B28 F. F. F. F. F.

Здесь «B28 F. F. F. F. F.» заменяем на слово

: B28. ( A^2 A^3 A^5 A^10 A^15 -> ) B28 F. F. F. F. F. ;

Такой вариант предпочтительный не только из-за упрощения, но и исходя из того, что вы со временем забудете, как работает слово, а разбираться в работе слове каждый раз не разумно, да и зачем перегружать мозг бесполезными мелочами? Оператор «.» - точка в Форте всегда связана с печатью на экран, поэтому «Name.» - логично означает печать результатов слова «Name». Только не забудьте написать соответствующее слово перед ее выполнением, автоматически слова в Форте не пишутся. Так же можно включать словесное описание результатов, например,

: B28. ( A^2 A^3 A^5 A^10 A^15 -> ) B28
    .” A^15= “ F. CR     \ CR – это оператор перевода на новую строку
    .” A^10= “ F. CR
    .” A^5= “  F. CR
    .” A^3= “  F. CR
    .” A^2= “  F. CR
;

Окончательно вызов слова B28 будет выглядеть, с учетом упрощения, так:

2E B28.

Так мы разделяем алгоритм работы слова с его примитивным интерфейсом вывода результатов. В таком случае при необходимости что-то доработать, одно не помешает другому и, разумеется, код становится более лаконичным и понятным.