Czytaj książkę: «Решение задач по программированию на языке Форт (Forth). Версия 3»
Введение
С этой книгой вы изучите язык программирования Форт с нуля на конкретных примерах, минимум теории максимум практики. Почему Форт? Философия Форта – минимализм, простота, быстрота выполнения кода. Интерпретатор Форта компилирует все новые, определяемые слова, в словарь, немедленно после ввода, а это значит, что он сохраняет:
интерактивность REPL-среды и вы сразу видите результат своих действий и
быстроту компилятора
Потенциал Форта как языка программирования – безграничен. Хотя и являясь низкоуровневым, его можно превратить в язык высокого уровня и вводить любые типы данных и конструкции из других языков (Форт можно превратить в любой другой язык, диалект или создать свой).
Синтаксис этого ЯП (Языка Программирования) настолько прост, что его практически нет. Есть только одно главное правило – все Слова (именно так называются операторы языка и определяемые пользователем слова-функции) и числа должны быть разделены между собой хотя бы одним символом пробела и/или табуляции и/или переноса строки (то есть «любой символ пустоты, разделитель»).
Для кого эта книга?
Программировать на языке Форт настолько просто, что материал подойдет даже для детей. Не говоря уже об обучении, этому прекрасному виду человеческой деятельности, школьников, абитуриентов и студентов. Так что данным материалом может пользоваться любое заинтересованное лицо, независимо от возраста и рода человеческой деятельности. Сложность будет наращиваться постепенно, с нуля, с возможностью подтянуть сложные моменты и пропусков примитивов, уже подготовленными читателями.
Среда программирования SP-Forth – замечательно подойдет для наших целей. Программировать в этом консольном приложении можно начать сразу после быстрого скачивания и установки с официального сайта последней его версии. Он распространяется бесплатно. Работает он в режиме интерпретатора. Это значит, что после запуска системы программирования SP-Forth можно вводить команды или целую программу, после, нажав <Enter>, приложение обработает код и выдаст, предусмотренные результаты. Скачивание и установка SP-Forth не должен вызвать никаких сложностей, а потому этот процесс мы здесь опустим (в любом поисковике наберите «скачать SP-Forth», исполняемый файл запускается двойным кликом левой мыши по нему). После запуска Форта вы увидите:

Рис 1
Исторически сложилось, что основы программирования начинают изучать с первой программы, которая выводит на экран «Hello, World!», что по-русски означает «Здравствуй, Мир!».
Код для ЯП Форт будет следующий:
.( Hello world!)
или
S" Hello, World!" TYPE
После чего на экран будет выведено вышеупомянутое сообщение. Операторы ЯП (язык программирования) Форт <.(> и <S">: первый берет текст, следующий за ним до закрывающей кавычки – " (признак конца выводимой на экран строки) и печатает его на экран, второй создаёт строку адрес которой с ее длиной отправляется на стек. Затем оператор TYPE используя эти числа, адрес и его размер, как параметры также печатает сообщение. Главное «TYPE» должен идти сразу, во избежание ошибок при исполнении, это особенность работы системы. Обращайте внимание на пробелы – в форте они разделяют слова языка. Зачем нужен второй вариант, если первый проще и короче? Затем что он универсальный, так определяются в Форте строки, и они будут полезны в будущем.
Вот как должен выглядеть результат работы:

Рис 2
Третий вариант этой же программы будет выглядеть так:
: Hello_World ." Hello, World!" ;
Hello_World

Рис 3
Отличие в том, что определяется слово с именем приветствия, которое затем вызывается. Первые два варианта работают в режиме интерпретации, а последний – компиляции (в форт-словаре создается новое слово). Будучи универсальным, его можно вызывать многократно из различных мест программы или даже использовать это слово в своих библиотеках.
Так мы написали первую полноценную программку. Вместо «Hello_World», вы можете назвать «Start» или как захочется, главное придерживаться простого правила – названия должны быть информативными. Стиль программирования на ЯП Форт ничем не отличается от других языков, будет полезно в дальнейшем почитать об этом.
Так мы определили новое слово. Определение слова начинается с двоеточия, затем идет любой символ-разделитель (пробел, табуляция или перевода строки, но традиционно используется один пробел). После идёт имя слова, которое вы сами придумаете, далее код – реализация (последовательность операторов, чисел и других уже определённых слов), разделенных пробелами (и / или другими разделителями). Завершается точкой с запятой, также отделенный от кода пробелом.
Начало уже положено. Первая программа на Форте для начинающих выглядит довольно просто.
Добавим пару штрихов – комментарии:
Принято в каждом определении нового слова (на Форте) после названия писать стековый комментарий, обозначающий что слово берет со стека в качестве параметров и что оставляет на нем как результаты своей работы. Перепишем первое наше Форт-Слово:
: Hello_World ( -> ) ." Hello, World!" ; Hello_World
Так как Hello_World оставляет стек неизменным (не трогает его), то до и после стрелки пусто. Комментарий – это содержимое скобок.
Также существует второй способ комментирования кода до конца строки. Это символ – «\».
: Hello_World ( -> ) ." Hello, World!" ; Hello_World \ это программа, выводящая сообщение «Hello, World!»
Программирование без посторонней помощи не составит особой трудности даже, если вы только начинаете. В отличие от языков программирования С или С++, с которых также можно стартовать с нуля, все довольно наглядно и просто. На Форте можно практиковать программирование как онлайн, так и офлайн. Для первого варианта существует скрипт интерпретатор языка Форт на JavaScript (с его помощью можно создавать Форт программы прямо в браузере). Мы же будем ориентироваться на конкретный диалект Форта – SP-Forth. Он существует для всех популярных систем (Windows, Linux).
Обычно вторая задача при обучении программированию – это написание калькулятора. В Форте калькулятор писать не нужно, так как он поддерживает основные операции встроенными средствами, правда несколько в необычной форме, которая называется обратная польская запись или постфиксная. В математике вы привыкли писать формулы в инфиксной форме типа «(1 + 2) * 5 * (4 – 5)», где знак бинарной операции пишется между числами, например, в Лиспе сначала идёт операция, а затем операнд или операнды, а в Форте наоборот, сперва мы отправляем на стек операнды, затем операция выполняет действия над ними, оставляя результат там же. Стек это просто место в памяти, поддерживаемое на аппаратном уровне, следовательно, все операции над ними выполняются очень быстро, где будет хранится промежуточные данные. Так будет выглядеть работа с нашим калькулятором:
Операнд1 Операнд2 Операция. То есть вместо «1 + 2» на Форте мы должны написать «1 2 +».
1 2 +
Ok ( 3 ) \ 1+2=3 в скобках – это содержимое стека
1 2 *
Ok ( 3 2 ) \ 1*2=2 очередной результат на вершине стека
1 2 -
Ok ( 3 2 4294967295(-1) ) \ 1-2=-1 4294967295 – это без знаковый вариант числа -1
1 2 /
Ok ( 3 2 4294967295(-1) 0 ) \ 1/2=0 – это целочисленное деление, потому результат нуль
1 2 MOD
Ok ( 3 2 4294967295(-1) 0 1 ) \ остаток от деления 1/2
1 2 /MOD
Ok ( [7].. 4294967295(-1) 0 1 1 0 ) \ остаток от деления 1/2 и целая часть ½

Рис 4
Вы можете сказать программирование на питоне – гораздо богаче, возможностями и будете правы. Но Форт быстрее и в качестве первого языка программирования гораздо проще и легче его освоить, а самое главное понятнее. Учить программирование на паскале, на мой взгляд уже не актуально. Хотя он и хорош для изучения различных алгоритмов, но это уже не современный язык программирования, со множеством избыточных конструкций и синтаксиса.
Приведем конкретные примеры на SP-Forth.
: ^2 ( A -> A^2 ) DUP * . ; \ возведение числа в квадрат
: ^3 ( A -> A^3 ) DUP DUP * * . ; \ возведение числа в куб
: ^4 ( A -> A^4 ) DUP * DUP * . ; \ возведение числа в четвертую степень
DUP – это слово которое просто дублирует число на вершине стека.
Возведём 5 в квадрат, для этого наберём на клавиатуре:
5 ^2
25 Ok
Получаем правильный ответ:

рис 5
Копировать код нужно аккуратно, при возведении в квадрат, получаем сообщение об ошибке. При «Копи пасте» куда-то делся символ табуляции между «;» и комментарием, вследствие видим «^ -2003 WORD OR FILE NOT FOUND», из-за нарушения синтаксиса. Наверное, это особенность системы. Почему-то «сглатывается» символ табуляции, а потому вставлен дополнительный символ пробела в коде выше, ниже приведен пример с ошибкой интерпретации:
: ^2 ( A -> A^2 ) DUP * . ; \ возведение числа в квадрат

рис 6
Знаки табуляции нужны, чтобы структурировать логично, красиво и понятно код, например, в Python они элемент синтаксиса, без них ваш код не будет работать или как минимум будет выполняться некорректно, в Форте как и в других языках это элементы стиля программирования.
Также, вместо ^2, можно написать **2, в стиле python:
: **2 ( A -> A^2 ) DUP * . ; \ возведение числа в квадрат
5 **2
25 Ok
В итоге получаем тот же ответ, но оформление, стиль изменились. Не забывайте «**2» – это слово, а значит он отделяется пробелом от операнда «5» (до и после, если за ним будут другие инструкции). Это одно из самых главных отличий Форта от других языков.
Для начала этого достаточно. Далее при решении конкретных задач, процесс станет более понятным и осознанным. Приступим.
BEGIN 1-10
Практиковаться программированию на Форте будем с задач из книги М. Э. Абрамян "1000 задач по программированию Часть I Скалярные типы данных, управляющие операторы, процедуры и функции" 2004. Автор пишет, что получить задачник можно по e-mail: mabr@math.rsu.ru или за подробностями обращайтесь к веб ресурсу ptaskbook.com. Текст задач я приводить не буду, дабы исключить плагиат. Думаю, пояснения к коду с описанием слов (функций-программ) должно быть достаточно, в противном случае обращайтесь к первоисточнику за текстом задач.
Пример 1. Итак, начнем, (для простоты вначале мы будем рассматривать все входные параметры как целые числа, далее мы перепишем код для вещественных аргументов, где об этом указано в условии задачи). Вот и решение первой задачи:
: B1 ( A -> P ) 4 * ; \ P=4*A
B – это сокращение от BEGIN, что обозначает первую группу заданий (мы и далее будем использовать такой вид названий в последующих группах заданий), затем слитно пишется номер примера, как часть названия слова. Сразу после имени в скобках пишется комментарий стековой нотации. Так принято в Форте. В данном случае Слово-функция B1 берет один параметр A (четырехбайтовое целое число и оставляет другое того же типа). A – сторона квадрата – вход функции, P – его периметр – возвращаемое функцией значение. Тело – очень короткое, просто умножает число на вершине стека на 4 (не забываем, что в Форте обратная польская запись, сначала идут операнды, затем операция).
Теперь чтобы пользоваться нашим словом, например, чтобы посчитать периметр квадрата со стороной 3, наберем следующий код:
3 B1 .
12 Ok

Рис 7
Точка «.» – это стандартное форт слово, которое печатает число на вершине стека на экран.
Напомним, общий вид определения нового слова в Форте:
: Название-Слова ( стек до выполнение –> после ) Код-Тела-Функции ; \ комментарий
Пример 2. Посчитаем площадь квадрата:
: B2 ( A -> S ) DUP * ; \ S=A^2
Мы просто дублируем число на вершине стека (для чего используем оператор языка Форт – DUP, от английского слова дублировать) и умножаем его на себя. Данный пример можно оформить более красиво для использования в ваших будущих программах и включать как библиотеку или как расширение стандартного словаря с последующим сохранением как новой версии Форта.
: SQR ( A -> A^2 ) DUP * ; \ A^2 – вычисление квадрата числа, или
: ^2 ( A -> A^2 ) DUP * ; \ или
: **2 ( A -> A^2 ) DUP * ; \ отличие только в названии

Рис 8
Результат тот же, так как слова делают одно и тоже. Имя слову можно давать любое – area, square (не только заглавными буквами, так просто исторически сложилось на Форте, что все операторы языка пишутся не строчными буквами), в зависимости от контекста. Какой вариант нравится, тот и можете использовать. Или все сразу, так тоже можно.
Пример 3. По сторонам прямоугольника нужно вычислить его Площадь и Периметр:
: B3 ( A B -> S P ) \ ( S=A*B P=2*(A+B) )
2DUP ( A B -> A B A B ) \ Слово 2DUP, дублирует сразу два числа
* ( A B A B -> A B A*B=S ) \ Площадь вычислен – это просто произведение сторон
ROT ROT ( A*B=S A B ) \ оператор ROT вытаскивает 3-ий от вершины параметр на вершину
\ применив его два раза на вершине мы получаем A B и вычисленный под ним Площадь
+ 2* ; \ складываем A и B, и умножив на 2, оператором 2*, получаем периметр
Слово «2*» делает тоже самое что и два слова «2 *», только короче и проще, и главное одно слово выполняется быстрее, чем два.
В итоге на стеке мы получаем Площадь и Периметр. Чтобы напечатать результаты на экран из примеров нужно просто ввести точку с клавиатуры «.» и затем нажать «Enter». Сначала напечатается вершина, т. е. периметр, в данном примере, затем повторив действия площадь. Чтобы изменить порядок печати, наберите слово SWAP, который меняет местами 2 числа на вершине стека ( A B -> B A), т.е., например чтобы напечатать площадь и периметр прямоугольника со сторонами 1 и 2 введём следующее:
1 2 B3 SWAP . .
2 6 Ok
Площадь равна «1*2=2», а периметр равен «2*(1+2) = 6». Слово работает корректно и вычисляются площадь и периметр соответственно стековой нотации, а выводятся по условию задачи.
Перепишем слово «B3» более красиво:
: B3 ( A B -> S P ) \ ( S=A*B P=2*(A+B) )
2DUP \ A B -> A B A B
* \ A B A B -> A B A*B=S
ROT ROT \ A B S -> S A B
+ 2* \ S A B -> S (A+B)*2=P
;
Удалены лишние комментарии, оставлена только стековая нотация.
Слово «B3» считает площадь и периметр и ничего не выводит на экран. Его можно использовать, как и первый вариант, результат будет тот же:
1 2 B3 SWAP . .
Проверьте этот факт самостоятельно.

рис 9
Последний вариант выглядит более профессионально, первый (с максимальным количеством комментариев) только для учебных целей, чтобы освоится с новыми словами языка.
Пример 4. Нужно вычислить длину круга зная его диаметр:
: B4 ( D -> L ) 314 * ; \ L=Pi*D*100
Ответ буде в 100 раз больше для целочисленных данных («Пи» вместо 3,14 взяли 314), таким образом избавились от дробной части. Так себе решение. Напишем слово:
: .2 ( A -> {[A/100],[A%100]} )
100 /MOD . 8 EMIT 44 EMIT .
;
Слово «.2» печатает результат, который в 100 раз больше, как два разных числа «склеенных» вместе (целая часть от «A/100» и его дробная часть – 2 знака десятичной дроби) с запятой. Теперь вычислим длину окружности диаметром 1 – результат точный для «Пи»=3,14:
1 B4 .2
3,14 Ok

Рис 10
Подробно рассматривать код слова «.2» не будем, а перепишем так, чтобы можно было работать с вещественными числами. Для этого в SP-Forth нужно подключить соответствующие библиотеки. Скопируйте и вставьте следующие две строчки:
S" lib\include\float.f" INCLUDED
S" lib\include\float2.f" INCLUDED
Можно только вторую строчку (вызов второй, после первой выводит много сообщений типа «isn't unique (lib\include\float2.f)» – это не ошибки, означает, что слова с таким именем ранее были введены в систему).
Теперь чтобы ввести вещественное число, скажем 0,5, нужно набрать на клавиатуре следующее:
5E-1 или 5e-1
До E – это мантисса (число), после экспонента (степень). Мантисса и экспонента могут быть как положительными (знак не требуется), так и отрицательными (в данном случае степень -1, что значит 10 в минус первой степени).
После ввода, вещественное число размещается на соответствующем ей вещественном стеке. Поэтому мы не видим его после вывода слова Ok в скобках, так как это другой стек для целых чисел. Чтобы его увидеть нужно ввести «F.». Итак, чтобы проверить, что всё работает как надо, введём код:
5E-1 F.
В ответ увидим:
0.5000000 Ok
Слово «F.», аналогично, как и «.» выводит число на экран, только не с целочисленного стека, а с вещественного.
Теперь мы можем переписать пример 4 для вещественных аргументов:
: B4 ( D -> L ) \ L=Pi*D
314E-2 F* ;
Вместо 314E-2 можно набрать 3.14E, результат тот же. Посчитаем длину окружности диаметром 0,5, набрав следующее:
5E-1 B4 F. \ вызываем слово, которое считает длину окружности и «F.» печатает его результат
1.5700000 Ok
Мы готовы переписать таким же образом код первых трех примеров для случая с вещественными аргументами, сделав их более универсальными.
Пример 1:
: B1 ( A -> P ) 4E F* ; \ P=4*A
Знак «*» заменяется на «F*», четверка вводится как вещественное число (операция «F*», в отличие от «*» производит операцию над вещественными числами на вещественном стеке). Теперь проверим, посчитаем периметр квадрата со стороной 0,5:
5E-1 B1 F.
2.0000000 Ok
Ответ 2 (0,5*4=2) что является правдой.
Данный пример, так же можно преобразовать, написав в стиле:
: B1 ( A -> P ) \ P=4*A
4E F*
;
Но он настолько маленький и примитивный, что едва ли это необходимо, проще и лаконичней всё оставить в одной строчке. В более сложных и больших примерах код нужно писать структурированным, понятным и разумеется в едином стиле.
Пример 2:
: B2 ( A -> S ) FDUP F* ; \ S=A^2
Опять DUP превращается в FDUP, умножение как в первом случае. Проверим работу слова. Посчитаем площадь квадрата со стороной 0,5:
5E-1 B2 F.
0.2500000 Ok \ 0,5*0,5 = 0,25
Иллюстрация примеров 1,2 и 4 для вещественных аргументов:

рис 11
Пример 3: Расчет площади и периметра прямоугольника.
: 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
FROT FROT \ A B S -> S A B
F+ 2E F* \ S A B -> S (A+B)*2=P
;
Первая строка стандартное, уже знакомое начало слова на Форте. Далее:
Вторая строка «FOVER FOVER ( A B -> A B A B )». Слова 2FDUP – нет в словаре, «FOVER» копирует элемент под вершиной вещественного стека на вершину, двойное повторение аналогично целочисленному «2DUP».
Третья – умножение двух чисел на вершине вещественного стека. Получаем площадь прямоугольника.
Четвертая, как и для целочисленного варианта слова (ROT заменяется на FROT).
Пятая сложение двух чисел на вершине вещественного стека и удвоение результата.
Шестая завершение слова, в итоге на вершине стека получаем 2 числа – площадь и периметр.
Проверим работу слова B3 на сторонах прямоугольника 0,2 и 0,3:
2E-1 3E-1 B3 FSWAP F. F.
0.0600000 1.0000000 Ok
Как можете увидеть ниже всё работает верно:
S = 0,2*0,3=0,06
P=2*(0,2+0,3)=2*0,5=1
0,2 и 0,3 можно вводить и в следующем виде: 0.2E и 0.3E. Самостоятельно можете убедиться, что слово «F.» выведет на экран тоже самое значение.
Код «2E-1 3E-1 B3 FSWAP F. F.» можно собрать в отдельное слово, если не приятно работать с таким количеством команд:
: B3. ( S P -> S. P. ) B3 FSWAP F. F. ;
Ok
2E-1 3E-1 B3.
0.0600000 1.0000000 Ok
Согласно логике Форта «B3.» аналогично «.» означает печать результатов слова «B3».
Вы можете спросить зачем такие сложности? Код становится универсальным, мы отделяем вычисляемую часть от метода вывода данных на экран, его можно использовать при определении других слов, включать в свои библиотеки, и использовать в других задачах как отдельную функцию.
Вы можете заметить, что, в примере 3, каждая строка кода сопровождается комментарием, которая поясняет изменения на стеке. Так как все операции и манипуляции, в данном примере, производятся только с вещественным стеком, мы это никак не обозначаем, когда слово будет работать с обоими стеками сразу, вещественный стек будем выделять так: «F: A B A*B=S -> S A B», изменения целочисленного стека дополнительно выделять не будем. Существует еще и стек возвратов. Будьте осторожны, любые неосторожные манипуляции приведут к некорректным результатам, в лучшем случае, в худшем вызовут сообщения об ошибке.
Так же обратите внимание, что комментарии выравниваются так чтобы они выстроились в вертикальную линию, даже те строчки, в которых нет кода, исключительно для удобства чтения (транслятор Форта «проглотил» знаки табуляции, поэтому между кодом и комментарием остался 1 пробел).

рис 12
Пример 5. Здесь вычисляется объем куба и площадь его боковой поверхности. Вначале приведем работу с целочисленным аргументом.
: B5 ( A -> V=A^3 S=6*A^2 )
DUP 2DUP \ A -> A A A A
* * \ A A A A -> A A^3
SWAP \ A A^3 -> A^3 A
DUP * 6 * \ A^3 A -> A^3 A^2*6 – V=A^3 S=6*A^2
;
Поясним код:
Во второй строчке кода 2DUP, в отличие от DUP дублирует сразу 2 верхних элемента, совместное использование (DUP и 2DUP) оставляют четыре числа A.
В третьей, два умножения «* *» приводят к вычислению куба ( A A A A -> A A*A*A=A^3 ).
В четвертой, SWAP меняет куб числа «A» с ним самим местами ( A A^3 -> A^3 A ).
В пятой, «DUP *» (A^3 A -> A^3 A*A ), возводит в квадрат число на вершине стека, а «6 *» умножает его на шесть. В результате получаем площадь боковой поверхности.
Вызовем написанное слово с параметром 15 (сторона куба):
15 B5
Ok ( 3375 1350 )
3375=15*15*15 и 1350=6*15*15, все верно, слово работает корректно.
То же самое с вещественными числами:
: B5 ( A -> V S ) \ V=A^3 S=6*A^2
FDUP FDUP FDUP \ A -> A A A A – 2FDUP SP-Forth не понимает
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
;
Проверим написанный код, возьмем куб со стороной 1,5:
15E-1 B5 F. F.
13.500000 3.3750000 Ok \ 6*1.5^2 = 13.5 1.5^3 = 3.375
Помните, что оператор «F.» печатает то, что лежит на вершине стека. Если вам нужен другой порядок можно применить FSWAP, так при необходимости вывести сперва объем, как в стековой нотации, можно набрать следующее:
15E-1 B5 FSWAP F. F.
3.3750000 13.500000 Ok

рис 13
Самостоятельно напишите слово «B5.» для вещественного варианта и корректный вывод результатов с целочисленного стека в первом случае.
Пример 6. Здесь необходимо вычислить объем и площадь поверхности прямоугольного параллелепипеда, через его ребра.
: 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)
Где (A*B+B*C+C*A)*2 – это площадь поверхности, а (C*A*B) – объем.
В данном примере появляется 3 параметра, что не слишком усложняет задачу, и по-прежнему мы не будем использовать переменные в явном виде, манипулируя только с данными на стеке.
В коде для вещественных чисел надо, чтобы число элементов не превышало максимума, из-за его ограниченности произойдет ошибка. Проверим сколько вмещает наша система, для этого наберем следующие команды:
FDEPTH \ Это слово возвращает количество элементов в вещественном стеке
Ok ( 0 ) \ 0 элементов
5E-1 FDEPTH \ введем 1-ое число
Ok ( 0 1 ) \ 1 элемент на вещественном стеке
5E-1 FDEPTH \ введем 2-ое число
Ok ( 0 1 2 ) \ 2 элемента
5E-1 FDEPTH \ введем 3-е число
Ok ( 0 1 2 3 ) \ 3
5E-1 FDEPTH \ введем 4-ое число
Ok ( 0 1 2 3 4 ) \ 4
5E-1 FDEPTH \ введем 5-ое число
Ok ( [6].. 1 2 3 4 5 )
5E-1 FDEPTH \ введем 6-ое число
Ok ( [7].. 2 3 4 5 6 )
5E-1 FDEPTH \ введем 7-ое число
Ok ( [8].. 3 4 5 6 7 )
5E-1 FDEPTH \ ошибка !!!
Если после ошибки ввести «F.» получим:
infinity Ok
После ошибки лучше перезапустить SP-Forth. Так же не забывайте о подключении библиотек заново для работы с вещественными числами. Существует слово DEPTH для обычного стека, которое также оставляет количество его элементов, не считая возвращаемый параметр, но для целочисленного стека.

рис 14
Или можете набрать «FINIT» для переинициализации.

рис 15
Перепишем Пример 6 для вещественных чисел.
: 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. \ 1-ый результат – объем
FROT F* F+ 2.E F* \ (B+C) B*C A -> {B*C+A*(B+C)}*2
F. \ 2-ой результат S=2*(A*B+B*C+A*C)
;
Теперь можно проверить как работает написанное слово:

рис 16
1E-1 2E-1 3E-1 B6
0.0060000 0.2200000 Ok
Объем прямоугольного параллелепипеда 0,006=0,1*0,2*0,3 и площадь его поверхности 0,22=2*(0,1*0,2+0,2*0,3+0,1*0,3).
Проведите тесты с целочисленным вариантом. Как вывести результаты, нужны ли дополнительные манипуляции со стеком? Последний вариант получился не универсальным, печать результатов в самом слове. Можете это исправить как уже было сделано ранее?
Пример 7. Зная радиус окружности, посчитаем его длину и площадь.
: B7 ( R -> L S) \ L=2*Pi*R и 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
;
Целочисленный вариант принимает целое значение радиуса и выдает результат в 100 раз больше. Надеюсь по комментариям стековой нотации работа слова понятна (она довольно тривиальна).
Результаты теста с ранее написанным словом «.2»:

рис 17
Для печати с тем же порядком как в стековой нотации наберите «1 B7 SWAP .2 .2»
Код для вещественного аргумента:
: B7 ( R -> L S) \ L=2*Pi*R и 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
;
Вычислим длину окружности и площадь круга радиусом 0,1:
1E-1 B7 F. F.
0.0314000 0.6280000 Ok
0.0314000=0,1*0,1*3,14 и 0.6280000= 2*3,14*0,1. Результаты теста корректны.

рис 18
Пример 8. Простая задачка на вычисление среднего арифметического двух целых чисел:
: B8 ( A B -> [A+B]/2 ) + 2/ ;
1 3 B8
Ok ( 2 )
Мини-код работает правильно (1+3)/2=2. Ниже приведем код для вещественного аргумента:
: B8 ( A B -> [A+B]/2 ) F+ 2E F/ ;

рис 19
1E-1 2E-1 B8 F.
0.1500000 Ok
0.15 = (0.1+0.2)/2 – ИСТИНА
Пример 9. Среднее геометрическое двух чисел – это квадратный корень из их произведения. Сразу напишем код для вещественного аргумента, так как возможности извлечение корня для целых чисел в системе SP-Forth нет, для этого придётся переводить целое число в вещественное извлечь квадратный корень, затем перевести обратно в целый вид, поэтому здесь такие хлопоты не оправданы, но если где-то вам это понадобится, то такое возможно.
: B9 ( A B -> SQRT[A*B] ) F* FSQRT ;
Очень короткий и понятный код, который тестируем ниже:
3E-1 75E-1 B9 F.
1.5000000 Ok \ 1,5 = Корень_Квадратный_из(0,3*7,5) – ИСТИНА

рис 20
Этот и предыдущий примеры можно оформить красиво, для дальнейшего использования в математических вычислениях или в других программах, как ваши библиотечные функции.
: MIDDLE_ARITHMETIC ( A B -> [A+B]/2 ) F+ 2E F/ ;
: MIDDLE_GEOMETRIC ( A B -> SQRT[A*B] ) F* FSQRT ;
За грамотные английские названия не ручаюсь.
Пример 10. Вход два числа, не равные нулю. Вычислим сумму, разность, произведение и частное их квадратов, те есть:
: 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)=(-)
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 )=(/)
;
Протестируем на числах 4 и 2.
4 2 B10
Ok ( 20 12 64 4 )
Всё корректно, проверяйте самостоятельно. В комментариях я сократил сумму, разность и произведение квадратов до соответствующих операций в скобках. Специально подобраны такие числа, чтобы результат деления был целочисленным, но это не обязательно – код для вещественных аргументов избавит нас от таких неудобств:
: 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)=(-)
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)=(/)
;
Тест примера 10:
1E-1 2E-1 B10 F. F. F. F.
0.2500000 0.0004000 -0.0300000 0.0500000 Ok
Не забываем, что оператор F. Печатает число с вершины стека, поэтому сначала печатается частное, затем произведение, после чего разность и в конце сумма.
0,25 = 0,01/0,04; 0,0004 = 0,01*0,04; -0,03 = 0,01-0,04; 0,05 = 0,01+0,04.
Если вам нужен другой порядок вывода результатов, то самостоятельно решите эту задачу для обоих вариантов, слова ниписаны в соответствии со стековой нотацией.
Полезные слова (если переименовать и разбить некоторые из приведенных выше, то получим список, который можете использовать как свою маленькую математическую библиотеку):
: SquarePerimeter ( a -> P=4*a ) 4E F* ;
: SquareArea ( a -> S=a*a ) FDUP F* ;
: RectanglePerimeter ( a b -> P=2*[a+b] ) F+ 2E F* ;
: RectangleArea ( a b -> S=a*b ) F* ;
: CircleLength ( r -> l=2*Pi*r ) 2E FPI F* F* ;
: CircleArea ( r -> S=Pi*r*r ) FDUP F* FPI F* ;
: CubeVolume ( a -> V=a*a*a ) FDUP FDUP F* F* ;
: CubeLateralSurfaceArea ( a -> S=6*a*a ) FDUP F* 6E F* ;
: RectangularPrismVolume ( a b c -> V=a*b*c ) F* F* ;
: RectangularPrismLateralSurfaceArea ( a b c -> S=2*[a*b+b*c+a*c] ) FOVER FOVER F+ FROT FROT F* FSWAP FROT F* F+ 2E F* ;
: ArithmeticMean ( a b -> [A+B]/2 ) F+ 2E F/ ;
: GeometricMean ( a b -> SQRT[A*B] ) F* FSQRT ;
Тесты проведите самостоятельно.

рис 21 Результат работы целочисленного и вещественного вариантов примера B10
BEGIN 11-20
Пример 11. Отличается от 10-ого примера незначительными поправками. Просто заменяем квадрат на модуль: код «DUP *» на «ABS» (и «FDUP F*» на «FABS» для вещественных аргументов).
: B11 ( A B -> {|A|+|B|} {|A|-|B|} {|A|*|B|} {|A|/|B|} )
SWAP ABS SWAP ABS \ A B ->|A| |B|
2DUP + \ |A| |B|-> |A| |B| (|A|+|B|)
ROT ROT 2DUP – \ |A| |B| (|A|+|B|) -> (|A|+|B|) |A| |B| (|A|-|B|)
ROT ROT 2DUP * \ (+) |A| |B| (-) -> (+) (-) |A| |B| (|A|*|B|)
ROT ROT / \ (+) (-) |A| |B| (*)-> (+) (-) (*) (|A|/|B|)
;
В случае для вещественных аргументов:
: B11 ( A B -> {|A|+|B|} {|A|-|B|} {|A|*|B|} {|A|/|B|} )
FSWAP FABS \ A B -> B |A|
FSWAP FABS \ B |A| -> |A| |B|
FOVER FOVER F+ \ |A| |B|-> |A| |B| (|A|+|B|)=(+)
FROT FROT FOVER FOVER F- \ |A| |B| (+) -> (+) |A| |B| (|A|-|B|)=(-)
FROT FROT FOVER FOVER F* \ (+) |A| |B| (-) -> (+) (-) |A| |B| (|A|*|B|)=(*)
FROT FROT F/ \ (+) (-) |A| |B| (*)-> (+) (-) (*) (|A|/|B|)=(*)
;

Рис 22
В комментариях (скобках) соответствующие сумма, разность, произведение и частное взяты в фигурные скобки для визуального выделения элементов на стеке. Обычные скобки в данном случае применять нельзя, так как они обозначают комментарий и являются зарезервированными словами Форта и системы программирования SP-Forth в частности.
Тесты на корректность работы написанных слов произведите самостоятельно.
Пример 12. Вычислить гипотенузу и периметр прямоугольного треугольника по его катетам. Так как мы имеем дело с квадратным корнем, сразу приведем код для случая с вещественным аргументом.
: B12 ( A B -> C P ) \ C=Квадратный_Корень(A^2+B^2) P=A+B+C
FOVER FDUP F* \ A B -> A B A^2
FOVER FDUP F* \ A B A^2 -> A B A^2 B^2
F+ FSQRT \ A B A^2 B^2 -> A B Квадратный_Корень(A^2+B^2)=C
FROT FROT F+ \ A B C -> C A+B
FOVER F+ \ C A+B -> C A+B+C=P
;
Проверим на прямоугольном треугольнике с катетами 3 и 5:
3E 4E B12 F. F. \ вызываем нашу подпрограмму и печатаем результат
12.000000 5.0000000 Ok
3^2+4^2=25. Квадратный корень из 25=5. 5+3+4=12– что является истиной. В данном случае специально подобрана Пифагорова тройка, для простоты проверки. Проверим общий случай:
3E 5E B12 F. F.
13.830952 5.8309519 Ok
Можете самостоятельно проверить истинность теста.

Рис 23
Пример 13. Найти площади двух кругов (с общим центром) и кольца между ними. Даны радиусы R1 и R2, причем R1 > R2. Как и ранее сперва напишем слово для целочисленных чисел. Если не совсем понятно почему я не пишу сразу универсальный вариант для вещественных данных, то поясняю: отладка в этом случае наиболее проста для сложных слов и для начинающих, так как все данные на стеке видны сразу после их ввода, то получается легко проверить и понять работу кода пошагово вводя команду за командой. Этого преимущества лишены операторы для работы с вещественными числами. После написания слова с целыми аргументами не сложно перевести его код для работы с вещественными и получить результат того же типа.
: B13 ( R1 R2 -> S1 S2 S3) \ S1=Pi*R1^2 S2= Pi*R2^2 S3=S1-S2
SWAP DUP * 314 * \ R1 R2 -> R2 (Pi*R1^2)=S1
SWAP DUP * 314 * \ R2 S1 -> S1 (Pi*R2^2)=S2
2DUP – \ S1 S2 -> S1 S2 (S1-S2)=S3
;
Запустим наше слово на примере двух кругов с радиусами 25 и 15 соответственно.
25 15 B13
Ok ( 196250 70650 125600 )
Выше приведен вариант кода с целочисленными аргументами, причем все 3 площади больше в 100 раз из-за того, что мы приняли Пи равным 314. При необходимости используйте написанное ранее слово «.2». Далее перепишем пример для случая вещественных аргументов.
: B13 ( R1 R2 -> S1 S2 S3) \ S1=Pi*R1^2 S2= Pi*R2^2 S3=S1-S2
FSWAP FDUP F* 314E-2 F* \ R1 R2 -> R2 (Pi*R1^2)=S1
FSWAP FDUP F* 314E-2 F* \ R2 (Pi*R1^2)=S1 -> (Pi*R1^2)=S1 (Pi*R2^2)=S2
FOVER FOVER F- \ S1 S2 -> S1 S2 (S1-S2)=S3
;
Тестирование примера 13:
25E-1 15E-1 B13 F. F. F.
12.560000 7.0650000 19.625000 Ok
S1 = 19,625 = 3.14*2.5^2; S2 = 7,065 = 3.14*1.5^2; S3=S1-S2=12,56=19,625-7,065.
Тестирование прошло успешно. Не забываем про обратный порядок печати со стека. Написанное слово работает правильно, соответственно стековой нотации. Если вам необходим другой порядок вывода, то можете самостоятельно скорректировать слово, добавив код после вызова «B13» и до вывода «F. F. F.». Ранее аналогичные манипуляции со стеком уже совершались. Также в зависимости от того в каком виде вам нужны результаты вывода слова «B13» в первом случае, проведите соответствующую обработку по выводу на экран или напишите слово «B13.». Чтобы отличать обе версии, добавьте к именам «I и F», чтобы получились имена «B13I» и «B13F» и соответствующие им слова для печати результатов «B13I.» и «B13F.»

рис 24
Пример 14. Определить радиус окружности и площадь круга, через ее длину. Сразу составим программку для вещественного аргумента, ибо целочисленное огругление будет давать неприемлемый по качеству результат для малых значений длины окружности.
: B14 ( L -> R S ) \ R=L/(2*Pi) S=Pi*R^2
628e-2 F/ \ L -> R=L/6.28 где 6,28=2*Pi=D
FDUP FDUP F* 314e-2 F* \ R -> R Pi*R^2
;

рис 25
Посчитаем R и S для L=25,37
2537E-2 B14 F. F.
51.244976 4.0398089 Ok
R=25.37/6.28= 4,0398 и S=3,14* 4,0398^2= 51,244. Тест прошел успешно.
Перепишем код с учетом слова «FPI» (значение «Пи» получаемое системой). Наберите «FPI F.», чтобы узнать точное значение получаемое Форт-словом.
: B14 ( L -> R S ) \ R=L/(2*Pi) S=Pi*R^2
FPI 2E F* F/ \ L -> R=L/6.28 где 6,28=2*Pi=D
FDUP FDUP F* FPI F* \ R -> R Pi*R^2
;
2537E-2 B14 F. F.
51.218997 4.0377609 Ok
Сами можете сравнить уточненные результаты, при использовании более точного значения «Пи». Второй вариант не только универсален, но и незаменим для научных расчетов.
Пример 15. Зная площадь круга, вычислить его диаметр и длину.
: B15 ( S -> D L ) \ D=Квадратный_Корень(4*S/Pi) L=Pi*D
4E F* \ S -> 4*S
314E-2 F/ \ 4*S -> 4*S/Pi
FSQRT \ 4*S/Pi -> Квадратный_Корень(4*S/Pi)=D
FDUP 314E-2 F* \ D -> D D*Pi=L
;
Посчитаем диаметр и длину круга площадью равной 12,345.
12345E-3 B15 F. F.
12.452036 3.9656166 Ok
Квадратный корень из (12,345*4/3.14) равно 3,965616, а 3,965616*3,14=12,4520, то ест ь ИСТИНА. Пример довольно простой и нет других причин писать код для целочисленного варианта аргументов. В случае необходимости несложно самостоятельно решить эту задачу.

рис 26
Пример 16. Вычислим расстояние между двумя точками на числовой оси, зная координаты.
: B16 ( X1 X2 -> |X1-X2| )
– ABS \ X1 X2 -> |X1-X2|
;
Для вещественных аргументов.
: B16 ( X1 X2 -> |X1-X2| )
F- FABS \ X1 X2 -> |X1-X2|
;

рис 27
31E-1 -12E1 B16 F.
123.10000 Ok \ |3.1-(-120)|=123.1
Пример 17. По трем координатам на числовой оси (X1, X2, X3) вычислить следующие расстояния: |x1-x3|, |x2-x3| и их сумму. Сперва для целых чисел.
: B17 ( X1 X2 X3 -> |x1-x3| |x2-x3| {|x1-x3|+|x2-x3|} )
SWAP OVER \ X1 X2 X3 -> X1 X3 X2 X3
– ABS \ X1 X3 X2 X3 -> X1 X3 |X2-X3|
ROT ROT – ABS SWAP \ X1 X3 |X2-X3| -> | X1-X3| |X2-X3|
2DUP + \ | X1-X3| |X2-X3|-> | X1-X3| |X2-X3| (| X1-X3|+|X2-X3|)
;
Для вещественных.
: B17 ( X1 X2 X3 -> |x1-x3| |x2-x3| {|x1-x3|+|x2-x3|} )
FSWAP FOVER \ X1 X2 X3 -> X1 X3 X2 X3
F- FABS \ X1 X3 X2 X3 -> X1 X3 |X2-X3|
FROT FROT F– FABS FSWAP \ X1 X3 |X2-X3| -> | X1-X3| |X2-X3|
FOVER FOVER F+ \ | X1-X3| |X2-X3|-> | X1-X3| |X2-X3| (| X1-X3|+|X2-X3|)
;
Тест на координатах
–1E1 1E-1 3E2 B17 F. F. F.
609.90000 299.90000 310.00000 Ok
|X1-X3|=|-10-300|=310; |X2-X3|=|0.1-300|=299.9; (|X1-X3|+|X2-X3|)=310+299.9=609.9.

рис 28
Пример 18. Схож с предыдущей задачей. Сумма заменяется произведением.
: B18 ( X1 X2 X3 -> {|x1-x3|*|x2-x3|} )
SWAP OVER \ X1 X2 X3 -> X1 X3 X2 X3
– ABS \ X1 X3 X2 X3 -> X1 X3 |X2-X3|
ROT ROT – ABS * \ X1 X3 |X2-X3| -> {|x1-x3|*|x2-x3|}
;
–5 2 7 B18
Ok ( 60 )
|-5-7|*|2-7|= 12*5=60
Для вещественных чисел.
: B18 ( X1 X2 X3 -> {|x1-x3|*|x2-x3|} )
FSWAP FOVER \ X1 X2 X3 -> X1 X3 X2 X3
F- FABS \ X1 X3 X2 X3 -> X1 X3 |X2-X3|
FROT FROT F– FABS F* \ X1 X3 |X2-X3| -> {|x1-x3|*|x2-x3|}
;
–1E1 2E-1 23E1 B18 F.
55152.000 Ok
|-10-230|*|0.2-230|=240*229.8=55152

рис 29
Пример 19. По координатам противоположенных вершин прямоугольника вычислить его периметр и площадь, стороны параллельны координатным осям.
: B19 ( X1 Y1 X2 Y2 -> P S ) \ P=2*[A+B] S=A*B
ROT – ABS \ X1 Y1 X2 Y2 -> X1 X2 |Y2-Y1|
SWAP ROT – ABS \ X1 X2 |Y2-Y1| -> |Y2-Y1|=A |X2-X1|=B
2DUP + 2* \ A B -> A B 2*(A+B)=P
ROT ROT * \ A B P -> P A*B=S
;
1 3 7 8 B19 . .
30 22 Ok
A=|1-7|=6 B=|3-8|=5. P=2*(A+B)=2*(6+5)=22. S=A*B=6*5=30.
Вариант с вещественными аргументами не сильно отличается от целочисленного.
: B19 ( X1 Y1 X2 Y2 -> P S ) \ P=2*[A+B] S=A*B
FROT F- FABS \ X1 Y1 X2 Y2 -> X1 X2 |Y2-Y1|
FSWAP FROT F– FABS \ X1 X2 |Y2-Y1| -> |Y2-Y1|=A |X2-X1|=B
FOVER FOVER F+ 2E F* \ A B -> A B 2*(A+B)=P
FROT FROT F* \ A B P -> P A*B=S
;
11E-1 15E-1 73E-1 62E-1 B19 F. F.
29.140000 21.800000 Ok
A=|1.5-6.2|=4.7; B=|1.1-7.3|=6.2; P=2*(4.7+6.2)= 21,8; S=A*B=4.7*6.2= 29,14.

рис 30
Пример 20. Вычислить расстояние между двумя точками на плоскости по их координатам. Так как придется извлекать квадратный корень, то вариант с целочисленными координатами пропускаем.
: B20 ( 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
;
11E-1 15E-1 73E-1 62E-1 B20 F.
7.7801028 Ok
A=|1.5-6.2|=4.7; B=|1.1-7.3|=6.2; R= Квадратный_Корень(A^2+B^2)= Квадратный_Корень(22.09+ 38,44)= 7,7801.

рис 31
Полезные слова из предыдущих десяти примеров:
: Hypotenuse ( A B -> C P ) \ C=Квадратный_Корень(A^2+B^2) P=A+B+C
FDUP F* FSWAP FDUP F* F+ FSQRT ; \ A B -> SQRT(A^2+B^2)
: RightTriangleP ( A B C -> P ) \ P=A+B+C
F+ F+ ;
: TriangleP ( A B C -> P ) \ P=A+B+C
F+ F+ ;
Если даны только 2 катета, то можем вычислить периметр так:
3E 4E FOVER FOVER Hypotenuse TriangleP F.
12.000000 Ok
: R1 ( X1 X2 -> R1=|X1-X2| ) \ R1 – расстояние между двумя точками на прямой
F- FABS \ X1 X2 -> |X1-X2|
;
: R2 ( X1 Y1 X2 Y2-> R ) \ R= SQRT{[X2-X1]^2+[Y2-Y1]^2}
\ R2 – расстояние между двумя точками на плоскости
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
;
BEGIN 21-30
Перед решением очередного примера рассмотрим, как объявляются и используются переменные в 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 ( 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. В стековом комментарии написано, то что остается после работы строки (из-за нехватки места), присвоение переменных FA, FB, FC никак не обозначено.
Шестая строка вычисление периметра и его дублирование
Седьмая вычисление полупериметра.
С восьмой по десятую – вычисление сомножителей в формуле площади.
Одиннадцатая – вычисление самой площади.
Проверим работу слова на координатах: (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

рис 32
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 ;

рис 33 без использования переменных
Теперь напишем с переменными:
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

рис 34 с целочисленными переменными
Обмен вещественных переменных.
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

рис 35 с вещественными переменными
У нас получилось четыре слова с одинаковыми именами, и каждое заменяет предыдущее, о чем нас предупреждает Форт система – 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.

рис 36
Для вещественного аргумента нужны соответствующие переменные.
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
Инициализация и вывод слова «1E FA F! 2E FB F! 3E FC F! B23 FA F@ F. FB F@ F. FC F@ F.» получились не короче самого слова. Оформите в отдельное слово для обоих вариантов самостоятельно.

рис 37
Пример 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

рис 38

рис 39
Пример 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

рис 40
А вот при аргументе равным нулю выдает ошибку (возведение нуля в степень):
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

рис 41
Но во второй раз у меня выдал другой ответ (разумеется уже некорректный):
0E B25 F.
infinity Ok
После этого лучше перезапустить систему и заново подключить библиотеки работы с вещественными числами или воспользоваться знакомым словом «FINIT», иначе он их не распознает:
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.» по-прежнему выдает ошибку. Будьте осторожны с возведением в степень, это опасная операция, вызывающая много ошибок.
Не привожу скана с тестом примера 26, разберите самостоятельно.
Пример 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 )
Без комментариев.

рис 42
С вещественным аргументом задача ничуть не сложнее. Заменяем все операторы на соответствующие, просто добавив «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

рис 43
Пример 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
;

рис 44

рис 45
Тесты с вещественным параметром.
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 раз больше, поэтому его код не приводится. При необходимости можно его получить из кода для вещественного аргумента. Также можете воспользоваться словом «.2»
: 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».

рис 46
Пример 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

рис 47
Как и в предыдущем случае, перепишите для увеличения точности.
Если вы планируете использовать последние два слова в своей работе, то можно их назвать по практичнее и добавить в свой файл Форт-расширения (или в свою математическую библиотеку).
Например, 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
;
Назовите файл как вам нравится, но обязательно с расширением «.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.
Так мы разделяем алгоритм работы слова с его примитивным интерфейсом вывода результатов на экран. В таком случае при необходимости что-то доработать, одно не помешает другому и, разумеется, код становится более лаконичным и понятным. Оригинал «B28» можно использовать в других словах, библиотеках, и это никак не помешает по-прежнему пользоваться примером 28. Так же при решении сложной задачи, можно писать аналогичные промежуточные слова «Name.» для отладки и контроля промежуточного состояния сложной системы слов, когда не понятно на каком этапе возникает ошибка.

рис 48
Darmowy fragment się skończył.