Код «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 ( FX1 FY1 FX2 FY2 FX3 FY3 -> 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 название слова с комментариями.
Вторая – сохранение координат в соответствующих переменных.
С третьей по пятую – вычисление сторон треугольника с сохранением в переменных A, B, C. Здесь мы не высчитаем расстояния между точками (стороны треугольника), а пользуемся предыдущей задачей, в которой эта проблема решена, просто вызвав ее с параметрами задачи №21.
Шестая строка вычисление периметра и его дублирование
Седьмая вычисление полупериметра.
С восьмой по десятую – вычисление сомножителей в формуле площади.
Одиннадцатая – вычисление площади.
Проверим работу слова на координатах: (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 ! \ A=C C=B B=A
;
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! 2E FB F! 3E FC F! B23 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! 2E-1 FB F! 3E-1 FC F! B24 \ FA=0.1 FB=0.2 FC=0.3
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
После замены кода «F**2» на «FDUP F*» и «3E F**» на «FDUP FDUP F* F*» проблема исчезает.
: B25 ( X -> F[X] ) \ F(X)=3*X^6-6*x^2-7
FDUP F* \ 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
FDUP FDUP F* 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}
;
0E B25 F.
–7.0000000 Ok
Пример 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
3E B26 F.
2.0000000 Ok
Здесь ошибки пи возведении нуля в степень не выдал, но при вводе кода «0E 3E F** F.» по-прежнему выдает ошибку. Будьте осторожны при возведении в степень, это опасная операция, вызывающая много ошибок.
Пример 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
Для повышения точности, самостоятельно перепишите слово «B29» используя слово «FPI».
Пример 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
;
: MIDDLE_ARITHMETIC ( A B -> [A+B]/2 ) \ среднее арифметическое
F+ 2E F/ ;
: MIDDLE_GEOMETRIC ( 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^2= “ F. CR \ CR – это оператор перевода на новую строку
.” A^3= “ F. CR
.” A^5= “ F. CR
.” A^10= “ F. CR
.” A^15= “ F. CR
;
Окончательно вызов слова B28 будет выглядеть, с учетом упрощения, так:
2E B28.
Так мы разделяем алгоритм работы слова с его примитивным интерфейсом вывода результатов. В таком случае при необходимости что-то доработать, одно не помешает другому и, разумеется, код становится более лаконичным и понятным.
BEGIN 31-40
Пример 31. Перевести температуру в Фаренгейтах в градусы Цельсия. Сперва для целых значений температуры.
: B31 ( TF-> TC ) \ TC=(TF-32)*5/9
32 – 5 * 9 /
;
32 B31 \ 32 град Фаренгейта = 0 град Цельсия, 32-32=0, 0*5/9=0
Ok ( 0 )
35 B31 \ 35 град Фаренгейта = 1 град Цельсия (35-32)=3, 3*5=15, 15/9=1 – целая часть
Ok ( 0 1 )
40 B31 \ 40 град Фаренгейта = 4 град Цельсия (40-32)=8, 8*5=40, 40/9=4 – целая часть
Ok ( 0 1 4 )
С незначительными изменениями мы сможем переписать код для вещественных значений температуры.
: B31 ( TF-> TC ) \ TC=(TF-32)*5/9
32E F-
5E F* 9E F/
;
32E B31 F. \ как и в первом варианте, только результат в вещественном формате
0.0000000 Ok
321E-1 B31 F.
0.0555555 Ok
Пример 32. Обратная к предыдущему примеру задача. Перевести температуру из Цельсия в Фаренгейты.
: B32 ( TC -> TF ) \ TF= TC*9/5+32
9 * 5 / 32 + ;
0 B32 \ 0 град Цельсия = 32 град Фаренгейта
Ok ( 32 )
–18 B32
Ok ( 32 0 ) \ -18 град Цельсия = 0 град Фаренгейта
Для вещественного аргумента.
: B32 ( TC -> TF ) \ TF= TC*9/5+32
9E F* 5E F/ 32E F+ ;
0E B32 F. \ 0 град Цельсия = 32 град Фаренгейта
32.000000 Ok
–18E B32 F. \ -18 град Цельсия = -0,4 град Фаренгейта
–0.4000000 Ok
Ноль градусов по Цельсию по-прежнему 32 градусов Фаренгейта, единственное отличие – результат вещественное число, а вот при T=-18 остаток уже не отбрасывается, как в первом случае, и ответ получается точный.
Пример 33. Детская задача. Цена 1 кг конфет обозначим через C1, а Y кг соответственно CY, тогда легко вычислить C1=A/X и CY=C1*Y, а дано X кг за A рублей и количество Y кг, цену которого нужно вычислить.
: B33 ( X A Y -> C1 CY ) \ C1=A/X CY=C1*Y
SWAP ROT / \ X A Y -> Y A/X=C1
SWAP OVER * \ Y C1 -> C1 Y*C1= CY
;
3 9 10 B33 \ 3 кг стоит 9 р, т. е. 3р за 1 кг, а 10 кг будут стоить 3*10=30 р
Ok ( 3 30 )
Вы можете самостоятельно потренироваться на современных ценах различных товаров.
А теперь для дробных цен.
: B33 ( X A Y -> C1 CY ) \ C1=A/X CY=C1*Y
FSWAP FROT F/ \ X A Y -> Y A/X=C1
FSWAP FOVER F* \ Y C1 -> C1 Y*C1= CY
;
О проекте
О подписке