Arsen Gonian
Язык программирования Форт (Forth). Решение задач по программированию. Версия 2.

На сайте hi-aga.ru вы можете скопировать текст кода программ без искажений, связанные с форматированием текста (исходник книги в формате doc), также возможны некорректное отображение номеров страниц в оглавлении (по той же причине). По ссылке hi-aga.ru вы также найдете дополнительные материалы, не вошедшие в данную книжку. Сайт будет дополняться чаше нежели книжка. Об обнаруженных ошибках, также можете сообщить по контактам на сайте.

Оглавление

0 Введение                                                            3

1.1 BEGIN 1-10                                                            6

1.2 BEGIN 11-20                                                      15

1.3 BEGIN 21-30                                                      21

1.4 BEGIN 31-40                                                      32

2 Integer 1-30                                                            39

3.1 Boolean 1-20                                                      48

3.2 Boolean 21-40                                                      67

4 If 1-30                                                            88

5 Case 1-20                                                            123

6 FOR 1-40                                                            153

7 While 1-30                                                            189

8 Литература                                                            223

Введение

Эта книга поможет вам освоить язык программирования Форт с нуля. Синтаксис этого ЯП (Языка Программирования) настолько прост, что его практически нет. Есть только одно главное правило – все Слова (именно так называются операторы языка и определяемые пользователем слова-функции) и числа должны быть разделены между собой хотя бы одним символом пробела и/или табуляции и/или переноса строки (то есть «любой символ разделитель»).

Для кого этот сайт?

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

Среда программирования SP-Forth – замечательно подойдет для наших целей. Программирование в этом консольном приложении можно начать сразу после быстрого скачивания и установки с официального сайта последней его версии (SP-Forth свободное ПО с открытым исходным кодом). Работать мы будем в режиме интерпретатора. Это значит, что после запуска системы программирования SP-Forth можно вводить команды или целую программу, после, нажав <Enter>, приложение обработает код и выдаст, предусмотренные кодом результаты. Скачивание и установка SP-Forth не должен вызвать никаких сложностей, а потому этот процесс мы здесь опустим.

Исторически сложилось, что основы программирования начинают изучать с первой программы, которая выводит на экран «Hello, World!», что по-русски означает «Здравствуй, Мир!».

Код для ЯП Форт будет следующий:

.( Hello world!)

или

S" Hello, World!" TYPE

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

Третий вариант этой же программы будет выглядеть так:

: Hello_World ." Hello, World!" ;

Hello_World

Отличие в том, что мы определяем одноименное слово, которое затем вызывается. Первые два варианта работают в режиме интерпретации, а последний, компиляции (в форт-словаре создается новое слово). Будучи универсальным, его можно вызывать многократно из различных мест программы или даже использовать сей код в своих библиотеках.

Так мы написали первую полноценную программку. Вместо Hello_World, вы его можете назвать Start или как захочется, главное придерживаться простого правила – названия должны быть информативными. Стиль программирования на ЯП Форт ничем не отличается от других языков, будет полезно в дальнейшем почитать об этом дополнительно, для правильного оформления своего кода.

Так мы определили новое слово. Определение слова начинается с двоеточия, затем идет любой символ-разделитель (пробел, табуляция или перевода строки). После идёт имя слова, которое вы сами придумаете, далее код – реализация (последовательность операторов, чисел и других уже определённых слов), разделенных пробелами. Завершается точкой с запятой, также отделенный от кода пробелом.

Начало уже положено. Первая программа курса программирования на Форте для начинающих выглядит довольно просто.

Добавим пару штрихов. Комментарии:

Принято в каждом определении нового слова после названия писать комментарий, обозначающий что слово берет со стека в качестве параметров и что оставляет на нем. Перепишем наше первое Форт-Слово:

: Hello_World ( -> ) ." Hello, World!" ; Hello_World

Так как Hello_World оставляет стек без изменений (не трогает его), то до и после стрелки ничего нет. Комментарий – это содержимое скобок.

Также существует второй способ комментирования кода до конца строки. Это символ – «\».

\ это программа, выводящая сообщение «Hello, World!»

: Hello_World ( -> ) ." Hello, World!" ;

Hello_World

Программировать самому не составляет особой сложности даже для совсем начинающих. Это вам не язык программирования С или С++. Все довольно наглядно и просто. Можно практиковать программирование онлайн или офлайн. Для первого варианта существует скрипт транслятор языка форт. Мы же будем ориентироваться на конкретный диалект – 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 и целая часть 1/2

Вы можете сказать программирование на питоне – гораздо богаче, возможностями и будете правы. Но Форт быстрее и в качестве первого языка программирования гораздо проще и легче его освоить, а самое главное понятнее. Учить программирование на паскале, на мой взгляд, уже совсем не актуально. Хотя он и хорош для изучения различных алгоритмов, но это уже не современный язык программирования, со множеством избыточных конструкций и синтаксиса.

Приведем примеры на 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

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

: ^2 ( A -> A^2 ) DUP * . ;\ возведение числа в квадрат

^ -2003 WORD OR FILE NOT FOUND

При «Копи пасте» куда-то делись символы табуляции между «;» и комментарием, вследствие видим сообщение об ошибке, из-за нарушения синтаксиса.

Также, вместо ^2, можно слово назвать **2, в стиле python:

: **2 ( A -> A^2 ) DUP * . ;            \ возведение числа в квадрат

5 **2

25 Ok

В итоге получаем тот же ответ, но стилистика программы изменилась.

Для начала этого достаточно. Далее при решении конкретных задач, в среде программирования SP-Forth, процесс станет более понятным и осознанным.

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

Точка «.» – это стандартное форт слово, которое печатает число на вершине стека на экран.

Напомним, общий вид определения нового слова в Форте:

: Название-Слова ( стек до выполнение –> после ) Код-Тела-Функции ; \ комментарий

Пример 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 * ;      \ отличие только в названии

Какой нравится, тот и можете использовать. Или все сразу, так тоже можно.

Пример 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. Слово работает корректно: площадь и периметр вычисляются соответственно стековой нотации, а выводятся по условию задачи.

Пример 4. Нужно вычислить длину круга зная его диаметр:

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

Ответ буде в 100 раз больше для целочисленных данных, таким образом избавимся от дробной части. Перепишем код, чтобы можно было работать с вещественными числами. Для этого в SP-Forth нужно подключить соответствующие библиотеки. Скопируйте и вставьте следующие две строчки:

S" lib\include\float.f" INCLUDED

S" lib\include\float2.f" INCLUDED

Но можно только вторую строчку.

Теперь чтобы ввести вещественное число, скажем 0,5, нужно набрать на клавиатуре следующее:

5E-1

До E – это мантисса (число), после экспонента (степень). Мантисса и экспонента могут быть как положительными (знак не требуется), так и отрицательными (в данном случае степень -1, что значит 10 в минус первой степени).

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

5E-1 F.

В ответ увидим:

0.5000000 Ok

Слово «F.», аналогично, как и «.» выводит число на вершине стека на экран, только не с целочисленного, а вещественного.

Теперь мы можем переписать пример 4 для вещественных аргументов:

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

314E-2 F* ;

Посчитаем длину окружности диаметром 0,5, набрав следующее:

5E-1 B4 F.      \ вызываем слово, которое считает длину и «F.» печатает ответ

1.5700000 Ok

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

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

Пример 3:

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

FOVER FOVER      \ A B -> A B A B

\ Слово FOVER, дублирует слово под вершиной стека на ее вершину т.е. по схеме (A B -> A B A)

\ Повторив его 2 раза получим ( A B -> A B A B )

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

\ Площадь вычислен – это просто произведение сторон

F+ 2E F* F. ;      \ складываем A и B, и умножив на 2, оператором F*, получаем периметр

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

2E-1 3E-1 B3

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.» выведет на экран тоже самое значение.

Универсальный вариант того же примера, если вы не хотите сразу печатать результаты обработки в слове:

: 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 A*B=S -> S A B

F+ 2E F* ;      \ складываем A и B, и умножив на 2, оператором F*, получаем периметр

Проверим. Посчитаем площадь и периметр прямоугольника со сторонами 0,2 и 0,3:

2E-1 3E-1 B3

Ok

F. F.

1.0000000 0.0600000 Ok

Сначала выводит периметр затем площадь, чтобы изменить порядок как указано в стековой нотации нужно набрать команду FSWAP перед печатью результатов, то есть:

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

0.0600000 1.0000000 Ok

Результаты по-прежнему верны.

Вы можете спросить зачем такие сложности? Код становится универсальным, мы отделяем вычисляемую часть от метода вывода данных на экран, его можно включать в свои библиотеки, и использовать в других задачах как отдельную функцию.

Как вы уже могли заметить одно замечательное свойство Форта – его слова-функции не только принимают любое количество аргументов, но также оставляют на стеке желаемое число результатов, не каждый ЯП может этим похвастаться.

Вы можете заметить, что, в примере 3, каждая строка кода сопровождается комментарием, которая поясняет изменения на стеке. Так как все операции и манипуляции, в данном примере, производятся только с вещественным стеком, мы это никак не обозначаем, когда слово будет работать с обоими стеками сразу, вещественный стек будем выделять так: «F: A B A*B=S -> S A B», изменения целочисленного стека дополнительно выделять не будем. Существует еще и стек возвратов. Будьте осторожны, любые неосторожные манипуляции приведут к некорректным результатам, в лучшем случае, в худшем вызовут сообщения об ошибке.

Так же обратите внимание, что комментарии выравниваются так чтобы они выстроились в вертикальную линию, даже те строчки, в которых нет кода, исключительно для удобства чтения.

Пример 5. Здесь вычисляется объем куба и площадь его боковой поверхности. Вначале приведем работу с целочисленным аргументом.

: B5 ( A -> V S )

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

Пример 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 для обычного стека, которое также оставляет количество его элементов, не считая возвращаемый параметр.

Теперь перепишем Пример 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)

;

Теперь можно проверить как работает написанное слово:

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 раз больше. Надеюсь по комментариям стековой нотации работа слова понятна (она довольно тривиальна).

Код для вещественного аргумента:

: 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. Результаты теста корректны.

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

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) – ИСТИНА

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

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

Если вам нужен другой порядок вывода результатов, то самостоятельно решите эту задачу.

BEGIN 11-20

Пример 11. Отличается от 10-ого примера незначительными поправками. Просто заменяем квадрат на модуль: код «DUP *» на «ABS».

: 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|) |A| |B| (|A|-|B|)

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

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

;

В комментариях стековой нотации (скобках) соответствующие сумма, разность, произведение и частное взяты в фигурные скобки для визуального выделения элементов на стеке. Обычные скобки в данном случае применять нельзя, так как они обозначают комментарий и являются зарезервированными словами Форта и системы программирования 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 и 4:

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

Можете самостоятельно проверить истинность теста.

Пример 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. Теперь перепишем данный пример для случая с вещественными аргументами.

: 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.».

По-прежнему, мы считаем «Пи» равным «3,14». Для большей точности мы можем использовать слово «FPI», вместо числового значения в коде, который оставляет значение числа «Пи» на вещественном стеке. Проверим работу этого слова. Введем следующий код:

FPI F.

3.1415927 Ok

Самостоятельно перепишите код, с учетом этих изменений.

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

;

Посчитаем 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».

: 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* 314e-2 F*       \ R -> R Pi*R^2

;

2537E-2 B14 F. F.

51.193031 4.0377609 Ok

Сами можете сравнить уточненные результаты, при использовании более точного значения числа «Пи». Второй вариант не только универсален, но и незаменим для научных расчетов.

Пример 15. Зная площадь круга, вычислить его диаметр и длину.

: B15 ( S -> D L )                   \ D=Квадратный_Корень(4*S/Pi) L=Pi*D

4E F*                   \ S -> 4*S

FPI F/                   \ 4*S -> 4*S/Pi

FSQRT                   \ 4*S/Pi -> Квадратный_Корень(4*S/Pi)=D

FDUP FPI F*             \ D -> D D*Pi=L

;

Посчитаем диаметр и длину круга площадью равной 12,345.

12345E-3 B15 F. F.

12.455194 3.9646112 Ok

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

Пример 16. Вычислим расстояние между двумя точками на числовой оси, зная координаты.

: B16 ( X1 X2 -> |X1-X2| )

– ABS      \ X1 X2 -> |X1-X2|

;

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

: B16 ( X1 X2 -> |X1-X2| )

F- FABS      \ X1 X2 -> |X1-X2|

;

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.

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

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

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

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.

Шестая строка вычисление периметра и его дублирование

Седьмая вычисление полупериметра.

С восьмой по десятую – вычисление сомножителей в формуле площади.

Одиннадцатая – вычисление площади.

Проверим работу слова на координатах: (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

;

3E 9E 10E B33 F. F.

30.000000 3.0000000 Ok

Проверили на тех же данных. И снова не забываем об обратном порядке при печати со стека, не важно с какого (целочисленного или вещественного). Чтобы в начале вывести цену за 1 кг, можно использовать слово FSWAP перед «F.» или переписать строчку

FSWAP FOVER F*      \ Y C1 -> C1 Y*C1= CY

таким образом

FDUP F. F*            \ Y C1 -> Y*C1= CY

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

Пример 34. Продолжение детских задачек.

: B34 ( X A Y B -> CX CY CX/CY )            \ CX=A/X CY=B/Y CX/CY

SWAP /                        \ X A Y B -> X A B/Y=CY

SWAP ROT /                  \ X A CY -> CY A/X=CX

SWAP 2DUP /                  \ CY CX -> CX CY CX/CY

;

5 30 5 15 B34

Ok ( 6 3 2 )

Цена шоколадных конфет равна 30/5= 6 р/кг, ирисок 15/5=3 р/кг, и соответственно шоколадные конфеты дороже ирисок в 6/3=2 раза. Перепишем для вещественных аргументов, чтобы не терять точность для «неудобных данных».

: B34 ( X A Y B -> CX CY CX/CY )            \ CX=A/X CY=B/Y CX/CY

FSWAP F/                  \ X A Y B -> X A B/Y=CY

FSWAP FROT F/                  \ X A CY -> CY A/X=CX

FSWAP FOVER FOVER F/      \ CY CX -> CX CY CX/CY

;

5E 30E 5E 15E B34 F. F. F.

2.0000000 3.0000000 6.0000000 Ok

И снова обратный порядок, результат идентичен первому варианту с целыми аргументами.

Пример 35. Детская задачка на движение лодки в стоячей и движущейся воде.

VARIABLE T2

: B35 ( V U T1 T2 -> S )      \ S=S1+S2, S1=V*T1, S2=(V-U)*T2

T2 !                  \ V U T1 T2 -> V U T1

ROT DUP ROT *            \ V U T1 -> U V V*T1=S1

SWAP ROT –            \ U V S1 -> S1 V-U

T2 @ *                  \ S1 V-U -> S1 [V-U]*T2=S2

+                  \ S=S1+S1

;

Манипуляции на стеке неоправданно усложняются с четырьмя и более элементами, поэтому мы сохраним значение T2 в одноименной переменной. Сперва мы вычисляем путь S1 в стоячей воде, затем считывая время T2 из переменной – путь S2 и соответственно ему скорость. Ответ есть сумма двух путей. Проверим написанное слово.

10 5 1 1 B35

Ok ( 15 )

1 час в стоячей воде со скоростью 10 – это путь равный 10*1=10 и такое же время со скоростью 10-5=5. В итоге общий путь 10+5=15.

С вещественными данными слово будет иметь вид:

FVARIABLE FT2

: B35 ( V U T1 T2 -> S )      \ S=S1+S2, S1=V*T1, S2=(V-U)*T2

FT2 F!                  \ V U T1 T2 -> V U T1

FROT FDUP FROT F*      \ V U T1 -> U V V*T1=S1

FSWAP FROT F-            \ U V S1 -> S1 V-U

FT2 F@ F*            \ S1 V-U -> S1 [V-U]*T2=S2

F+                  \ S=S1+S1

;

10E 5E 1E 1E B35 F.

15.000000 Ok

На предыдущих данных, как и положено, дает тот же результат, но с типом float.

Пример 36. Без комментариев, сразу приведем решение.

: B36 ( V1 V2 S0 T -> S )            \ S=S0+(V1+V2)*T

2SWAP + * +            \ V1 V2 S0 T -> S0+T*(V1+V2)

;

3 5 100 2 B36

Ok ( 116 )

\ 3+5=8, 8*2=16, 16+100=116.

FVARIABLE FS0

: B36 ( V1 V2 S0 T -> S )            \ S=S0+(V1+V2)*T

FSWAP FS0 F!            \ V1 V2 S0 T -> V1 V2 T

FROT FROT F+ F*      \ V1 V2 T -> T*(V1+V2)

FS0 F@ F+            \ T*(V1+V2)+S0

;

3E 5E 100E 2E B36 F.

116.00000 Ok

По предыдущему опыту мы вводим новую переменную, чтобы уменьшить количество элементов на вещественном стеке. Ответ совпадает с первым вариантом, только вещественного формата, что подтверждает корректность теста.

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

: B37 ( V1 V2 S0 T -> S )            \ S=|S0-T*(V1+V2)|

2SWAP + * – ABS      \ V1 V2 S0 T -> |S0-T*(V1+V2)|

;

3 5 100 2 B37

Ok ( 84 )

\ 3+5=8, 8*2=16, |100-16|=84. Проверим как работает модуль, для этого положим S0=10.

3 5 10 2 B37

Ok ( 6 )

\ 3+5=8, 8*2=16, |10-16|=6. Все верно, код работает корректно.

FVARIABLE FS0

: B37 ( V1 V2 S0 T -> S )            \ S=|T*(V1+V2)-S0|

FSWAP FS0 F!            \ V1 V2 S0 T -> V1 V2 T

FROT FROT F+ F*      \ V1 V2 T -> T*(V1+V2)

FS0 F@ F- FABS            \ T*(V1+V2) –> |T*(V1+V2)-S0|

;

3E 5E 100E 2E B37 F.

84.000000 Ok

3E 5E 10E 2E B37 F.

6.0000000 Ok

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

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

: B38 ( A B -> X )                  \ X=-B/A

–1E F* FSWAP F/            \ A B -> -B/A

;

10E 3E B38 F.

–0.3000000 Ok

Код «-1E F*» можно заменить на «FNEGATE», который лучше, но для начала может быть менее наглядным.

Пример 39. Решение квадратного уравнения. Обратите внимание, что в названии вещественных переменных опущены префиксы «F», так как в этих словах используются переменные только одного типа. Если вас это пугает, то вы легко можете добавить их. Если все слова вы добавляете в один файл, то эта проблема будет актуальной, так же вы можете в названиях слов добавлять префикс I (Integer), для функций с целочисленными аргументами и F, с вещественными, иначе SP-Forth, будет выводить сообщения «B39 isn't unique ()», что не является ошибкой, но предупреждением, о том, что при вызове слова будет вызвано определенное последней слово. Могут возникнуть скрытые ошибки логики работы. Так если вы определили целочисленное слово, затем вещественное и после выполняете попеременно слова для разных аргументов, то как было сказано будет вызвана исключительно последняя реализация, что неизбежно приведет к ошибке, связанное с тем что слова будут работать с аргументами в разных стеках, в итоге, будет либо нехватка аргументов, либо порча других данных не предназначенных для этого. Изменив названия при помощи префиксов, вы избавитесь от этих проблем.

FVARIABLE A

: B39 ( A B C -> X1 X2)      \ X1,X2=(-B+-SQRT(D))/2*A

FROT FDUP A F!       F*            \ A B C -> B C*A

FOVER FDUP F*                  \ B C*A -> B C*A B^2

FSWAP 4E F* F- FSQRT            \ B C*A B^2 -> B SQRT{B^2-4*C*A=D}

FSWAP -1E F* FSWAP            \ B D^0.5 -> -B D^0.5

FOVER FOVER F+ A F@            \ -B D^0.5 -> -B D^0.5 –B+D^0.5 A

2E F* F/                  \ -B D^0.5 –B+D^0.5 A -> -B D^0.5 [–B+D^0.5]/[A*2]=X1

FROT FROT F- A F@            \ -B D^0.5 X1 -> X1 –B-D^0.5 A

2E F* F/                  \ X1 –B-D^0.5 A -> X1 [–B-D^0.5]/[A*2]=X2

FOVER FOVER F<            \ X1 X2 –> X1 X2 X1<X2?

IF FSWAP THEN

;

1E 4E 1E B39 F. F.

–3.7320508 -0.2679491 Ok

X^2+4*X+1=0, D=4^2-4*1*1=16-4=12. X1,X2=(-4+-12^0.5)/(2*1)=-2+-SQRT(3)

X1=-2- 1,732= -3,732, X2=-2+1,732=-0,268.

А вот случай, когда D=0

1E 2E 1E B39 F. F.

–1.0000000 -1.0000000 Ok

Если манипуляции со стеком вам не понятны, то мы можем переписать последнее слово с использованием 3-переменных A, B, C, по классической схеме.

FVARIABLE A

FVARIABLE B

FVARIABLE C

: B39 ( A B C -> X1 X2)      \ X1,X2=(-B+-SQRT(D))/2*A, X1,X2

C F! B F! A F!      \ A B C ->

B F@ FDUP -1E F* FSWAP FDUP F*      \ -> -B B^2

A F@ C F@ F* -4E F* F+ FSQRT            \ -B B^2 -> -B {B^2+A*C*(-4)}^0.5=D^0.5

FOVER FOVER F+                  \ -B D^0.5 -> -B D^0.5 -B+D^0.5

A F@ 2E F* F/                        \ -B D^0.5 -B+D^0.5-> -B D^0.5 [-B+D^0.5]/[A*2]=X1

FROT FROT F-                        \ -B D^0.5 X1 -> X1 -B-D^0.5

A F@ 2E F* F/                        \ X1 [-B-D^0.5]/[A*2]=X2

FOVER FOVER F<                  \ X1 X2 X1<X2?

IF FSWAP THEN

;

1E 4E 1E B39 F. F.

–3.7320508 -0.2679491 Ok

Ответ как в первом случае. Какой проще и понятнее решайте сами.

Пример 40. Задача решения системы из двух линейных уравнений с двумя неизвестными. Пропустим вариант с целочисленными аргументами и перейдем к общему случаю.

FVARIABLE A1 FVARIABLE B1 FVARIABLE C1

FVARIABLE A2 FVARIABLE B2 FVARIABLE C2

: B40 ( A1 B1 C1 A2 B2 C2 -> X Y ) \ X=(C1*B2-C2*B1)/D, Y=(A1*C2-A2*C1)/D, D=A1*B2-A2*B1

C2 F! B2 F! A2 F! C1 F! B1 F! A1 F!             \ A1 B1 C1 A2 B2 C2 ->

A1 F@ B2 F@ F* A2 F@ B1 F@ F* F-             \ -> A1*B2-A2*B1=D

FDUP .” D=“ FDUP F. CR                         \ D -> D D

C1 F@ B2 F@ F* C2 F@ B1 F@ F* F- FSWAP F/       \ D D -> D (C1*B2-C2*B1)/D=X

A1 F@ C2 F@ F* A2 F@ C1 F@ F* F- FROT F/       \ D X -> X (A1*C2-A2*C1)/D=Y

;

Здесь мы создали 6 новых вещественных переменных, чтобы не путаться в коэффициентах.

Первая строка – стандартное описание логики работы слова.

Вторая – сохранение значений на стеке в соответствующие переменные. На вершине находится значение последней введённой, а значит его первым и сохраняем.

Вычисление D.

Дублируем D, так как он нам понадобится два раза, также мы его копируем и печатаем для проверки промежуточных вычислений. После отладки код «.” D=“ FDUP F. CR» можно удалять.

Вычисление X

Вычисление Y

Проверим корректность работы слова на следующих данных

3x+5y=7      (A1=3      B1=5      C1=7)

6x+y=4            (A2=6      B2=1      C2=4)

3E 5E 7E 6E 1E 4E B40 F. F.

D= -27.000000

1.1111111 0.4814814 Ok

D=3*1-6*5=3-30=-27, совпадает с результатом выданном словом. X=(7*1-4*5)/(-27)=13/27=(0,481). Y=(3*4-6*7)/(-27)=(12-42)/(-27)=30/27=1,(1). Видим, что слово работает корректно.

Integer 1-30

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

Пример 1. Перевести см в м без округления. Здесь вы увидите всю красоту Форта.

Rm=Rcm/100 – целочисленно

: I1 ( Rcm -> Rm )

100 /

;

503 I1

Ok ( 5 )

Что означает в 503 см содержится 5 полных метров.

Пример 2. Отличается от предыдущего заменой 100 на 1000.

Mt= Mkg/1000

: I2 ( Mkg -> Mt )

1000 /

;

12683 I2

Ok ( 12 )

В 12683 кг 12 полных тонн.

Пример 3. Так же отличается от предыдущего константой. Теперь 1000 меняем на 1024.

DkB= DB/1024

: I3 ( DB -> DkB )

1024 /

;

2050 I3

Ok ( 2 )

2050 B – это 2 полных kB.

Пример 4. Так же простейшая задачка на целочисленное деление.

Для определенности A>B, результат равен A/B.

: I4 ( A B -> A/B )

/

;

15 4 I4

Ok ( 3 )

В отрезке длиной 15 размещается 3 целых отрезка длиной 4.

Пример 5. Отличается от предыдущего примера заменой целочисленного деления на взятие остатка от деления.

: I5 ( A B -> остаток{A/B} )

MOD

;

15 4 I5

Ok ( 3 )

15/4 – остаток равен 3 – все верно, результат теста корректный.

Примеры 1-5 настолько просты, что даже нет необходимости создавать соответствующие слова. Можно просто ввести число-операнд затем тело слова. Результат получите в скобках на стеке. Чтобы распечатать его и не засорять стек нажмите «.» и «Enter». Перепишем эти примеры для наглядности.

«503 100 / .»

«12683 1000 / .»

«2050 1024 / .»

«15 4 / .»

«15 4 MOD .»

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

Пример 6. Вывести число десятков и единиц двузначного числа. Для этого используем операцию /MOD, которая одновременно вычисляет и целую часть, и остаток от деления.

: I6 ( AB -> A B )

10 /MOD SWAP

;

45 I6

Ok ( 4 5 )

Чтобы вывести в таком же порядке как на стеке слегка изменим код (помним, что сначала напечатается вершина стека, поэтому перед выводом используем команду SWAP)

45 I6 SWAP . .

4 5 Ok

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

: I6. I6 SWAP . . ;

Теперь окончательно задача будет решаться просто вызовом одного слова с одним аргументом:

45 I6.

4 5 Ok

Результат тот же, но решение выглядит более профессионально и красиво.

Разумеется, эти два слова можно объединить в одно по схеме:

: I6. 10 /MOD SWAP SWAP . . ;

: I6. 10 /MOD . . ;

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

Пример 7. Вычислить сумму и произведение цифр двузначного числа. Дальнейшее развитие предыдущего примера.

: I7 ( AB -> A+B A*B )

10 /MOD      \ AB -> A B

2DUP +            \ A B -> A B A+B

ROT ROT *      \ A B A+B -> A+B A*B

;

45 I7

Ok ( 9 20 )

Сумма цифр числа 45 равна 4+5=9, а произведение 4*5=20, тест корректен.

Если вас не удовлетворяет результат на стеке, то можете аналогично предыдущему примеру написать слово для вывода на экран.

Пример 8. Перестановка местами цифр в двузначном числе.

: I8 ( AB -> BA )

10 /MOD SWAP 10 * +

;

45 I8

Ok ( 54 )

Разделяем десятки и единицы с помощью «/MOD», затем собираем, умножив единицы на десять, таким образом, сделав их десятками, и прибавляем число десятков в исходном числе, как единицы в выходном результате.

Пример 9. Вывести число сотен в трехзначном числе.

: I9 ( ABC -> A )

100 /

;

578 I9

Ok ( 5 )

Все верно. Число сотен в числе 578 равно 5.

Пример 10. Аналогичен предыдущему. Выводим сначала число единиц затем десятков.

: I9 ( ABC ->C B )

10 /MOD 10 MOD

;

123 I9

Ok ( 3 2 )

Пример 11. Вычислить сумму и произведение цифр трехзначного числа.

: I11 ( ABC -> A+B+C A*B*C )

10 /MOD 10 /MOD      \ ABC -> C B A

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

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

;

456 I11

Ok ( 15 120 )

Сумма 4+5+6=15, а произведение 4*5*6=120.

Пример 12. Перевернуть трехзначное число справа на лево.

: I12 ( ABC -> CBA )

10 /MOD 10 /MOD      \ ABC -> C B A

SWAP 10 * +            \ C B A -> C BA

SWAP 100 * +            \ C BA -> CBA

;

123 I12

Ok ( 321 )

Вот так просто Форт переворачивает трехзначное число «задом наперед».

Пример 13. В трехзначном числе сотни перенести в крайнее правое положение, вместо единиц.

: I13 ( ABC -> BCA )

100 /MOD      \ ABC -> BC A

SWAP 10 * +      \ BC A -> BCA

;

123 I13

Ok ( 231 )

Пример 14. Аналогичен предыдущему. Довольно несложная задачка.

: I14 ( ABC -> CAB )

10 /MOD      \ ABC -> C AB

SWAP 100 * +      \ C AB -> CAB

;

123 I14

Ok ( 312 )

Пример 15. В трехзначном числе переставить цифры сотен и десятков местами.

: I15 ( ABC -> BAC )

10 /MOD 10 /MOD      \ ABC -> C B A

10 * SWAP 100 * + +      \ C B A -> BAC

;

123 I15

Ok ( 213 )

Как и прежде, сначала разбираем число на цифры, затем собираем, в требуемом для условии задачи порядке.

Пример 16. Поменять местами десятки и единицы в трехзначном числе.

: I16 ( ABC -> ACB )

10 /MOD 10 /MOD      \ ABC -> C B A

100 * + SWAP 10 * +      \ C B A -> ACB

;

123 I16

Ok ( 132 )

Пример 17. Довольно тривиальная задачка. В числе, большем 999, определить число сотен. Сначала отсекаем часть числа до сотен, поделив на 1000 и взяв только остаток. Затем поделив, полученное число на 100, и взяв целую часть, узнаем количество сотен.

: I17 ( A -> X )      \ X – число сотен

1000 MOD 100 /

;

123456 I17

Ok ( 4 )

Пример 18. Абсолютно идентична предыдущему примеру и также примитивна. В аналогичном числе найти число тысяч (1000 заменяется на 10000, а 100 на 1000).

: I18 ( A -> X )      \ X – число тысяч

10000 MOD 1000 /

;

123456 I18

Ok ( 3 )

Пример 19. Дано S секунд перевести в количество полных минут M.

: I19 ( S -> M )      \ M – результат в минутах

60 /

;

179 I19

Ok ( 2 )

Пример 20. Аналогична предыдущему. Переводим секунды S в число полных часов H.

: I20 ( S -> H )      \ H – результат в часах

3600 /

;

7201 I20

Ok ( 2 )

Конец ознакомительного фрагмента.

Текст предоставлен ООО «Литрес».

Прочитайте эту книгу целиком, купив полную легальную версию на Литрес.

Безопасно оплатить книгу можно банковской картой Visa, MasterCard, Maestro, со счета мобильного телефона, с платежного терминала, в салоне МТС или Связной, через PayPal, WebMoney, Яндекс.Деньги, QIWI Кошелек, бонусными картами или другим удобным Вам способом.