Начальный курс программирования на языке Форт

         

РЕКОМЕНДАЦИИ ПО ПРИМЕНЕНИЮ ОПЕРАТОРА DO В СТИЛЕ ФОРТА


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

Рассмотрим, каким образом «оперившийся» программист может заняться отладкой определения СЛОЖНЫЕ-ПРОЦЕНТЫ (из первого раздела настоящей главы). Прежде чем добавить сообщения ." , программист может кратко записать такой вариант на листе бумаги: : СЛОЖНЫЕ-ПРОЦЕНТЫ ( вклад процент — ) SWAP 21 1 DO CR I . 2DUP R% + DUP . LOOP 2DROP ;

Он может вести отладку этого варианта за терминалом, используя . или .S для проверки результата на каждом шаге цикла. Такой «диалог» выглядит следующим образом:

При моделировании мы не используем фразы "граница счетчик DO", "I" и "DUP".1000 6 SWAP .S<return>

6 1000 ok 2DUP .S<return> Шаг 1

6 1000 6 1000 ok R% .S<return>

6 1000 60 ok

+ .S<return>

6 1060 ok 2DUP R% + .S<return>

6 1124 ok 2DROP .S<return> Шаг 2

Стек ПУСТ ok

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

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

: ПЯТЕРКИ ( -- ) 100 0 DO I 5 . LOOP ;

вместо

: ПЯТЕРКИ ( — ) 100 0 DO I 5 * . LOOP ;

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

ASDFKJ

что, естественно, не является словом Форта и приведет к тому, что текстовый интерпретатор вызовет ABORT, который, помимо всего прочего, почистит оба стека. (Но объясните новичку, что не нужно бить по клавишам при каждой ошибке.)





РЕКУРСИЯ


Рекурсией называется обращение процедуры к самой себе. Обычными средствами в Форте запрещается это делать. Например, при вводе : LOAD ( n -- ) DUP . LOAD ;

вы определяете новую версию слова LOAD, которое выдает номер загружаемого блока, после чего обращается к исходному варианту LOAD. Чтобы выражения такого типа были возможны, Форт-система во время компиляции определения умышленно «прячет» имя данного определения, и тем самым в новое определение компилируется адрес исходного варианта.

Тем не менее на Форте реализовать рекурсию довольно просто (нехитрые средства защиты Форта легко обмануть). Слово RECURSE осуществляет компиляцию адреса текущего определяемого слова. (В некоторых системах это слово известно как MYSELF.)1 Приведем пример рекурсии: VARIABLE СЧЕТЧИК : ПОЧЕМУ CR ." Почему вы спрашиваете? " -1 СЧЕТЧИК +! СЧЕТЧИК @ IF RECURSE THEN ; 5 СЧЕТЧИК !

Выполнив слово ПОЧЕМУ, вы получите: ПОЧЕМУ

Почему вы спрашиваете? Почему вы спрашиваете? Почему вы спрашиваете? Почему вы спрашиваете? Почему вы спрашиваете? ok

1 Для пользователей систем фиг-Форта. Определение следующее:

: RECURSE LATEST PFA CFA , ; IMMEDIATE

Для более ранних систем полифорта:

: RECURSE LAST @ @ 2+ , ; IMMEDIATE

Обратите внимание на то, что в рассматриваемом примере нет ни одного цикла. Начиная со значения 5 счетчика, слово ПОЧЕМУ будет обращаться к самому себе до тех пор, пока значение счетчика не станет равным нулю. (Не выполняйте слово ПОЧЕМУ при нулевом или слишком большом значении счетчика, поскольку ваш стек возвратов при этом переполнится.)



РЕШЕНИЕ ЗАДАЧ (УПРАЖНЕНИЕ 2-Б)


Преобразуйте следующие выражения из инфиксной формы в форму определений Форта и укажите порядок аргументов в стеке для этих определений. Порядок аргументов в стеке может быть произвольным, но он должен быть наиболее удобным для данного определения, В соответствии с номером упражнения 2-Б вы можете именовать ваши определения как 2Б1 и 2Б2 и т. д. Например:1. ab + c примет вид: 2Б1 * + ; a - 4b 2. ------ + c 6 a 3. ----- 8b 0.5ab 4. ------- 100 5. a(2a + 3) a - b 6. ----- c

Ответы к упражнению 2-Б

6. Если вы скажете, что такое выражение преобразовать нельзя, то будете правы, по крайней мере сейчас, пока мы еще не рассмотрели специальных стековых операций. 2. : 2Б2 ( c a b -- x ) 4 * - 6 / + ;

3. : 2БЗ ( a b -- x ) 8 * / ;

4. : 2Б4 ( a b -- x ) * 200 / ;

5. : 2Б5 ( a a -- x ) 2 * 3 + * ;



РЕЖИМ КАЛЬКУЛЯТОРА


Ниже приводятся четыре простейшие операции над целочисленными значениями, записанные на языке Форт1: + плюс ( n1 n2 -- сумма ) сложение ( n1 + n2 ) - минус ( n1 n2 -- разность) вычитание ( n1 - n2 ) * звездочка ( n1 n2 -- произведение) умножение ( n1 * n2 ) / слэш ( n1 n2 -- частное) деление ( n1 / n2 )

В отличие от калькулятора на терминале компьютера не предусмотрены специальные клавиши для выполнения операций умножения и деления. Вместо них мы пользуемся клавишами * и /.

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

1 Для нематематиков. Хотя данная глава и напоминает .немного учебник по алгебре, решение математических задач— всего лишь небольшая часть из того, что вы сможете делать с помощью Форта. Позднее вы познакомитесь с другими применениями Форта. Здесь же уместно напомнить, что целые числа — это такие круглые числа, как ... — 3, —2, —1, 0, 1, 2, 3, ,.., а целочисленная арифметика (что достаточно логично) — операции над целыми числами.17 5 + . 22 ok

Вы можете выполнить таким образом все арифметические операции даже без составления «программы», используя Форт-систему как калькулятор. Решите задачу на умножение: 7 8 * . 56 ok

Как видите, знак операции следует за значениями. Если же вы производите вычитание и деление, необходимо учитывать порядок следования значений («7 — 4» не эквивалентно «4 — 7»).

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

Поэтому чтобы выполнить вычитание

7 - 4 =

наберите на клавиатуре 7 4 - . 3 ok

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


10.00 2.25 +

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

21 4 / . 5 ok , а не 5.25 ok

Во-вторых, если вы попытаетесь выполнить умножение: 

10000 10 *

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

 


Напомним, что, рассматривая перечень слов Фopтa, мы употребляли букву n, чтобы обозначить место, где должно находиться число. Так как в Форте числа одинарной длины используются гораздо чаще чисел других типов, вместо п следует подставлять число одинарной длины. Конечно, существуют операции, которые выполняются и над значениями из расширенного диапазона (двойной длины). Они обозначаются буквой d.

Все эти непонятные пока проблемы будут объяснены в свое время, так что не снижайте внимания.

Порядок чисел остается тем же. В качестве примера решите задачу на деление: 20 4 /

Слово / определено таким образом, что нижнее число в стеке делится на число, находящееся в его вершине. 



Как поступить, если необходимо выполнить несколько операций? Например: 4 + (17 * 12)

Как известно, сначала нужно выполнить операцию, указанную в скобках, т. е. 17 умножить на 12, а затем добавить четыре. На Форте это будет выглядеть так:17 12 * 4 + . 208 ok



Числа 17 и 12 помещаются в стек. Слово * перемножает их и возвращает результат в стек.

Далее число 4 помещается в стек над числом 204. Слово + «выкатывает» суммирующую машину и складывает эти два числа, а в стек возвращается только результат.

 


Предположим, вы хотите сложить пять чисел. Вы можете это сделать на Форте, скажем, так: 17 20 + 132 + 3 + 9 + . 181 ok

 


Еще одна интересная задача: (3+9) * (4+6)

Чтобы ее решить, мы должны сначала сложить числа 3 и 9, затем 4 и 6 и, наконец, перемножить полученные две суммы.На Форте это можно записать следующим образом: 3 9 + 4 6 + *

В результате вы получите.

 


Заметьте, что мы весьма кстати сохранили сумму, равную 12, в стеке на то время, пока складывали числа 4 и 6.

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


РЕЖИМ ОПРЕДЕЛЕНИЙ


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

Допустим, вы хотите перевести различные единицы измерения в дюймы. Вам известно, что 1 ярд = 36 дюймам и 1 фут = 12 дюймам1. Поэтому вы можете определить эти два слова следующим образом: : ЯРД>ДМ ( ярды -- дюймы ) 36 * ; ок

: фУТ>ДМ ( футы -- дюймы ) 12 * ; ок

Здесь определяемые имена означают «ярды-в-дюймы» и «футы-в-дюймы» соответственно. Ниже приводятся примеры выполнения указанных слов: 10 ЯРД>ДМ . 360 ок 2 ФУТ>ДМ . 24 ок

1 дюйм = 2,54 см, 1 фут = 30,48 см, 1 ярд = 0,914 м. — Примеч. пер.

Если результат должен всегда выражаться в дюймах, то необходимо ввести следующие определения: : ЯРДОВ ( ярды -- дюймы ) 36 * ; ок : ФУТОВ ( футы -- дюймы ) 12 * ; ок : ДЮЙМОВ ( -- ) ; _ok

Таким образом, можно записать выражение: 10 ЯРДОВ 5 ФУТОВ + 9 ДЮЙМОВ + . 429 ок

Отметим, что назначение слова ДЮЙМОВ — напомнит вам, для чего здесь поставлено число 9. Если вы хотите писать грамотно, то должны добавить три определения: : ЯРД ЯРДОВ ; ок : ФУТ ФУТОВ ; ок : ДЮЙМ ; ок

Набирая при вводе эти существительные в единственном числе, вы получите такой же результат: 1 ЯРД 5 ФУТОВ + 1 ДЮЙМ + . 97 ок 6 ЯРДОВ 1 ФУТ + . 228 ok

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

Допустим, вы хотите получить слово, которое суммирует пять чисел, находящихся в стеке. Ранее мы уже суммировали пять чисел следующим образом: 17 20 + 132 + 3 + 9 + . 181 ok

однако можно набрать на клавиатуре и такую строку: 17 20 132 3 9 + + + + . 181 ok

Мы получили тот же ответ, несмотря на то что собрали все числа в одну группу, а все знаки операций — в другую. Запишем наше определение так:: 5#СУММА ( n1 n2 n3 n4 n5 — сумма ) + + + + ; ок


и выполним слово, как показано ниже: 17 20 132 3 9 5#СУММА . 181 ок

Ниже приводится еще одно выражение, для которого нужно записать определение1: (а + b) * с

1Для начинающих, которым нравятся задачи, сформулированные словесно. Реактивный самолет летит со средней скоростью 600 миль/ч. Скорость попутного ветра составляет 25 миль/ч. Сколько миль пролетит этот самолет при таких условиях за пять часов? Если мы определим слово

: РАССТОЯНИЕ ( время скорость попутный—ветер — расстояние ) + * ;

то можно затем ввести следующее:5 600 25 РАССТОЯНИЕ . 3125 ок

Попытайтесь выполнить это слово с различными значениями, учитывая и встречный ветер (отрицательные значения).

Как вы видели из упр. 2-А, это выражение может быть записано в постфиксной форме: с а b + *

Итак, мы можем записать наше определение: : РЕШЕНИЕ ( с a b — п ) + * ; ок


ROT (ПЕРЕМЕЩАТЬ ПО КРУГУ)


Это четвертая операция в нашем списке (ROT — сокращение от rotate). Посмотрите, что происходит при ее выполнении с тремя верхними элементами стека:

Например, если вам нужно вычислить выражение ab — bс, то сначала необходимо вынести b за скобки: b(а - с)

а затем, если начальное состояние стека таково: ( с b a — )

можно написать ROT - *

При этом выполняются следующие действия:

ОПЕРАЦИЯ СОДЕРЖИМОЕ СТЕКА
c b a
ROT b a c
- b (a - c)
* (b*(a-c))



«СЕКРЕТ» ОПЕРАТОРА IF


Мы уже знаем, что IF принимает -1 за истину. Программирующие на Форте часто пользуются тем, что на самом деле IF воспринимает как истину любое ненулевое значение и только нуль кал ложь . Обычно вы над этим не задумываетесь, но бывают ситуации, когда такая реализация представляет интерес.

Так, если вы проверяете некоторое число только на равенство нулю, то операция сравнения вам не нужна. Определение рассмотренного нами слова /ПРОВЕРКА может поэтому принять более прослою форму: : /ПРОВЕРКА ( числитель знаменатель -- результат) DUP IF / ELSE ." Знаменатель нуль " DROP THEN ;

Или, допустим, вам нужно узнать, является ли некоторое число в точности кратным десяти, например 10, 20, 30, 40 и т, д. Как известно, выражение 10 MOD

осуществляет деление на 10 и возвращает только остаток. А при вычислении кратного в остатке должен быть нуль, поэтому выражение 10 MOD 0=

соответствует флагу «истина» или «ложь».

1 Для сомневающихся. Чтобы проверить это, введите следующий текст:

: ТЕСТ ( ? ) IF ." He нуль " ELSE ." Нуль " THEN ;

Даже несмотря на то, что в данном определении нет операции сравнения, вы получите такой результат:0 ТЕСТ Нудь ок

1 ТЕСТ Не нуль oк 8000 ТЕСТ Не нуль ok

Далее, слово — (минус) вы зачастую можете использовать в качестве операции сравнения, чтобы проверить на равенство два значения. Если вы выполняете операцию вычитания над двумя одинаковыми числами, то получаете нуль (ложь), но если при вычитании числа не равны, то вы получаете не нулевое значение (истину). В некоторых Форт-системах по этой причине отсутствует слово <>. Тем не менее <> не только более удобно, чем «не равно», но и оставляет на вершине стека в качестве истинного значения отрицательную единицу. Как вы увидите в гл. 7, операция сравнения имеет свои достоинства (в сравнении с арифметическим вычитанием).



СИСТЕМЫ СЧИСЛЕНИЯ


После загрузки Форт-системы все преобразования чисел как для ввода, так и для вывода осуществляются в десятичной системе счисления.

Применив перечисленные ниже команды, вы можете сменить текущую систему счисления: HEX ( -- ) - устанавливает шестнадцатиричную систему счисления; OCTAL ( -- ) - устанавливает восьмиричную систему счисления (применяется в некоторых системах); DECIMAL ( -- ) - возвращает десятичную систему.

Вновь принятая система счисления остается таковой до следующего изменения, так что не забудьте объявить DECIMAL, как только закончите работать с другой системой счисления.

Рассмотренные команды упрощают преобразования чисел при работе в режиме калькулятора. Если требуется, к примеру, перевести число 100 в шестнадцатиричную систему, вы должны ввести следующее:DECIMAL 100 HEX . 64 ok

Для того чтобы перевести шестнадцатиричное число F в десятичную систему (помните, что вы уже имеете дело с шестнадцатиричной системой), нужно ввести: 0F DECIMAL . 15 ok

Возьмите себе в привычку с этого момента предварять каждое шестнадцатиричное значение нулем, например:0А 0B 0FF

что позволит отличать их от системных слов, таких, как В в словаре РЕДАКТОРА (EDITOR).

Полезный прием. Определение двоичной (BINARY) или любой другой системы счисления. Начинающие, которые хотят посмотреть, как выглядят числа в двоичной системе, могут ввести следующее определение:

: BINARY 2 BASE ! ;

Новое слово BINARY выполняется так же, как и OCTAL или HEX, но изменяет текущую систему счисления на двоичную. В тех системах, где нет слова OCTAL, в порядке эксперимента можно определить:

: OCTAL 8 BASE ! ;



СЛОВАРЬ


Каждое слово (его имя и определение) заносится в так называемый словарь Форта. Этот словарь, когда вы начинаете писать на Форте, уже содержит какое-то количество слов, но вы можете помещать в него и «свои» слова. При определении нового слова оно переводится в словарную форму и добавляется в словарь. Такой процесс называется компиляцией.

К примеру, если вы вводите строку : STAR 42 ЕМIТ ;<return>

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

(Не хотите ли вы уже сейчас посмотреть список слов, находящихся в словаре? В большинстве систем для этого нужно ввести слово WORDS, и тогда вы получите на экране имена слов вместе с их «адресами» (ссылками на участки памяти). Слова перечислятся в том порядке, в котором их определили — последнее определенное слово будет верхним. В некоторых, более старых системах для таких целей существует слово VLIST.)

Теперь слово STAR находится в словаре. А как выполняется слово, находящееся в словаре? Наберите непосредственно (не внутри определения) следующую строку: STAR 30 SPACES<return>

Эта строка активирует слово с именем INTERPRET, означающее «текстовый интерпретатор».

Текстовый интерпретатор просматривает входной поток символов в поисках строки символов, внутри которой нет пробелов. Если он находит такую строку, то он ищет ее в словаре.

Найдя нужное слово в словаре, интерпретатор передает его определение слову с именем EXECUTE1, которое выполняет это определение (в нашем случае оно инициирует печать звездочки). Интерпретатор вновь выдает вам ok.

1 Слово EXECUTE (ВЫПОЛНИТЬ) в одном из вариантов переводится как «казнить». Отсюда ассоциации с палачом. — Примеч. пер.

Если интерпретатор не находит такой строки в словаре, он обращается к обработчику чисел (NUMBER). Последний проверяет, не является ли переданная ему информация числом, и если это действительно так, помещает его в участок памяти, отведенный для чисел.


Что произойдет, если вы попытаетесь выполнить слово, которого нет в словаре? Введите следующую фразу: XLERB<return> XLERB ?

Не найдя слово XLERB в словаре, текстовый интерпретатор попытается передать его NUMBER, который не признает его, после чего интерпретатор вернет вам это слово со знаком вопроса.


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

Упоминали ли мы о том, что символ «:» является словом? Так, если вы вводите этот символ: : STAR 42 EMIT ;<return>

происходит следующее:

 


текстовый интерпретатор находит двоеточие во входном потоке и передает его EXECUTE.




EXECUTE предлагает начать компиляцию. Компилятор переводит определение в словарную форму и записывает его в словарь.




Дойдя до точки с запятой, компилятор останавливается и снова начинает выполняться текстовый интерпретатор, который выдает на экран ok.


СМЕШАННЫЕ МАТЕМАТИЧЕСКИЕ ОПЕРАЦИИ


Приведем еще четыре математические операции. Как и в случае сокращенных операций, их функции должны быть вам понятны:ABS ( n -- |n| ) Помещение в стек абсолютной величины заданного числа. NEGATE ( n -- -n ) Изменение знака на противоположный. MIN ( n1 n2 -- min) Помещение в стек минимального из двух заданных чисел. МАХ ( n1 n2 -- max) Помещение в стек максимального из двух заданных чисел.

Рассмотрим две простые задачи на использование слов ABS и MIN.

ABS

Напишите определение для вычисления разности между двумя числами (независимо от порядка их следования): : РАЗНОСТЬ ( n1 n2 -- разность ) - ABS ;

Результаты получаются одинаковыми при любом порядке ввода: 52 37 РАЗНОСТЬ . 15 ok или 37 52 РАЗНОСТЬ . 15 ok

MIN

Напишите определение для вычисления суммы комиссионного сбора, которую получат продавцы мебели, если им обещано 50 дол. или 1/10 часть от продажной цены при каждой сделке (в зависимости от того, какая из сумм окажется меньшей): : КОМИССИОННЫЕ ( цена - комиссионные ) 10 / 50 MIN ;

При трех различных значениях получается следующее. 600 КОМИССИОННЫЕ . 50 ok 450 КОМИССИОННЫЕ . 45 ok 50 КОМИССИОННЫЕ . 5 ok



СОКРАЩЕННЫЕ ОПЕРАЦИИ


Рассмотрим сначала наиболее простые операции. Ниже приводятся слова, смысл которых вам очевиден1:

1+ ( n -- n+1 ) Добавление единицы. 1- ( n -- n-1 ) Вычитание единицы. 2+ ( n -- n+2 ) Добавление двойки. 2- ( n -- n-2 ) Вычитание двойки. 2* ( n -- n*2 ) Умножение на два (арифметический сдвиг влево). 2/ ( n -- n/2 ) Деление на два (арифметический сдвиг вправо).

Существуют три причины, по которым желательно в вашем новом определении применять такие операции, как 1+ вместо 1 +. Во-первых, вы всякий раз экономите немного словарной памяти. Во-вторых, поскольку указанные слова определены в терминах машинного языка конкретного типа компьютера (чтобы учесть преимущества архитектуры данного компьютера), они выполняются быстрее, чем комбинация: 1+. Наконец, вы экономите какое-то время на компиляции.

1 Для начинающих. Понятие «арифметический сдвиг влево (вправо)» мы объясним позднее



СРАВНЕНИЕ СТРОК


Для сравнения строк предусмотрены следующие два слова Форта:

-TEXT

( al # a2 -- ?)

Сравнение двух строк длиной # , начинающийся с al и а2. Если сравнение успешное, на стек заносится ложь. Если нет, на стек заносится истина (положительное число, если двоичное представление строки1 > двоичного представления строки2, и отрицательное, если стр.1 < стр.2 ).

-MATCH

( d # s # -- а ?)

Поиск фрагмента длиной #, начинающегося с адреса s (источник) в области памяти длиной # , начинающейся с адреса d (получатель). Если поиск завершился успешно, на стек помещается начало искомого фрагмента a заданной области памяти и ложь. В противном случае неправильный адрес и истина.

С помощью слова -TEXT вы можете либо сравнить две строки, либо проверить порядок их расположения (по алфавиту)1. В гл. 12 приводится пример использования слова

-TEXT для выявления полного совпадения строк.

Так как для быстроты слово -TEXT осуществляет сравнение поячеечно, необходимо следить за тем, чтобы при наличии машин с ячеечной адресацией слову

-TEXT выдавались адреса, выравненные по границе ячейки. Например, если вы хотите сравнить вводимую строку со строкой, находящейся в каком-то массиве, перенесите вводимую строку в рабочую область (посредством -TEXT, а не WORD), поскольку адрес PAD лежит на границе ячейки. Аналогичным образом, когда вам нужно проверить некую строку, находящуюся в блочном буфере, убедитесь в том, что ее адрес выравнен по границе ячейки, или, если вы не можете этого сделать, перед выполнением проверки перешлите строку по выравненному адресу (используя CMOVE).

Отметим, что дефис в слове -TEXT, как и символ «~» кода ASCII, означает логическое «нет». По этой причине указанный префикс удобно применять в именах слов, помещающих в стек флаги с противоположным логическим значением (т.е. нуль представляет истину, а ненулевое значение - ложь).

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

-TEXT обычно пишется в машинных кодах.: -TEXT ( a1 # а2 -- f=сравнение | полож=1>2 | отр=1<2 ) 2DUP + SWAP DО DROP 2+ DUP 2- @ I @ - DUP IF DUP ABS / LEAVE THEN 2 +LOOP SWAP DROP ;

1 Для пользующихся процессорами INTEL, DEC и Zilog. Для того чтобы выполнить такую проверку, вы должны расположить байты в обратном порядке

Слово -MATCH применяется в командах редактирования, таких, как F и S, по которым должен осуществляться поиск некоторого фрагмента в памяти, содержащей данный фрагмент. Как и в случае с

-ТEХТ, желательно, чтобы слово -MATCH было написано в машинных, кодах. Если этого сделать не удается, можете воспользоваться следующим определением высокого уровня, которое вам подойдет (для .надежности и переносимости описанные далее слова не используют такие приемы, как принудительный выход из циклов посредством EXIT, что ускорило бы их выполнение):VARIABLE 'ИСТОЧНИК ( адрес исходного фрагмента) VARIABLE ИСТОЧНИК# ( длина исходного фрагмента) VARIABLE ФЛАГ ( t=сравнвмия-не-прмоошло) : -MATCH ( d # s # -- a t=cpавнение-не-произошло) SWAP 'ИСТОЧНИК ! DUP ИСТОЧНИК# ! - DUP 0< NOT IF 1+ 0 DO 0 ФЛАГ ! 'ИСТОЧНИК @ ИСТОЧНИК# @ 0 DO OVER I + С@ OVER I С@ - IF -1 ФЛАГ ! LEAVE THEN LOOP DROP ФЛАГ @ 0= IF LEAVE THEN 1+ LOOP ФЛАГ @ THEN ;


СТЕК — РАБОЧАЯ ОБЛАСТЬ ОПЕРАТИВНОЙ ПАМЯТИ ДЛЯ ВЫПОЛНЕНИЯ АРИФМЕТИЧЕСКИХ ДЕЙСТВИЙ


Компьютер вряд ли удовлетворит ваши запросы, если не сможет выполнять арифметические действия. Если вы никогда ранее не имели дело с компьютером, то вам может показаться странным, что он (или даже карманный калькулятор) вообще производит какие-то вычисления. Мы не можем в рамках данной книги

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

В принципе компьютеры при выполнении операций разбивают их на очень мелкие этапы и реализует предельно элементарные действия. Для нас с вами операция сложения «3 + 4» не составляет труда и вы, не задумываясь, называете результат: «7>, для компьютера же это очень длинная цепочка действий. Представьте себе, что у вас есть карманный калькулятор и вы хотите выполнить на нем такое сложение. Чтобы получить результат, требуется нажать клавиши в указанном ниже порядке.

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

 

 

 

Многие калькуляторы и компьютеры решают арифметические задачи аналогичным способом. Вы можете этого и не знать, но компьютер действительно запоминает числа в различных участках своей оперативной памяти и затем выполняет над ними необходимые операции. Центральный участок памяти, где временно запоминаются числа, над которыми затем будет выполнена операция, называется стеком. Числа «вталкиваются в стек» и впоследствии операции выполняются именно над данными числами1. Поясним это на примере. Введите со своего терминала строку3 4 + .<return> 7 ok

 

Вспомните, что при вводе числа интерпретатор текста переправляет его обработчику чисел NUMBER, который помещает это число в некоторый участок памяти. Таким участком является стек.

Короче говоря, когда вы вводите число 3 с терминала, вы «вталкиваете» его в стек.

 

Далее в вершину стека помещается число 4, «проталкивая» тройку внутрь.

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

 

Следующее слово из входного потока должно быть найдено в словаре. Знак + был предварительно определен как последовательность таких действий: «взять два верхних числа из стека, просуммировать их и поместить результат обратно в 

Слово . также находится в словаре. Оно входит Форт-системы и определено таким образом: «взять и вывести его на терминал».



СТЕК ВОЗВРАТОВ


Ранее мы рассмотрели несколько операций со стеком. Однако существует еще целый ряд операций, с которыми, вам предстоит познакомиться. До сих пор речь шла лишь о конкретном стеке так, как если бы он был один. Но на самом деле их два: стек данных и стек возвратов. Стек данных используется при программировании на Форте чаще, поэтому в тех случаях, когда это не вызывает недоразумений, он называется просто стеком.

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

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

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

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

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

>R ( n -- ) Выборка значения из стека данных и занесение его в стек возвратов.

R> ( -- n ) Выборка значения из стека возвратов и занесение его в стек данных.

R@ ( -- n ) Копирование содержимого вершины стека возвратов без изменения его значения.

Слова >R и R> перемещают элемент стека данных в стек возвратов и элемент стека возвратов в стек данных соответственно. Показанные выше забавные рисунки иллюстрируют операции со стеком в состоянии (2 3 1 -- 3 2 1),


заданные выражением>R SWAP R>

Каждое слово >R и соответствующее ему слово R> должны выполняться совместно в одном и том же определении или, если они выполняются в диалоговом режиме, в одной и той же вводимой строке (прежде чем вы нажмете клавишу RETURN).

Слово R@ только копирует значение из стека возвратов, но не удаляет его. Так что, введя выражение >R SWAP R@

вы получите ожидаемый результат, но если при этом не уберете «мусор» до следующего двоеточия (или до того, как нажмете клавишу возврата каретки), то выведите систему из строя.

Поясним изложенное на примере. Допустим, вам настолько не повезло, что приходится вычислять значение полинома ах2 + bx+с, причем задаваемые величины хранятся в стеке в следующем порядке: ( а b с х -- )

(Напоминаем, что операция возведения в степень должна выполняться первой.) ОПЕРАЦИЯ СТЕК ДАННЫХ СТЕК ВОЗВРАТОВ

а b с х >R a b с х SWAP ROT с b а х R@ с b а х х * с b ах х + с (ax+b) х R> * с x(ax+b) + x(ax+b)+c

Попытаемся вычислить его. Загрузите следующее определение: : ПОЛИНОМ ( a b c x -- n) >R SWAP ROT R@ * + R> * + ;

Теперь проверим его: 2 7 9 3 ПОЛИНОМ 48 ok


СТЕКОВАЯ НОТАЦИЯ


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

Применительно к нашему простому примеру ПЛЮС-ЧЕТЫРЕ изложенное выше означает, что перед выполнением указанного слова в стеке должен находиться один аргумент, а после его вы-

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

полнения оставаться результат в виде одного значения. Если вы поместите внутри определения ПЛЮС-ЧЕТЫРЕ «точку» для вывода получаемого результата на дисплей, то изменится стековый эффект: данное слово не должно будет возвращать в стек значение, полученное в результате своего выполнения.

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

Комментарий — это некоторая информация, предназначенная только для человека: она ни выполняется, ни компилируется. В Форте слово ( , левая круглая скобка, символизирует начало комментария. Текстовому интерпретатору предписывается осуществлять пропуск последующего текста до тех пор, пока не встретится правая круглая скобка, означающая конец комментария.
Поскольку ( является словом, вы обязаны отделять этот символ пробелом так же, как и в случае применения слова ."1. 



Вы можете использовать обычный комментарий внутри некоторого определения следующим образом: : НИЧЕГО ( это слово ничего не выполняет ) ;

Текст «это слово ничего не выполняет» является комментарием. Вернемся к стековой нотации. Основной формат комментария этого вида выглядит так: ( -- )

1 Для начинающих. Закрывающая круглая скобка не является словом. Это просто символ, который служит ограничителем для слова ( . (Вспомните, что для слова ." ограничителем является символ ")

Такой комментарий означает, что данное определение никакого эффекта на стек не оказывает. К словам подобного рода относится CR или специфицированное нами слово STAR. (Во время своего исполнения слово STAR помещает в стек число 42, но извлекает его из стека до завершения работы, так что стековых эффектов в рассматриваемом случае нет.) Принято отделять стековую нотацию от имени определения двумя пробелами: : STAR ( -- ) 42 EMIT ;

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

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

(Буква n заменяет число.) Если слово возвращает стеку аргументы, то они перечисляются справа от двойного дефиса. Стековая нотация для слова + имеет вид:

( n1 n2 — сумма )

Для обозначения аргументов вы можете использовать имена, сокращения или просто нумеровать их: nl, n2, nЗ и т. д., как было сделано в приведенном выше примере.

Когда вы указываете несколько аргументов справа или слева от дефиса, необходимо строго соблюдать порядок их размещения. Запомните следующее правило: крайний правый объект в стековой нотации является верхним элементом стека. 



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


Если вы вводите в стек для некоторого слова числа 1 2 3, то стековый комментарий будет таков: ( 1 2 3 -- )

т е. 1 окажется на дне, а 3 - в вершине стека.

Так как вы, очевидно, уже разобрались в правилах записи, в дальнейшем будет опускаться <return>, за исключением тех случаев, где это необходимо для ясности. Ответы компьютера в книге всегда подчеркнуты, поэтому вам должно быть понятно, когда следует нажимать клавишу RETURN.

Ниже приводится список слов Форта, которые вам уже знакомы, вместе с их стековой нотацией (n замещается числом, с — символом)

: ххх уyу ; ( — ) Определение нового слова с именем ххх, состоящее из слова или слов ууу. CR ( — ) Возврат каретки и перевод строки. SPACES ( n — ) Вывод заданного числа пробелов. SPACE ( — ) Вывод одного пробела. ЕMIТ ( с — ) Вывод символа. ." ххх" ( — ) Вывод строки символов ххх. Символ " является признаком конца строки. + ( nl n2 — сумма ) Суммирование.

. ( n — ) Вывод числа, за которым следует один пробел. ( xхх) ( — ) Комментарий, который текстовым интерпретатором не воспринимается. Символ ) является ограничителем.

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


СТРОКОВЫЕ ЛИТЕРАЛЫ


Текстовая строка, скомпилированная в словарь со средствами получения ее адреса, называется строковым литералом. Общим словом для создания строковых литералов является слово STRING (СТРОКА)1. Оно функционирует следующим образом:CREATE СООБЩЕНИЕ BL STRING ПРИВЕТ СООБЩЕНИЕ COUNT TYPE ПРИВЕТ ok

Слово STRING выбирает из стека в качестве аргумента символ в коде ASCII - ограничитель строки - и считывает до него фрагмент из входного потока, компилируя его в словарь как строку со счетчиком.

1 Для пользователей всех систем, кроме полифорта. Во многих системах (но не всюду) вы можете воспользоваться следующим определением:

: STRING ( с -- ) WORD C@ 1+ ALLOT ;

Мы полагаем, что в таких системах буфер слова WORD начинается с Н ERE, поэтому здесь не требуется пересылка строки и счетчика в словарь. Адрес, оставляемый в стеке словом WORD, является адресом со счетчиком, там что слово С@ помещает значение этого счетчика в вершину стека Затем слово ALLOT продвигает указатель словаря по длине строки (плюс один из-за счетчика), резервируя память для нее в словаре

Вы можете воспользоваться словом STRING для создания строковых массивов. Вспомните наше определение слова МАРКИРОВКА в программе сортировки яиц, где мы применяли вложенные конструкции IF THEN. На этот раз сделаем сообщения о категории яиц одинакового размера (по восемь букв) и соединим их в одну строку с помощью единственного строкового литерала:CREATE "МАРКИРОВКА" ASCII " STRING Брак Мелкие Средние Крупные Оч.крупнОшибка"

Обращаясь к слову МАРКИРОВКА, мы получаем адрес данной строки. Можно вывести любое сообщение о категории яиц, вычислив смещение внутри строки. Например, если нужно выдать сообщение о категории 2, то добавляем единицу (чтобы проскочить байт со счетчиком), затем прибавляем 16(2*8) и выводим восемь символов имени:"МАРКИРОВКА" 1+ 16 + 8 TYPE

Теперь переопределим слово МАРКИРОВКА так, чтобы оно выбирало из стека номер категории от нуля до пяти и использовало его как индекс в нашем строковом массиве:: МАРКИРОВКА ( категория# -- ) 8 * "МАРКИРОВКА" + 8 TYPE SPACE ;


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

Новый вариант слова МАРКИРОВКА выполняется немного быстрее, потому что ему не требуется производить ряд операций сравнения до тех пор, пока не будет найдена соответствующая категория. Адрес выводимого сообщения вычисляется по аргументу. Но если аргумент слова МАРКИРОВКА выходит за пределы диапазона от нуля до пяти, то выведется «мусор». В том случае когда слово МАРКИРОВКА используется только внутри слов РАЗМЕР-ЯИЦ, проблем еще нет, однако если вы собираетесь от дать это слово конечному пользователю (т. е. оператору), следует обеспечить контроль: : МАРКИРОВКА О MAX 5 MIN МАРКИРОВКА ;

Вы встретитесь со словом STRING снова при рассмотрении определения слова ." в гл. 11.

Во многих Форт-системах существует еще одно слово для создания строковых литералов. Это слово не имеет стандартного имени, поэтому присвоим ему имя LIT". Его можно употреблять только внутри определений. Оно подобно слову .", но вместо вывода строки оставляет в вершине стека адрес строки со счетчиком. По существу, выражение : 1TECT LIT" Что случилось?" COUNT TYPE ;

эквивалентно выражению : 2TECT ." Что случилось?" ;

Слово LIT" является более мощным, чем STRING (поэтому менее употребительным). Последнее может применяться для определения слова LIT".

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

Ниже приводится перечень слов Форта, рассмотренных в настоящей главе.
UPDATE

( -- )

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

SAVE-BUFFERS

( -- )

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

FLUSH

( -- )

Осуществляется SAVE-BUFFERS, затем происходит погашение признака обновления всех буферов. Используется при установке или смене накопителей внешней памяти.

EMPTY-BUFFERS

( -- )

Все блочные буфера отмечаются как пустые независимо от им содержания. Обновленные блоки во внешнюю память не записываются.

BLOCK

( u -- )

Занесение в стек адреса первого байта в блоке u. Если данного блока еще в памяти нет, то происходит его пересылка из внешней памяти в тот буфер, к которому

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

BUFFER

( u -- a)

Функции те же, что и у BLOCK, за исключением того, что сам блок из внешней памяти не считывается.

TYPE

( а количество - )

Происходит выдача заданного количества символов, начиная с заданного адреса, на текущее внешнее устройство

-TRAILING

( a u1 -- a u2)

Удаление незначащих пробелов из строки с заданным адресом путем уменьшения значения счетчика от u1 (счетчик исходных байтов) до u2 (счетчик байтов, полученных в результате вычеркивания пробелов).

>TYPE

( a # -- )

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

MOVE

( a1 a2 u -- )

По ячеечное копирование участка памяти длиной u байтов, начинающегося с адреса a1, в участок памяти, начинающийся с а2. Пересылка идет с al В сторону увеличения адресов.

CMOVE

( al a2 u -- )

Побайтное копирование участка памяти длиной u байтов, начинающегося с a1, в участок памяти, начинающийся с а2. Пересылка идет с а1 в сторону увеличения адресов.

СМОVE>

( a1 a2 u -- )

Копирование участка памяти длиной u байтов, начинающегося с адреса a1, в участок памяти, начинающийся с адреса а2. Копирование начинается с КОНЦА строки и продвигается в сторону уменьшения адресов.

BLANK

( a u -- )

Заполнение участка памяти длиной u байт символам пробела в коде ASCII.

KEY

( -- с)

Занесение на стек значения в коде ASCII очередного доступного символа на текущем устройстве ввода.

EXPECT

( а u --)

Ожидание и символов (или нажатий клавиши RETURN) с клавиатуры и запоминание их в участок памяти, начинающийся с адреса a и продолжавшийся сторону увеличения адресов. На нажатие клавиши ЗАБОЙ осуществляется возврат курсора.

SPAN

( -- a)

Содержится количество символов, полученных, словом EXPECT

WORD

( с -- а)

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

COUNT

( a -- a+1 #)

Преобразование адреса строки со счетчиком (длина которой находится в первом строки) в формат, соответствующий использования словом TYPE а именно: в стек заносится адрес начала текста строки и ее длина.

>IN

( -- a)

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

BLK

( -- а)

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

CONVERT

( ud1 a1 -- ud2 а2)

Начиная с адреса a1+1 (байт, содержащий длину, пропускается), CONVERT преобразует строку в двоичное значение, которое зависит от текущей системы счисления (значения BASE). Полученное значение накапливается в ud1, и остается на стеке как ud2. Процесс продолжается до тех пор, пока не встретится символ, который не может выть истолкован как цифра в текучей системе счисления. Адрес этого символа заносится на стек как а2.

NUMBER

( a -- d)

Преобразование текста, начинающегося с адреса а+1, в двоичное значение с учетом текущей системы счисления (значения BASE). Строка может предваряться знаком минус, что делает полученное значение отрицательным.

-TEXT

( al # a2 -- ?)

Сравнение двух строк длиной # , начинающийся с al и а2. Если сравнение успешное, на стек заносится ложь. Если нет, на стек заносится истина (положительное число, если двоичное представление строки1 > двоичного представления строки2, и отрицательное, если стр.1 < стр.2 ).

-MATCH

( d # s # -- а ?)

Поиск фрагмента длиной #, начинающегося с адреса s (источник) в области памяти длиной # , начинающейся с адреса d (получатель). Если поиск завершился успешно, на стек помещается начало искомого фрагмента a заданной области памяти и ложь. В противном случае неправильный адрес и истина.

STRING ( с - ) Компиляция строкового литерала, ограниченного символом с, в словарь как строки со счетчиком.
LIT" xxx"

период-выполнения: ( -- a)

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


СТРУКТУРА ОПРЕДЕЛЕНИЯ ЧЕРЕЗ ДВОЕТОЧИЕ


Если формат заголовка и поля указателя кода одинаков для всех типов определений в конкретной системе, то формат поля параметров меняется от типа к типу. Рассмотрим поле параметров определения через двоеточие.

Поле параметров определения через двоеточие содержит адреса уже определенных слов, составляющих данное определение1.

Ниже приводится элемент словаря для определения слова СНИМОК, которое выглядит следующим образом: : СНИМОК ЗАТВОР ОТКРЫТЬ ВРЕМЯ ВЫДЕРЖАТЬ ЗАТВОР ЗАКРЫТЬ ;

При выполнении слова СНИМОК определения, расположенные в последующих адресах, выполняются в порядке очереди. Механизм, который читает список адресов и выполняет определения, расположенные по каждому адресу, называется адресным интерпретатором. Слово ; в конце определения компилирует адрес слова с именем EXIT. Как видно из рисунка, адрес EXIT расположен в последней ячейке элемента словаря. Адресный интерпретатор выполнит слово EXIT тогда, когда он подойдет к его адресу, точно так же, как он выполняет остальные слова определения. EXIT завершает выполнение адресного интерпретатора, что будет показано в следующем разделе.

1 Для специалистов. Адреса, составляющие тело определения через двоеточие, как правило, указывают поле кода, а не поле параметров (т. е. cfa, а не pfa).



СТРУКТУРА СЛОВАРНОЙ СТАТЬИ


Все определения, независимо от того, были ли они специфицированы посредством :, VARIABLE, CREATE или любого другого определяющего слова, состоят из следующих полей1:

поля имени; поля связи;

поля кода;

поля параметров.

Точное представление перечисленных полей в памяти зависит от реализации. И хотя Стандарт-83 предписывает полю имени «естественную длину» (в словаре запоминаются все символы имени - максимально 31), в наших примерах будет использоваться вариант с тремя хранимыми символами имени, так как это легче объяснить.

Ниже показано на примере переменной ДАТА, как рассматриваемые поля располагаются в памяти. На приведенной ниже схеме каждая строка представляет ячейку словаря.

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

Поле имени. В нашем примере первый байт этого поля содержит число символов полного имени определяемого слова (в слове ДАТА четыре буквы). В следующих трех байтах хранятся представления первых трех букв имени определяемого слова в коде ASCII. В системах, где сохраняются только три символа имени, - это вся информация, которая используется апострофом или ['] для сравнения имени определения со словом из входного потока.

(Заметьте, что знаковый бит байта, в котором расположен счетчик символов, используется во время компиляции и показывает, будет ли данное слово исполняться во время компиляции или просто компилироваться в новое определение. Более подробно об этом идет речь в гл. 11.)

Поле связи. Ячейка связи содержит адрес предыдущего определения в словарном списке и применяется при поиске по словарю. В несколько упрощенном виде можно представить организацию связи следующим образом. Всякий раз, добавляя в словарь новое слово, компилятор заносит в поле связи указатель адреса предыдущего определения.
На рисунке он заносит в поле связи слова КУЛИНАРИЯ указатель определения АВТОМОБИЛЬ.



Свой поиск апостроф или ['] начинает с самого последнего занесенного в словарь слова и просматривает «цепочку» в обратном направлении с помощью адресов находящихся в ячейках связи.

Поле связи первого определения в словаре содержит нуль, который предписывает апострофу прекратить поиск - искомого слова в словаре нет





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

Код, на который происходит ссылка, называется кодом периода выполнения, потому что он используется при выполнении слова данного вида (а не тогда, когда оно определяется или компилируется).

Все переменные имеют свой один и тот же указатель кода, а все константы - свой Все определения через двоеточие - то же и т. д.



Поле параметров. За полем указателя кода следует поле параметров. В случае переменных и констант роле параметров представляет собой только одну ячейку, в случае 2CONSTANT или 2VARIABLE

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




SWAP (ПЕРЕСТАНОВКА)


Слово SWAP, определено так, что при его выполнении два верхних элемента стека меняются местами.

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

Для начала введите следующее: 1 2 . . 2 1 ok

а затем то же самое, но со словом SWAP:

1 2 SWAP . . 1 2 ok

Теперь задача 6 из упр. 2-Б может быть решена таким образом: - SWAP /

если содержимое стека определяется как ( c a b -- ).

Присвоим переменным а, b, с контрольные значения: а = 10, b = 4, с = 2. Поместим их в стек и выполним предложение, например такое: 2 10 4 - SWAP / . 3 ok

Ниже приводится список операций работы со стеком:

SWAP ( nl n2 -- n2 nl ) Перестановка двух верхних элементов стека
DUP ( n -- n n ) Дублирование верхнего элемента стека.
OVER ( n1 n2 -- nl n2 n1 ) Копирование второго элемента стека и размещение копии в вершине стека.
ROT ( nl n2 n3 -- n2 n3 n1 ) Размещение третьего элемента в вершине стека.
DROP ( n -- ) Удаление верхнего элемента из стека.



ТЕКСТОВЫЙ РЕДАКТОР ФОРТА


Здесь мы рассмотрим вариант текстового редактора, имеющегося в ряде Форт-систем. Этот вариант ориентирован в большей степени на управление с помощью команд (командный редактор), нежели на управление посредством курсора (экранный редактор). Редактор второго вида проще. Он отличается от первого способом доступа к модифицируемому тексту. Вы «входите» в редактор неким присущим только данной системе способом и вносите исправления в текст, перемещая курсор по экрану нажатием функциональных клавиш (или с помощью устройства типа «мышь», если оно имеется).

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

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

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



УКАЗАТЕЛИ ВХОДНОГО ПОТОКА, ИСПОЛЬЗУЕМЫЕ СЛОВОМ WORD


Откуда и что читать, слово WORD «понимает» по двум указателям. Первый из них >IN1

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

и нажали клавишу RETURN. Вначале переменная >IN устанавливается в нуль. После того как слово WORD прочитало строку STAR из входного потока, значение >IN становится равным 5.

1 Для пользователей систем фиг-Форта. В вашей системе это слово IN. Чтобы обеспечить соответствие нашему указателю, переопределите его:

: >IN ( -- a) IN ;

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

Второй указатель - BLK. Вспомните, что входной поток представляет собой последовательность символов, которые могут быть либо в буфере входного текста, либо в загружаемом блоке. Слово BLK указывает, где именно они находятся. Это слово играет роль и флага, и указателя. Если BLK содержит нуль, то WORD сканирует буфер входного текста, а если ненулевое значение, то WORD сканирует блок, номер которого находится в BLK (поэтому вы не можете загружать блок 0).

Ниже показан адрес интерпретируемого в данный момент текста:СОДЕРЖИМОЕ BLK ТЕКУЩИЙ АДРЕС, ИСПОЛЬЗУЕМЫЙ СЛОВОМ WORD Нуль TIB >IN " + (>IN байт относительно начала буфера входного текста) Не нуль BLK @ BLOCK >IN @ + (>IN байт относительно начала блочного буфера)

На Форте адрес сканирования для слова WORD вычисляется следующим образом:... BLK @ ?DUP IF BLOCK ELSE TIB THEN >IN @ + ...

Заметьте, что WORD обращается к слову BLOCK, поэтому если текст интерпретируется из блока, то его содержимое обязательно уже находится в некотором буфере.

>IN

( -- a)

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

BLK

( -- а)

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

  



в стеке выражение 0= NOT


4.1. Какое значение оставляет в стеке выражение 0= NOT при следующих значениях аргумента- 1, 0, 200? 4.2. Объясните, как поведет себя артишок при указанных выше значениях 4.3. Определите слово с именем РАЗРЕШЕНИЕ, которое берет из стека значение, соответствующее возрасту некоторого лица, и выводит одно из двух сообщений (согласно установленным в вашей местности порядкам)- Употребление спиртных напитков разрешено По возрасту не положено
4.4. Определите слово с именем ПРОВЕРКА-ЗНАКА, которое проверяло бы число, находящееся в стеке, и выдавало бы одно из трех сообщений- «ПОЛОЖИТЕЛЬНОЕ», «НУЛЬ» или «ОТРИЦАТЕЛЬНОЕ». 4.5. Напишите определение <> (не равно), которое в отличие от определения — (минус) всегда оставляло бы в стеке значение «истина» или "ложь" 4.6. Напишите определение XOR, используя другие логические операторы, такие, как AND, OR и NOT. 4.7. В гл 1 мы определили слово с именем STARS, но оно не выполняется правильно с аргументом, равным нулю (В Форт-системах, соответствующих Стандаргу-83, будет выведено 65535 звездочек, в остальных— одна) предложите на основе старого определения новый вариант STARS, который был бы свободен от указанного недостатка. 4.8. Напишите определение слова NEGATE (оно, между прочим, входит о состав Форт-систем), которое меняло бы знак числа в стеке на противоположный (4 - пpи этом становится —4, и наоборот). После этого, используя NEGATE, создайте слово ABS, чтобы поместить в стек абсолютное значение заданного числа n (Слово ABS также есть в вашей системе.) 4.9. Напишите определение слова /UP, которое округляло бы остаток от деления, не равный нулю. (При решении задачи: «Сколько ящиков потребу ется для упаковки п предметов?», для размещения округленного остатка потребуется дополнительный ящик.) 4.10. Напишите определение для слова  WITHIN (В ИНТЕРВАЛЕ) которое выбирает из стека три аргумента ( n нижняя-граница верхняя-граница -- )
и помещает в стек истинное значение только в том случае, когда n находится внутри диапазона: нижняя-граница <= n < верхняя-граница
4.11. Существует игра с угадыванием числа (программируя ее, вы получите больше удовольствия, чем в нее играя). Вначале вы вводите некоторое число в стек, причем можете сохранить это число в тайне, выполнив слово PAGE - которое очищает экран терминала. Затем вы просите своего партнера попытаться угадать задуманное число. Предполагаемое значение играющий должен ввести вместе со словом УГАДАЙ например. 100 УГАДАЙ
Компьютер должен выдать ответ «Слишком большое», «Слишком маленькое» или «Правильно!». Напишите определение для слова УГАДАЙ в предположении, что после нескольких попыток ответа введенные числа будут находиться в стеке. После правильного ответа стек необходимо очистить 4.12. С помощью операторов IF ... ELSE ... THEN напишите определение с именем ПРОПИСЬ, которое выводит число, находящееся в стеке, в виде слов на естественном языке. Диапазон выводимых чисел — от -4 до 4. Если в стеке находится число вне этого диапазона, то должно выдаваться сообщение «Выходит за диапазон». Например: 2 ПРОПИСЬ Дра ок -4 ПРОПИСЬ Минус четыре ок 7 ПРОПИСЬ Выходит за диапазон ок
Создайте по возможности короткое определение. (Указание: слово ABS выдает абсолютное значение числа, находящегося в стеке). 4.13. Используя созданное в упр. 4.10. определение WITHIN, разработайте еще одну игру с угадыванием чисел и назовите ее ЛОВУШКА. По условию игры вы вводите значение, не известное вашему партнеру, после чего он пытается угадать заданное число, вводя два числа, между которыми, по его мнению, заключено искомое значение, например:0 1000 ЛОВУШКА Между ок
330 660 ЛОВУШКА Между ок 440 550 ЛОВУШКА Вне ок 330 440 ЛОВУШКА Между ок
и т. д. до тех пор, пока ваш партнер не отгадает ответ: 391 391 ЛОВУШКА Вы угадали! ok
Совет: вы можете предусмотреть в своей программе следующее: когда с искомым числом совпадает только одно число из задаваемого интервала, она не будет выдавать «Между».
Про ошибки на сайте обязательно сообщите .

к решению задач, запомните следующее


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

СловоЗавершающий символ
:;
.""
()

1.1. Определите слово с именем ДАР, которое при своем выполнении выдаст название какого-то подарка. Например, вы можете определить : ДАР ." подставку для книг " ;
Теперь определите слово с именем ДАРИТЕЛЬ, которое выводит на печать имя некоего лица, а затем слово с именем СПАСИБО, определение которого включает вновь созданные слова ДАР и ДАРИТЕЛЬ и выводит на печать сообщение, аналогичное следующему: Дорогая Маша. спасибо за подставку для книг, ок
1.2. Определите слово с именем МИНУС-ДЕСЯТЬ, которое выбирает некоторое число из стека, вычитает 10 и помещает полученный результат в стек. (Подсказка: вы можете использовать слово +) Не забудьте включить в определение стековую нотацию. 1.3. После того как вы введете слова при решении упр. 1.1, переопределите слово ДАРИТЕЛЬ так, чтобы можно было вывести на печать чье-то другое имя (не переопределяя слово СПАСИБО), выполните последнее снова. Сможете ли вы объяснить, почему слово СПАСИБО выводит на печать имя первого дарителя?
Про ошибки на сайте обязательно сообщите .


10.1. Введите несколько известных цитат в некоторый доступный блок, скажем в блок 228. Определите слово с именем ЗАМЕНА, которое берет из стека два значения в коде ASCII и заменяет все вхождения первого символа в блоке 228 на второй символ. Так следующее выражение заменит все литеры А литерами Е: 65 69 ЗАМЕНА
10.2. Определите слово с именем ПРОГНОЗ, которое будет выдавать на вашем терминале некоторый прогноз, например «Вы получите хорошие новости по почте>. Прогноз должен выбираться случайным образом из списка, включающего 16 (или менее) вариантов. Каждый прогноз может иметь длину до 64 символов.
10.3. а) Определите слово ДА/НЕТ?, которое можно использовать в прикладной программе, требующей утвердительного или отрицательного ответа пользователя. Определение должно ожидать нажатия одной клавиши, после которого послать в стек значение истина, если была нажата клавиша Y, и ложь при нажатии любой другой клавиши.
б) Иногда пользователь нажимает эту клавишу, работая в режиме нижнего регистра. Перепишите определение ДА/НЕТ? таким образом, чтобы оно воспринимало нажатие клавиши Y в обоих регистрах как «да».
в) Пользователь может по ошибке вместо клавиши Y (например, при записи файла на диск, над которым он проработал последние три часа) нажать другую клавишу. Перепишите определение ДА/НЕТ? таким образом, чтобы оно воспринимало символы, поступающие только при нажатии клавиш Y и N в верхнем и нижнем регистрах и не прекращало своего выполнения до тех пор, пока не получит сигналы от этих клавиш с записью в стек истины в случае Y и лжи в случае N. 10.4. Согласно восточной легенде Будда наделял всех людей, родившихся в определенном году, чертами, присущими одному из 12 животных. На этой основе построен циклический календарь. Цикл составляет 12 лет, по истечении которых он возобновляется. Например, считается, что 1900 год был «годом крысы». Внутри цикла годы, обозначенные названием животного, чередуются в следующем порядке: год крысы, год быка, год тигра, кролика, дракона, змеи, лошади, барана, обезьяны, петуха, собаки, свиньи.


11.1. Введите определяющее слово с именем ЗАГРУЗКА, которое будет определять слова, загружающие при своем исполнении некоторый блок. Например, выражение690 ЗАГРУЗКА КОРРЕСПОНДЕНЦИЯ
должно определить слово КОРРЕСПОНДЕНЦИЯ. В результате выполнения последнего должен быть загружен блок 600.
11.2. Определите слово с именем СИСТЕМА., создающее слова для вывода чисел в конкретных системах счисления. Например, выражение 16 СИСТЕМА. Н.
должно определить слово Н., которое выводило бы значение из вершины стека в шестнадцатиричной системе, но саму систему счисления (BASE) не меняло бы: DECIMAL 17 DUP Н. .<return> 11 17 ok
11.3. Определите слово с именем МНОГО, чтобы оно получало адрес некоторого слова, например CR или STAR, и создавало множественное число этого слова, в нашем случае CRS или STARS. Вы будете задавать адрес слова в единственном числе посредством апострофа. Например, фраза ' CR МНОГО CRS
определит слово CRS так же, как и выражение : CRS ?DUP IF 0 DO CR LOOP THEN ;
11.4. На французский язык слова DO и LOOP переводятся как TOURNE и RETOURNE соответственно. Определите TOURNE и RETOURNE как французские имена слов управления циклом, используя слова DO и LOOP, после чего проверьте их, написав цикл на французском языке.
11.5. Напишите слово с именем РАЗ, которое вызовет исполнение оставшихся во входном потоке символов до возврата каретки. Число исполнений задается числом, находящимся в вершине стека, например: 7 РАЗ 42 EMIT SPACE<return> * * * * * * * ok
11.6. В настоящей главе демонстрировался пример использования определяющего слова с именем ФОРМА. Чтобы задать конкретное изображение, нужно было ввести восемь чисел, каждое из которых представляло собой битовый шаблон отдельной строки. Придумайте более изящный способ задания описания рисунка, содержащего 8х8 элементов изображения. Можно, например, вместо шестнадцатиричных цифр задавать некоторую схему.
Теперь реализуйте то, что вы придумали (с привлечением существующего определения слова .РЯД). Проверьте свой способ формирования рисунка.


12.1. Для описания четырех полей в простой файловой системе мы применяли следующие определения: ( смещение) ( длина) CREATE фамилия 0 , 16 , CREATE имя 16 , 12 , CREATE работа 28 , 24 , CREATE телефон 52 , 12 ,
а затем добавили еще одно 64 CONSTANT /ЗАПИСЬ
чтобы определить длину всей записи. Изменяя длину поля с фамилией, мы должны также подправлять начальный адрес остальных трех полей, а также значение переменной /ЗАПИСЬ Найдите программный способ изменения этих значений. Определите синтаксис и напишите программу.
12.2 Используя язык базы данных, реализованной в простой файловой системе, определите новое слово «вызвать», которое осуществляло бы поиск имени и помещало в вершину стека ФИО и номер телефона, например: Вызвать Конни
Конни Чанг 555-9653 ok



2.1. В чем различие между DUP DUP и 2DUP? 2.2. Определите слово NIP (отщипнуть) для удаления второго элемента стека, т. е. ( a b -- b)
2.3. Определите слово TUCK (подобрать) для копирования, верхнего элемента стека и размещения копии в стеке третьим элементом, т. е. ( a b -- b a b)
2.4. Определите слово —ROT, которое размещало бы верхний элемент под вторым и третьим (в противоположность ROT), т. е. ( a b c -- c a b)
 2.5. Напишите предложение для перестановки четырех верхних элементов стека в обратном порядке, т. е.  ( 1 2 3 4 -- 4 3 2 1)
2.6. Напишите слово с именем 3DUP, которое будет дублировать три верхних элемента стека, например ( 1 2 3 -- 1 2 3 1 2 3)
Напишите определения для следующих выражений в инфиксной форме с учетом указанной стековой нотации:  2.7. a2 + ab + c ( c a b -- результат) a - b 2.8 ----- ( a b -- результат) a + b
2.9. Представьте себе, что вы программист, занимающийся учетом продукции на птицеферме Мерайи. Определите слово с именем УПАКОВКА, которое снимает со стека значение, равное числу яиц, снесенных в день подсчета на данной ферме. В результате его выполнения на печать выдается число коробок, требуемых для упаковки этих яиц, из расчета по 12 штук на коробку, а также число яиц, оставшихся неупакованными из-за того, что их недостаточно для заполнения еще одной коробки.
Про ошибки на сайте обязательно сообщите .


3.1. а) Введите ваши определения слов ДАР, ДАРИТЕЛЬ и СПАСИБО из упр. 1.1 и 1.3 в блок, после чего загрузите и исполните слово СПАСИБО,
б) с помощью редактора измените имя в определении ДАРИТЕЛЬ, после чего загрузите и исполните слово СПАСИБО снова. Что произойдет в этом случае?
3.2. Попытайтесь загрузить некоторые из наших математических определений из гл. 2 в какой-нибудь доступный блок, а затем загрузите его. Поэкспериментируйте.


5.1. Укажите различия между -1 и 1-.
5. 2 Переведите следующее алгебраическое выражение в форму определения Форта: -(ab)/c
при состоянии стека ( а b с --).
5.3. При состоянии стека (6 70 123 45 --)
напишите выражение, которое бы инициировало печать наибольшего из этих значений.
5.4. а) Определите слово 2ПОРЯДОК, которое из имеющихся в стеке двух чисел располагало бы в вершине большее, а оставшееся - под ним.
б) Определите слово ЗПОРЯДОК, которое располагало бы три заданных числа в стеке так, чтобы большее было в его вершине.
в) Вспомните определение ОБЪЕМ из гл. 4. Перепишите его, используя определение ЗПОРЯДОК, так, чтобы пользователь мог вводить измерения в любом порядке.
Практикум в масштабировании
5.5. Гистограмма - это графическое представление серии значений, каждая из которых выражена высотой или длиной некоторого отрезка. Определите слово с именем РИСУЙ - компонент вашей программы по созданию гистограмм По заданному значению в диапазоне от 0 до 100 слово РИСУЙ должно вывести на экран горизонтальную линию из звездочек, графически представляющую это заданное значение.
Трудность заключается в том, что на экране только 80 колонок Таким образом, значение 100 должно соответствовать 80 звездочкам, значение 50 - 40 звездочкам, значение 0 - 0 звездочкам и т. д. (Начинайте ваше определение с команды CR и используйте вариант слова STARS из упр. 4 7 )
5.6. В режиме калькулятора переведите указанные значения температур из одной шкалы в другую по формулам°C=(°F-32)/1.8; °F = (°C x 1.8) + 32; °K = °C + 273.
Выразите все аргументы и результаты целыми числами (в градусах):
а) 0°F в °С,
б) 212° F в °С;
в) -32° F в °С;
г) 16° С в °F,
д) 233° К в °С.
5.7. Определите слова для выполнения преобразований из упр. 5.3. Используйте следующие имена: F>C Р>K C>F C>K K>F K>C
Проверьте их выполнение с приведенными выше значениями.


В задачах с 6.1 и по 6. 6 вы должны создать определения нескольких слов, которые будут выводить шаблоны из звездочек. Это потребует применения циклов DO и BEGIN ... UNTIL. 6.1. Создайте слово с именем STARS, которое выводит п звездочек в одной строке, п задается в стеке. 6.2. Определите слово БРУСОК, которое выводит прямоугольник из звездочек. Ширина и высота (число строк) задаются в стеке в следующем порядке: ( ширина высота -- ).10 3 БРУСОК ********** ********** ********** ok
6.3. Создайте слово \STARS, которое выводит наклонные (ромбовидные) массивы из звездочек. Высота задается в стеке. Используйте цикл DO и для простоты примите ширину постоянной и равной 10 звездочкам. 3 \STARS ********** ********** ********** ok
6.4. Определите слово, которое выводит массивы из звездочек с наклоном в другую сторону, и назовите его /STARS. Высота должна задаваться в стеке, а ширину возьмите постоянной и равной 10 звездочкам. Используйте оператор DO. 6.5. Переопределите последнее слово с использованием оператора цикла BEGIN ... UNTIL.
6.6. Напишите определение с именем РОМБЫ, которое выводит заданное число ромбов, как показано в следующем примере: 6.7 В гл. 3 было введено слово THRU. Сможете ли вы написать определение этого слова с помощью вызова LOAD? Если хотите, расширьте функции слова так, чтобы при использовании его во время загрузки блока выводился бы номер последнего. 6.8. При рассмотрении слова LEAVE мы приводили пример, в котором вычислялись сложные проценты при норме процента 6% и начальном остатке 1000 дол. за 20 лет или же до тех пор, пока начальный остаток не удвоится, в зависимости от того, какое из событий наступит раньше. Перепишите это определение таким образом, чтобы значения начальной суммы и нормы процента находились в стеке и процесс вычисления завершился с помощью LEAVE как только сумма начального остатка удвоится. 6.9. Определите слово с именем **, которое вычисляло бы степень, например: 7 2 ** . 49 ok ( семь в квадрате ) 2 4 ** . 16 ок ( два в четвертой степени )
Для простоты показатель степени примите положительным, но убедитесь в том, что ** выполняется правильно, если показатель равен нулю (в результате должна получиться единица) или единице (в результате должно получиться само число).
2 РОМБЫ * *** ***** ******* ********* *********** ************* *************** ***************** ******************* ******************* ***************** *************** ************* *********** ********* ******* ***** *** * * *** ***** ******* ********* *********** ************* *************** ***************** ******************* ******************* ***************** *************** ************* *********** ********* ******* ***** *** *
<

в некоторый доступный блок, скажем


10.1. Введите несколько известных цитат в некоторый доступный блок, скажем в блок 228. Определите слово с именем ЗАМЕНА, которое берет из стека два значения в коде ASCII и заменяет все вхождения первого символа в блоке 228 на второй символ. Так следующее выражение заменит все литеры А литерами Е: 65 69 ЗАМЕНА
10.2. Определите слово с именем ПРОГНОЗ, которое будет выдавать на вашем терминале некоторый прогноз, например «Вы получите хорошие новости по почте>. Прогноз должен выбираться случайным образом из списка, включающего 16 (или менее) вариантов. Каждый прогноз может иметь длину до 64 символов.
10.3. а) Определите слово ДА/НЕТ?, которое можно использовать в прикладной программе, требующей утвердительного или отрицательного ответа пользователя. Определение должно ожидать нажатия одной клавиши, после которого послать в стек значение истина, если была нажата клавиша Y, и ложь при нажатии любой другой клавиши.
б) Иногда пользователь нажимает эту клавишу, работая в режиме нижнего регистра. Перепишите определение ДА/НЕТ? таким образом, чтобы оно воспринимало нажатие клавиши Y в обоих регистрах как «да».
в) Пользователь может по ошибке вместо клавиши Y (например, при записи файла на диск, над которым он проработал последние три часа) нажать другую клавишу. Перепишите определение ДА/НЕТ? таким образом, чтобы оно воспринимало символы, поступающие только при нажатии клавиш Y и N в верхнем и нижнем регистрах и не прекращало своего выполнения до тех пор, пока не получит сигналы от этих клавиш с записью в стек истины в случае Y и лжи в случае N. 10.4. Согласно восточной легенде Будда наделял всех людей, родившихся в определенном году, чертами, присущими одному из 12 животных. На этой основе построен циклический календарь. Цикл составляет 12 лет, по истечении которых он возобновляется. Например, считается, что 1900 год был «годом крысы». Внутри цикла годы, обозначенные названием животного, чередуются в следующем порядке: год крысы, год быка, год тигра, кролика, дракона, змеи, лошади, барана, обезьяны, петуха, собаки, свиньи.

с именем ЗАГРУЗКА, которое будет


11.1. Введите определяющее слово с именем ЗАГРУЗКА, которое будет определять слова, загружающие при своем исполнении некоторый блок. Например, выражение690 ЗАГРУЗКА КОРРЕСПОНДЕНЦИЯ
должно определить слово КОРРЕСПОНДЕНЦИЯ. В результате выполнения последнего должен быть загружен блок 600.
11.2. Определите слово с именем СИСТЕМА., создающее слова для вывода чисел в конкретных системах счисления. Например, выражение 16 СИСТЕМА. Н.
должно определить слово Н., которое выводило бы значение из вершины стека в шестнадцатиричной системе, но саму систему счисления (BASE) не меняло бы: DECIMAL 17 DUP Н. .<return> 11 17 ok
11.3. Определите слово с именем МНОГО, чтобы оно получало адрес некоторого слова, например CR или STAR, и создавало множественное число этого слова, в нашем случае CRS или STARS. Вы будете задавать адрес слова в единственном числе посредством апострофа. Например, фраза ' CR МНОГО CRS
определит слово CRS так же, как и выражение : CRS ?DUP IF 0 DO CR LOOP THEN ;
11.4. На французский язык слова DO и LOOP переводятся как TOURNE и RETOURNE соответственно. Определите TOURNE и RETOURNE как французские имена слов управления циклом, используя слова DO и LOOP, после чего проверьте их, написав цикл на французском языке.
11.5. Напишите слово с именем РАЗ, которое вызовет исполнение оставшихся во входном потоке символов до возврата каретки. Число исполнений задается числом, находящимся в вершине стека, например: 7 РАЗ 42 EMIT SPACE<return> * * * * * * * ok
11.6. В настоящей главе демонстрировался пример использования определяющего слова с именем ФОРМА. Чтобы задать конкретное изображение, нужно было ввести восемь чисел, каждое из которых представляло собой битовый шаблон отдельной строки. Придумайте более изящный способ задания описания рисунка, содержащего 8х8 элементов изображения. Можно, например, вместо шестнадцатиричных цифр задавать некоторую схему.
Теперь реализуйте то, что вы придумали (с привлечением существующего определения слова .РЯД). Проверьте свой способ формирования рисунка.

в простой файловой системе мы


12.1. Для описания четырех полей в простой файловой системе мы применяли следующие определения: ( смещение) ( длина) CREATE фамилия 0 , 16 , CREATE имя 16 , 12 , CREATE работа 28 , 24 , CREATE телефон 52 , 12 ,
а затем добавили еще одно 64 CONSTANT /ЗАПИСЬ
чтобы определить длину всей записи. Изменяя длину поля с фамилией, мы должны также подправлять начальный адрес остальных трех полей, а также значение переменной /ЗАПИСЬ Найдите программный способ изменения этих значений. Определите синтаксис и напишите программу.
12.2 Используя язык базы данных, реализованной в простой файловой системе, определите новое слово «вызвать», которое осуществляло бы поиск имени и помещало в вершину стека ФИО и номер телефона, например: Вызвать Конни
Конни Чанг 555-9653 ok


В чем различие между DUP


2.1. В чем различие между DUP DUP и 2DUP? 2.2. Определите слово NIP (отщипнуть) для удаления второго элемента стека, т. е. ( a b -- b)
2.3. Определите слово TUCK (подобрать) для копирования, верхнего элемента стека и размещения копии в стеке третьим элементом, т. е. ( a b -- b a b)
2.4. Определите слово —ROT, которое размещало бы верхний элемент под вторым и третьим (в противоположность ROT), т. е. ( a b c -- c a b)
 2.5. Напишите предложение для перестановки четырех верхних элементов стека в обратном порядке, т. е.  ( 1 2 3 4 -- 4 3 2 1)
2.6. Напишите слово с именем 3DUP, которое будет дублировать три верхних элемента стека, например ( 1 2 3 -- 1 2 3 1 2 3)
Напишите определения для следующих выражений в инфиксной форме с учетом указанной стековой нотации:  2.7. a2 + ab + c ( c a b -- результат) a - b 2.8 ----- ( a b -- результат) a + b
2.9. Представьте себе, что вы программист, занимающийся учетом продукции на птицеферме Мерайи. Определите слово с именем УПАКОВКА, которое снимает со стека значение, равное числу яиц, снесенных в день подсчета на данной ферме. В результате его выполнения на печать выдается число коробок, требуемых для упаковки этих яиц, из расчета по 12 штук на коробку, а также число яиц, оставшихся неупакованными из-за того, что их недостаточно для заполнения еще одной коробки.
Про ошибки на сайте обязательно сообщите .

Введите ваши определения слов ДАР,


3.1. а) Введите ваши определения слов ДАР, ДАРИТЕЛЬ и СПАСИБО из упр. 1.1 и 1.3 в блок, после чего загрузите и исполните слово СПАСИБО,
б) с помощью редактора измените имя в определении ДАРИТЕЛЬ, после чего загрузите и исполните слово СПАСИБО снова. Что произойдет в этом случае?
3.2. Попытайтесь загрузить некоторые из наших математических определений из гл. 2 в какой-нибудь доступный блок, а затем загрузите его. Поэкспериментируйте.

Переведите следующее алгебраическое выражение


5.1. Укажите различия между -1 и 1-.
5. 2 Переведите следующее алгебраическое выражение в форму определения Форта: -(ab)/c
при состоянии стека ( а b с --).
5.3. При состоянии стека (6 70 123 45 --)
напишите выражение, которое бы инициировало печать наибольшего из этих значений.
5.4. а) Определите слово 2ПОРЯДОК, которое из имеющихся в стеке двух чисел располагало бы в вершине большее, а оставшееся - под ним.
б) Определите слово ЗПОРЯДОК, которое располагало бы три заданных числа в стеке так, чтобы большее было в его вершине.
в) Вспомните определение ОБЪЕМ из гл. 4. Перепишите его, используя определение ЗПОРЯДОК, так, чтобы пользователь мог вводить измерения в любом порядке.
Практикум в масштабировании
5.5. Гистограмма - это графическое представление серии значений, каждая из которых выражена высотой или длиной некоторого отрезка. Определите слово с именем РИСУЙ - компонент вашей программы по созданию гистограмм По заданному значению в диапазоне от 0 до 100 слово РИСУЙ должно вывести на экран горизонтальную линию из звездочек, графически представляющую это заданное значение.
Трудность заключается в том, что на экране только 80 колонок Таким образом, значение 100 должно соответствовать 80 звездочкам, значение 50 - 40 звездочкам, значение 0 - 0 звездочкам и т. д. (Начинайте ваше определение с команды CR и используйте вариант слова STARS из упр. 4 7 )
5.6. В режиме калькулятора переведите указанные значения температур из одной шкалы в другую по формулам°C=(°F-32)/1.8; °F = (°C x 1.8) + 32; °K = °C + 273.
Выразите все аргументы и результаты целыми числами (в градусах):
а) 0°F в °С,
б) 212° F в °С;
в) -32° F в °С;
г) 16° С в °F,
д) 233° К в °С.
5.7. Определите слова для выполнения преобразований из упр. 5.3. Используйте следующие имена: F>C Р>K C>F C>K K>F K>C
Проверьте их выполнение с приведенными выше значениями.

вы должны создать определения


В задачах с 6.1 и по 6. 6 вы должны создать определения нескольких слов, которые будут выводить шаблоны из звездочек. Это потребует применения циклов DO и BEGIN ... UNTIL. 6.1. Создайте слово с именем STARS, которое выводит п звездочек в одной строке, п задается в стеке. 6.2. Определите слово БРУСОК, которое выводит прямоугольник из звездочек. Ширина и высота (число строк) задаются в стеке в следующем порядке: ( ширина высота -- ).10 3 БРУСОК ********** ********** ********** ok
6.3. Создайте слово \STARS, которое выводит наклонные (ромбовидные) массивы из звездочек. Высота задается в стеке. Используйте цикл DO и для простоты примите ширину постоянной и равной 10 звездочкам. 3 \STARS ********** ********** ********** ok
6.4. Определите слово, которое выводит массивы из звездочек с наклоном в другую сторону, и назовите его /STARS. Высота должна задаваться в стеке, а ширину возьмите постоянной и равной 10 звездочкам. Используйте оператор DO. 6.5. Переопределите последнее слово с использованием оператора цикла BEGIN ... UNTIL.
6.6. Напишите определение с именем РОМБЫ, которое выводит заданное число ромбов, как показано в следующем примере: 6.7 В гл. 3 было введено слово THRU. Сможете ли вы написать определение этого слова с помощью вызова LOAD? Если хотите, расширьте функции слова так, чтобы при использовании его во время загрузки блока выводился бы номер последнего. 6.8. При рассмотрении слова LEAVE мы приводили пример, в котором вычислялись сложные проценты при норме процента 6% и начальном остатке 1000 дол. за 20 лет или же до тех пор, пока начальный остаток не удвоится, в зависимости от того, какое из событий наступит раньше. Перепишите это определение таким образом, чтобы значения начальной суммы и нормы процента находились в стеке и процесс вычисления завершился с помощью LEAVE как только сумма начального остатка удвоится. 6.9. Определите слово с именем **, которое вычисляло бы степень, например: 7 2 ** . 49 ok ( семь в квадрате ) 2 4 ** . 16 ок ( два в четвертой степени )
Для простоты показатель степени примите положительным, но убедитесь в том, что ** выполняется правильно, если показатель равен нулю (в результате должна получиться единица) или единице (в результате должно получиться само число).
2 РОМБЫ * *** ***** ******* ********* *********** ************* *************** ***************** ******************* ******************* ***************** *************** ************* *********** ********* ******* ***** *** * * *** ***** ******* ********* *********** ************* *************** ***************** ******************* ******************* ***************** *************** ************* *********** ********* ******* ***** *** *
<

Первое слово увеличивает число имеющихся


8.1. а) Создайте два слови с именами ИСПЕКИ-ПИРОЖОЖ. и СЪЕШЬ-ПИРОЖОК. Первое слово увеличивает число имеющихся пирожков на единицу, второе уменьшает эго число на единицу и благодарит вас за пирожок. Но если пирожков в наличии нет, оно выводит: «Какой пирожок?». (Начните в предположении, что число пирожков равно нулю.)СЪЕШЬ-ПИРОЖОК Какой пирожок? ИСПЕКИ-ПИРОЖОК ok
СЪЕШЬ-ПИРОЖОК Спасибо! ok
6) Напишите слово с именем ЗАМОРОЗЬ-ПИРОЖКИ, которое берет все имеющиеся в наличии пирожки и добавляет их к числу пирожков, хранящихся в морозильной камере. Помните, что замороженные пирожки есть нельзя.ИСПЕКИ-ПИРОЖОК ИСПЕКИ-ПИРОЖОК ЗАМОРОЗЬ-ПИРОЖКИ ок ПИРОЖКИ ? 0 ok
ЗАМОРОЖЕННЫЕ-ПИРОЖКИ ? 2 ok
8.2. Определите слово с именем .BASE, которое выводит текущее значение переменной BASE в десятичной системе счисления. Проверьте это слово при первом же изменении переменной BASE на любом значении, отличном от 10, (Это не так просто, как может показаться.)DECIMAL .BASE 10 ok
HEX .BASE 16 ok
8.3. Определите слово для форматизации чисел с именем М., которое выводит на печать число двойной длины с десятичной точкой. Положение десятичной точки внутри числа подвижно и зависит от значения некоторой переменной Если в вашей системе имеется для указания положения десятичной точки только что введенного числа переменная DPL или какая-либо другая, воспользуйтесь ею, например гак:2000.0О M. 2000.00 ok
В противном случае определите переменную с именем ДРОБНЫЕ и загружайте туда значения вручную-2 ДРОБНЫЕ ! 2000.00 M. 2000.00 ok
(В том случае, когда вводимое число не содержит десятичной точки, DPL вырабатывает — I и в вершину стека помещается целое число одинарной длины. Для повышения надежности учтите при создании М. эту ситуацию.) 8.4. Для того чтобы иметь сведения о наличии цветных карандашей в вашем учреждении, создайте массив, каждая ячейка которого содержала бы счетчик карандашей одного конкретного цвета. Определите набор слов, такой, что выражение типа КРАСНЫЕ КАРАНДАШИ
доставляет адрес ячейки, содержащей число красных карандашей и т. д., а затем присвойте этим переменным значения, чтобы набор карандашей был следующим: 23 КРАСНЫЕ КАРАНДАШИ 15 ГОЛУБЫЕ КАРАНДАШИ 12 ЗЕЛЕНЫЕ КАРАНДАШИ 0 ЖЕЛТЫЕ КАРАНДАШИ
8.5. Создайте массив значений и напечатайте гистограмму (см. упражнения к гл. 5), где для каждого значения выводилась бы строка символов *. Для начала сформируйте массив из 10 элементов. Присвойте всем элементам массива значения в диапазоне от нуля до 10, после чего определите слово РИСУЙ, которое будет выводить строку, соответствующую каждому значению. На каждой строке должен выводиться номер элемента, а после него -- число звездочек, равное содержимому данного элемента. 8.6. В данном упражнении мы используем переменные одинарной длины как массивы флагов, а константы — как битовые маски. Начните с определения констант, каждая из которых представляет отдельную битовую позицию (из четырех младших разрядов 16-разрядного значения): ЖЕНЩИНА СЕМЕЙНЫЙ РАБОТАЕТ ГОРОДСКОЙ
Теперь определите следующие константы, каждая из которых несет нули во всех битах МУЖЧИНА ОДИНОКИЙ HE-РАБОТАЕТ НЕ-ГОРОДСКОЙ
Далее определите в виде переменных два имени, например. VARIABLE ВАСЯ VARIABLE МАША
Впоследствии в этих переменных будут содержаться атрибуты наших персонажей. Затем определите слово с именем ОПИСАНИЯ, которое выбирает из стека значения четырех констант из восьми нами заведенных плюс адрес поля, отведенного для одного из наших персонажей: ЖЕНШИНА ОДИНОКИЙ РАБОТАЕТ ГОРОДСКОЙ МАША ОПИСАНИЯ
Слово ОПИСАНИЯ заносит в область, отведенную персонажу, битовый шаблон, соответствующий атрибутам персонажа Наконец, определите слово СВЕДЕНИЯ, которое выбирало бы из стека имя персонажа и по битовому шаблону последнего выводило бы сведения о нем, например: МАША СВЕДЕНИЯ ЖЕНЩИНА ОДИНОКИЙ РАБОТАЕТ ГОРОДСКОЙ ok
8.7. Создайте программу, которая высвечивала бы поле для игры в «крестики-нолики» и давала возможность двум игрокам задавать свои ходы с помощью клавиатуры. Так, выражение 4 X! помещает X в клетку 4 (счет клеток ведется с единицы) и выводит на экран картинку:


После этого выражение 3 0! помещает 0 в клетку 3 и выводит следующую картинку.


Для того чтобы помнить содержимое игрового поля, примените массив байтов, где значение 1 соответствовало бы X, значение -1 --  0, а 0 -- пустой клетке. Так как некоторые компьютеры не позволяют программно подводить курсор к определенному месту экрана, мы будем просто заново генерировать поле для игры с помощью CR, SPACE и т. д. (Замечание: до тех пор, пока мы не приведем дополнительные сведения о словарях, избегайте обозначения чего-либо посредством «X», так как ваше имя может вступить в конфликт с редакторским X )


Про ошибки на сайте обязательно сообщите .

Напишите определение слова ПОЛУЧАЕМ так,


9.1. Напишите определение слова ПОЛУЧАЕМ так, чтобы его функции могли корректироваться словами СКЛАДЫВАЯ и УМНОЖАЯ, как в приведенном ниже примере:СКЛАДЫВАЯ 2 3 ПОЛУЧАЕМ 5 ok УМНОЖАЯ 2 3 ПОЛУЧАЕМ 6 ok
9.2. Каков начальный адрес вашего личного словаря? 9.3. Как далеко расположена рабочая область (PAD) в чашей системе от конца вашего личного словаря? 9.4. Если слово ДАТА определено через VARIABLE, то в чем состоит различие между следующими фразами. ДАТА . и ' ДАТА >BODY ? А чем отличаются друг от друга эти две фразы: BASE и ' BASE >BODY ? 9.5. В этом упражнении вы должны создать массив для векторных (косвенных) вычислений, т.е. массив с адресами слов Форта. Определите одномерный массив ячеек, содержащих по два байта каждая, который будет возвращать адрес n-го элемента при заданном индексе n. Определите несколько слов так, чтобы они обеспечивали вывод на ваш дисплей некоторой информации и ничего не вводили. Запомните адреса этих слов в различных элементах массива, а адрес ничего не выполняющего слова - в остальных его элементах. Определите слово, вырабатывающее правильный индекс и выполняющее слово, адрес которого расположен в соответствующем индексу элементе, например:0 ВЫПОЛНИ Привет, я ГОВОРЮ на Форте, ok
1 ВЫПОЛНИ 1 2 3 4 5 6 7 8 9 10 ok
2 ВЫПОЛНИ ********** ********** ********** ********** **********
3 ВЫПОЛНИ ок 4 ВЫПОЛНИ ок

Про ошибки на сайте обязательно сообщите .

УСЛОВНЫЙ ОПЕРАТОР


Запишем на Форте простой оператор принятия решения. Допустим, вы программируете механический упаковщик яиц в картонные коробки. Некоторое механическое устройство уже подсчитало число яиц, находящихся в ленте конвейера, и поместило это число в стек. В соответствии со следующим предложением на Форте: 12 = IF УПАКОВАТЬ THEN

осуществляется проверка: равно ли число в стеке 12, и ЕСЛИ (IF) да, ТО (THEN) выполняется слово УПАКОВАТЬ. Если число не равно 12, то выполняются слова, которые следуют за THEN.

Слово = выбирает два значения из стека и сравнивает их, чтобы проверить, равны ли они.

 

Если условие истинно (да), то будут выполнены слова, следующие за IF, если ложно — то слова, следующие за THEN.

Попытаемся выполнять этот оператор. Определим, например, следующее слово: : ?ПОЛНА ( число-яиц ) 12 - IF . " КОРОБКА ПОЛНА " THEN ; ok

11 ?ПОЛНА ok

12 ?ПОЛНА КОРОБКА ПОЛНА ок

Заметим, что оператор IF ... THEN должен содержаться только внутри определения (через двоеточие), так что вы не можете использовать его в режиме калькулятора.

Абстрагируйтесь от всех значений, которые заключены в словах IF (ЕСЛИ) и THEN (TO) в естественном языке В Форте они выражают то, что операторы, следующие за IF. выполняются лишь в случае, если условие истинно, а операторы, следующие за THEN, всегда. Это равносильно тому, что вы прикажете компьютеру после принятия решения (выполнять или не выполнять слова, следующие за IF, в зависимости от того, истинно условие или ложно) продолжить выполнение остальной части определения (следующей за THEN). В нашем примере после THEN находится единственное слово ;, которое означает конец определения.

Если вам удобно, сразу представляйте запись в постфиксной форме. Вместо традиционного применения выражения IF:

IF ( условие ) THEN ( действие ) ENDIF

мы имеем ( условие ) IF ( действие ) THEN

Помните, что каждому слову IF должно соответствовать свое THEN, причем в пределах одного и того же определения



УСОВЕРШЕНСТВОВАННЫЙ ГЕНЕРАТОР БЕССМЫСЛЕННЫХ СООБЩЕНИЙ


Генератор бессмысленных сообщений, о котором шла речь в гл. 10, имеет существенный недостаток: необходимо осуществлять возврат каретки в определении слова СООБЩЕНИЕ. Это ведет к мозаичному выводу фраз.

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

комментариев. Приобретайте навыки чтения программ на Форте. Попробуйте сделать определения более ясными. Результат работы нашей программы выглядит так: В ДАННОМ СООБЩЕНИИ МЫ РАССКАЖЕМ ВАМ О ТОМ, ЧТО ВКЛАДЫВАЯ ИМЕЮЩИЕСЯ В НАЛИЧИИ СРЕДСТВА В ИНТЕГРИРОВАННЫЙ ЦИФРОВОЙ КОМПЛЕКС ПРИМЕНЯЯ АВТОНОМНЫЙ КУЛЬТУРНЫЙ ПРОДУКТ ПРЕДСТАВЛЯЕТСЯ ВОЗМОЖНЫМ ДАЖЕ НЕСМОТРЯ НА КВАЛИФИЦИРОВАННЫЙ ЦИФРОВОЙ ПРОЕКТ ЕЦЕ БОЛЬШЕ КРЕПИТЬ УНИКАЛЬНЫЙ ПРОГРАММНЫЙ ОБЪЕМ.

С ОДНОЙ СТОРОНЫ, ИССЛЕДОВАНИЯ ПОКАЗАЛИ, ЧТО СТРУКТУРИРОВАННО ПРИМЕНЯЯ ОБЩИЙ ТРУДОВОЙ ОБЪЕМ УЧИТЫВАЯ АВТОМАТИЗИРОВАННЫЙ ПРОГРАММНЫЙ АВТОМАТ СТАНОВИТСЯ НЕОСУЩЕСТВИМЫМ ПОДНИМАЯ СЛУЧАЙНЫЙ ПРОИЗВОДСТВЕННЫЙ ПРОЦЕСС ЕЩЕ БОЛЬШЕ КРЕПИТЬ КВАЛИФИЦИРОВАННЫЙ МНОГООТРАСЛЕВОЙ ПРИНЦИП.

С ДРУГОЙ СТОРОНЫ, ТЕМ НЕ МЕНЕЕ, ПРАКТИЧЕСКИЙ ОПЫТ ПОКАЗЫВАЕТ, ЧТО СТРУКТУРИРОВАННО ПРИМЕНЯЯ ЦИФРОВОЙ ХИМИЧЕСКИЙ КОМПЛЕКС УЧИТЫВАЯ НЕОБЫЧАЙНЫЙ МНОГООТРАСЛЕВОЙ АВТОМАТ НЕОБХОДИМО РАССМАТРИВАЯ НЕОБЫЧАЙНЫЙ ПРОГРАММНЫЙ КРИТЕРИИ ФУНКЦИОНИРОВАТЬ КАК ВРЕМЕННЫЙ ЦИФРОВОЙ ПРОЕКТ.

В РЕЗУЛЬТАТЕ НАШЕ ПРЕДЛОЖЕНИЕ ЗАКЛЮЧАЕТСЯ В ТОМ, ЧТО СТРУКТУРНО ПРИМЕНЯЯ СЛУЧАЙНЫЙ КУЛЬТУРНЫЙ ИНТЕРЕС УЧИТЫВАЯ СИСТЕМАТИЗИРОВАННЫЙ ЦИФРОВОЙ УРОВЕНЬ НЕОБХОДИМО РАССМАТРИВАЯ ЦИФРОВОЙ ТРУДОВОЙ ПРОЕКТ ЕЩЕ БОЛЬШЕ КРЕПИТЬ СИНХРОНИЗИРОВАННЫЙ КОРОТКОВОЛНОВЫЙ КРИТЕРИИ.

Block # 156 0 ( Генератор бессмысленных выражений, вариант с самоформатированием ) 1 VARIABLE ОСТАТОК \ количество оставшихся символов, подлежащих сканировании 2 VARIABLE ПО-ГОРИЗОНТАЛИ \ текущее положение курсора для вывода по 3 \ горизонтали 4 70 CONSTANT ПРАВГРАН \ правая граница 5 : СКАНЕР ( длина-поиска -- адр-пробела | конец-поля) 6 2DUP + ROT ROT OVER + SWAP ВО 1 С@ BL = 7 IF DROP I LEAVE THEN LOOP ; 8 : ДАЙСЛОВО ( a -- a слово-счетчик) \ получ.
очереди, слова для форматирования 9 DUP ОСТАТОК @ BUP 0> IF СКАНЕР ELSE DROP THEN OVER - 10 DUP 1+ NEGATE ОСТАТОК +! ; 11 : СЮДА? ( счетчик -- 1=подходит для данной строки) 12 ПО-ГОРИЭОНТАЛИ @ + ПРАВГРАН < ; 13 : SPACE' ПО-ГОРИЗОНТАЛИ @ IF SPACE 1 ПО-ГОРИЭОНТАЛИ +! THEN ; 14 : CR' CR 0 ПО-ГОРИЗОНТАЛИ ! ; 15 157 LOAD 158 LOAD

Block # 157 0 ( Генератор бессмысленных выражений, вариант с самоформатированием ) 1 : .СЛОВО ( а # -- ) \ вывод слова, если необходимо, выполнение CR 2 DUP СЮДА? IF SPACE' ELSE CR' THEN 3 DUP ПО-ГОРИЗОНТАЛИ +! TYPE ; 4 : СЛЕДУЮЩЕЕ ( a # -- след.адр.) 5 + 1+ ; 6 : ВЫВОД ( а #символов -- ) \ вывод сформатированного текста 7 ОСТАТОК ! 8 BEGIN ДАЙСЛОВО DUP WHILE 2DUP .СЛОВО СЛЕДУЮЩЕЕ REPEAT 9 2DROP ; 10 : АБРЕД ( -- а) 161 BLOCK ; \ случайные слова 11 : ОБОРОТЫ ( -- а) 160 BLOCK ; \ связывающие обороты 12 : ВВЕДЕНИЯ ( - a) 1S9 BLOCK ; \ начала фраз 13 : БРЕД ( #строки #столбца -- а) \ получение адреса слова 14 20 * SWAP 64 * + АБРЕД + ; 15

Block # 158 0 ( Генератор бессмысленных выражений, вариант с самоформатированием ) 1 : .БРЕД ( #строки #столбца -- ) ВРЕД 20 ВЫВОД ; 2 : ЧАСТЬ-РЕЧИ ( #строки -- ) CREATE , \ определение частей речи 3 DOES> @ 10 CHOOSE SWAP .БРЕД ; 4 0 ЧАСТЬ-РЕЧИ 1ПРИЛАГАТЕЛЬНОЕ 5 1 ЧАСТЬ-РЕЧИ 2ПРИЛДГАТЕЛЬНОЕ 6 2 ЧАСТЬ-РЕЧИ СУЩЕСТВИТЕЛЬНОЕ 7 : ФРАЗА 1ПРИЛАГАТЕЛЬНОЕ 2ПРИЛАГАТЕЛЬНОЕ СУЩЕСТВИТЕЛЬНОЕ ; 8 : ОБОРОТ ( #группы -- ) [ 4 64 * ] LITERAL * 9 3 CHOOSE 64 * + ОБОРОТЫ + 64 ВЫВОД ; 10 : ПОВЕСТВОВАНИЕ 4 0 DO I ОБОРОТ ФРАЗА LOOP ." ." CR' ; 11 : ВВЕДЕНИЕ ( #абзаца -- ) 12 CR' 64 * ВВЕДЕНИЯ + 64 ВЫВОД ; 13 : СООБЩЕНИЕ CR' CR' 4 0 DO I ВВЕДЕНИЕ ПОВЕСТВОВАНИЕ LOOP ; 14 15

Block # 159 0 В ДАННОМ СООБЩЕНИИ МЫ РАССКАЖЕМ ВАМ О ТОМ, ЧТО 1 С ОДНОЙ СТОРОНЫ, ИССЛЕДОВАНИЯ ПОКАЗАЛИ, ЧТО 2 С ДРУГОЕ СТОРОНЫ, ТЕМ НЕ МЕНЕЕ, ПРАКТИЧЕСКИЙ ОПЫТ ПОКАЗЫВАЕТ, ЧТО 3 В РЕЗУЛЬТАТЕ НАШЕ ПРЕДЛОЖЕНИЕ ЗАКЛЮЧАЕТСЯ В ТОМ, ЧТО 4 5 6 7 8 9 10 11 12 13 14 15

Block # 160 0 ПРИМЕНЯЯ 1 ВКЛАДЫВАЯ ИМЕЮЩИЕСЯ В НАЛИЧИИ СРЕДСТВА В 2 СТРУКТУРИРОВАННО ПРИМЕНЯЯ 3 4 ИМЕЯ В ВИДУ 5 ЧТОБЫ КОМПЕНСИРОВАТЬ 6 УЧИТЫВАЯ 7 8 ПРЕДСТАВЛЯЕТСЯ ВОЗМОЖНЫМ ДАЖЕ НЕСМОТРЯ НА 9 СТАНОВИТСЯ НЕОСУЩЕСТВИМЫМ ПОДНИМАЯ 10 НЕОБХОДИМО РАССМАТРИВАЯ 11 12 ФУНКЦИОНИРОВАТЬ КАК 13 СОЗДАТЬ 14 ЕЩЕ БОЛЬШЕ КРЕПИТЬ 15

Block # 161 0 ВЫСОКИЙ КУЛЬТУРНЫЙ УРОВЕНЬ 1 ОБЩИЙ ПРОИЗВОДСТВЕННЫЙ ИНТЕРЕС 2 АВТОМАТИЗИРОВАННЫЙ НАУКОЕМКИЙ КОМПЛЕКС 3 ЗАПЛАНИРОВАННЫЙ ВАЛОВОЙ ОБЪЕМ 4 ИНТЕГРИРОВАННЫЙ ЦИФРОВОЙ КОЭФФИЦИЕНТ 5 КВАЛИФИЦИРОВАННЫЙ МНОГООТРАСЛЕВОЙ ПРИНЦИП 6 ПРЕДСТАВИТЕЛЬНЫЙ ХИМИЧЕСКИЙ ГЕНЕРАТОР 7 ТЕХНОЛОГИЧЕСКИЙ НЕПРЕРЫВНЫЙ ПРОЦЕСС 8 АВТОНОМНЫЙ АППАРАТНЫЙ ИНТЕРФЕЙС 9 ЦИФРОВОЙ НЕЗАВИСИМЫЙ АВТОМАТ 10 СИНХРОНИЗИРОВАННЫЙ ФУНКЦИОНАЛЬНЫЙ КРИТЕРИЙ 11 СИСТЕМАТИЗИРОВАННЫЙ КОРОТКОВОЛНОВОЙ ПРОЕКТ 12 СЛУЧАЙНЫЙ ОТРИЦАТЕЛЬНЫЙ ИМПУЛЬС 13 НЕОБЫЧАЙНЫЙ ТРУДОВОЙ ПОДЪЕМ 14 ВРЕМЕННЫЙ НЕХАРАКТЕРНЫЙ СПАД 15 УНИКАЛЬНЫЙ ПРОГРАММНЫЙ ПРОДУКТ


ВЕКТОРНЫЕ ВЫЧИСЛЕНИЯ


Векторное вычисление, хотя и воспринимается как нечто трудоемкое, на самом деле осуществляется довольно просто. Вместо того чтобы выполнять какое-либо определение непосредственно (см. выражение ' ВСТРЕЧА EXECUTE), мы выполняем его косвенно, запоминая адрес определения в некоторой переменной, а затем выполняя содержимое этой переменной:VARIABLE УКАЗАТЕЛЬ ( участок для хранения исполнительного вектора) ' ВСТРЕЧА УКАЗАТЕЛЬ ! ( указатель ссылается на слово ВСТРЕЧА ) УКАЗАТЕЛЬ @ EXECUTE ( выполнение фрагмента, на который ссылается УКАЗАТЕЛЬ).

(Для вычисления адреса, который засылается в УКАЗАТЕЛЬ, примените соответствующее выражение из столбца 1 приведенной выше таблицы). В ряде систем есть слово @EXECUTE, которое эквивалентно выражению "@  EXECUTE", но выполняется более эффективно.

Вы Можете попытаться сделать следующий пример самостоятельно: 1 VARIABLE 'ФРАЗА ( вектор) 2 : ФРАЗА 'ФРАЗА @ EXECUTE ; ( векторизуемое определение) 3 : ПРИВЕТ ." Привет " ; 4 : ДО-СВИДАНИЯ ." До свидания " ; 5

6 ' ПРИВЕТ 'ФРАЗА ! ( инициализация вектора)

В строке 1 вы определяете переменную с именем 'ФРАЗА. Она будет вашим указателем. В строке 2 специфицируется слово ФРАЗА для выполнения определения, адрес которого находится в указателе 'ФРАЗА. В строках 3 и 4 создаются слова, выводящие сообщения: «Привет» и «До свидания». В строке 6 вы загружаете адрес слова ПРИВЕТ в указатель 'ФРАЗА по правилам, изложенным в столбце 1 вышеприведенной таблицы.

Теперь если выполнить слово ФРАЗА, то получится следующее: ФРАЗА Привет ok

Если вы альтернативно выполните выражение ' ДО-СВИДАНИЯ 'ФРАЗА !

тем самым запомнив адрес ДО-СВИДАНИЯ в переменной 'ФРАЗА, то результат будет таким:

ФРАЗА До свидания

ok

Апостроф в имени ' ФРАЗА означает, что данное имя является указателем при векторном вычислении - таковы соглашения в Форте. Так как апостроф при выполнении выдает адрес, то префикс соответствует адресу слова ФРАЗА.

Мы можем заставить само слово ФРАЗА выполнять все, что захотим, даже слова (ПРИВЕТ и ДО-СВИДАНИЯ), которые определены после слова ФРАЗА.
Следовательно, апостроф обеспечивает один из способов реализации ссылок вперед. Ссылки вперед возникают в тех случаях, когда при определении некоторого слова приходится обращаться к другому слову, еще не определенному. В Форте нет естественных средств для программирования такой ситуации, да и в большинстве ситуаций вы можете просто по-иному расположить загружаемые определения. Но иногда переупорядочивание определений невозможно и вам без ссылок вперед не обойтись. В нашем примере на строке 6 происходит завершение реализации ссылки вперед.

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


ВЛОЖЕННЫЕ ЦИКЛЫ


Ранее мы определили слово с именем ПРОИЗВЕДЕНИЯ, в котором содержался цикл DO. При желании можно было бы поместить слово ПРОИЗВЕДЕНИЯ внутрь другого цикла DO, например: : ТАБЛИЦА CR 11 1 DO I ПРОИЗВЕДЕНИЯ LOOP ;

В результате мы получили бы таблицу умножения в таком виде: 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 и т.д. 10 20 30 40 50 60 70 80 90 100

так как I во внешнем цикле обеспечивает аргумент для слова ПРОИЗВЕДЕНИЯ.

Циклы тоже можно вкладывать один в другой только в пределах одного определения: : ТАБЛИЦА CR 11 1 DO 11 1 DO I J * 4 .R LOOP CR LOOP ;

Обращаем ваше внимание на тот случай, когда во внутреннем цикле находится фрагмент I J *

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

Что можно сказать теперь о выражении 4 .R

Это всего лишь видоизмененный оператор, который используется для печати чисел в табличной форме с выравниванием их по вертикали. Цифра 4 определяет заданное число позиций в столбце. Новую таблицу можно теперь распечатать так: 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 18 21 24 27 30 и. д.

Под каждое число выделено четыре позиции независимо от того, сколько цифр содержится в данном числе. Слово .R означает «вывод числа, выравненного вправо».

+LOOP

Если вы хотите, чтобы значение индекса на каждом шаге выполнения цикла изменялось не на единицу, а на некоторую другую величину, то вместо LOOP можно использовать слово +LOOP. Для этого слова в стеке должно находиться число, значение которого является приращением индекса на каждом шаге. Например, в определении : ЧЕРЕЗ-ПЯТЬ 50 0 DO I . 5 +LOOP ;

индекс всякий раз будет увеличиваться на пять, что приведет к следующему результату:ЧЕРЕЗ-ПЯТЬ 0 5 10 15 20 25 3В 35 40 45 ок

(Представляете, о каких гигантских шагах идет речь.)

Отрицательное приращение заставляет цикл выполняться в обратном направлении, например:: СНИЖЕНИЕ -10 0 DO I . -1 +LOQP ;


дает такой результат: СНИЖЕНИЕ 0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 ок

Слово +LOOP начнет с нуля и с шагом, равным -1, «пойдет» к финишной черте, пока не пересечет ее. Заметьте, что в этом направлении мы выполняем цикл фактически 11 раз, поскольку финишная черта всегда лежит между значением границы и ее значением  -1 независимо от того, в какую сторону перемещается +LOOP. Завершение же цикла вызывает переход финишной черты.



Приращение может быть получено каким угодно образом, но на каждом шаге выполнения оно должно находиться в стеке. Рассмотрим пример: : ПРИРАЩЕНИЕ ( приращение граница индекс -- ) DO I . DUP +LOOP DROP ;

Внутри этого определения непосредственно нет приращения. Оно будет взято из стека при выполнении слова ПРИРАЩЕНИЕ наряду с границей и индексом. Посмотрите, что происходит в данном случае: 1 5 0 ПРИРАЩЕНИЕ 0 1 2 3 4 ок 2 5 0 ПРИРАЩЕНИЕ 0 2 4 ок -3 -10 10 ПРИРАЩЕНИЕ 10 7 4 1 -2 -5 -8 ок

Наш следующий пример демонстрирует изменение приращения на каждом шаге выполнения цикла: : УДВАИВАНИЕ CR 32767 1 DO I . I +LODP ;

Здесь индекс непосредственно используется в качестве приращения. Начиная с единицы он всякий раз удваивается, как это показано ниже: УДВАИВАНИЕ

1 2 4 8 16 32 64 128 256 512 1024 2048 4096 8192 16384 ок


Если бы в этом примере аргумент для +LOOP принял хотя бы один раз значение 0, то мы никогда не вышли бы из цикла — получился бы так называемый бесконечный цикл1.



1 Для специалистов. Некоторые Форт-системы, созданные до принятия Стан-дарта-83, включают /LOOP, которое, как и +LOOP, выбирает из стека приращение, но оно должно быть положительным. Это может привести к тому, что индекс превысит значение 32767, скажем, при индексации по адресам или номерам блоков, так как указанные числа принимают только положительные значения в отличие от тех ситуаций, когда выполняются операции над числами со знаком. Слово 4- LOOP, удовлетворяющее Стандарту-83, снимает данную проблему.


ВЛОЖЕННЫЕ КОНСТРУКЦИИ IF...THEN


Имеется возможность помещать оператор IF ... THEN (или IF ... ELSE ... THEN) внутрь другого оператора IF ... THEN. Фактически вы можете создать оператор любой степени вложенности при условии, что каждый оператор IF будет иметь соответствующий оператор THEN.

1 Для специалистов. В Форте нет оператора GOTO. Если вы не можете обойтись без этого оператора немного подождите. К концу книги вам станут ясны отрицательные последствия его буквального применения. Вместо GOTO мы предложим вам средства, позволяющие сделать ваши программы более корректными.

2 Для специалистов. Как вы увидите ниже, существуют лучшие способы выполнения этих действий.

Рассмотрим следующее слово, которое определяет сорт яиц для продажи в зависимости от их размера (очень крупные, крупные и т. д.), по весу в унциях1 на дюжину2: : РАЗМЕР-ЯИЦ ( унций-на-дюжину -- ) DUР 18 < IF . " Брак " ELSE DUP 21 < IF ." Мелкие " ELSE DUP 24 < IF ." Средние " ELSE DUP 27 < IF ." Крупные " ELSE DUP 30 < IF ." Очень крупные " ELSE ." Ошибка " THEN THEN THEN THEN THEN DROP ;

Загрузив однажды слово РАЗМЕР-ЯИЦ, вы можете получать различные результаты, например3: 23 РАЗМЕР-ЯИЦ Средние ок 29 РАЗМЕР-ЯИЦ Очень крупные ок

40 РАЗМЕР-ЯИЦ Ошибка ок

Обратите внимание на некоторые особенности работы со словом РАЗМЕР-ЯИЦ. Все определение является совокупностью вложенных операторов IF ... THEN. Эти операторы вложены друг в друга, как матрешки. Пять слов THEN, расположенных внизу, соответствуют пяти словам IF, расположенным в обратном порядке:

Отметим также, что в конце определения должно быть слово DROP, которое позволит избавиться от лишнего исходного значения.

1 1 унция = 28,3 г. — Примеч. пер.

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

3 Для любознательных. Ниже приводится официальная таблица, согласно которой производится определение сорта яиц:

очень крупные 27—30 крупные 24—27 средние 21—24 мелкие 18—21

Можно ли удалить последний оператор DUР с тем, чтобы число убиралось из стека последней операцией сравнении, и оператор DROP? Нет, а почему?

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



ВЛОЖЕННЫЕ УРОВНИ ВЫЧИСЛЕНИИ


Функция слова EXIT заключается в том, чтобы возобновить процесс вычислений в определении более высокого уровня (по порядку вложенности), из которого была сделана ссылка на текущее определение. Рассмотрим упрощенную схему работы этого механизма. Предположим, что ОБЕД состоит из трех блюд: : ОБЕД ПЕРВОЕ ВТОРОЕ ДЕСЕРТ ;

причем сегодня на ВТОРОЕ подается только : ВТОРОЕ ЦЫПЛЕНОК РИС ;

Мы находимся на стадии выполнения слова ОБЕД и только что покончили с блюдом ПЕРВОЕ. Указатель, которым пользуется адресный интерпретатор, называется указателем интерпретатора (I). Так как за блюдом ПЕРВОЕ следует ВТОРОЕ, наш указатель интерпретатора указывает ячейку, содержащую адрес слова ВТОРОЕ. Прежде чем перейти к выполнению слова ВТОРОЕ, увеличим значение указателя интерпретатора настолько, чтобы при возврате он указывали бы на ДЕСЕРТ.

Теперь приступим к выполнению слова ВТОРОЕ и начнем с выполнения кода слова ВТОРОЕ, т. е. кода, на который указывает поле кода и который является общим для всех определений через двоеточие. Этот код выполняет две функции: вносит содержимое указателя интерпретатора в стек возвратов, а затем помещает адрес своего поля параметров (pfa) в указатель интерпретатора. Теперь указатель интерпретатора отсылает нас к слову ЦЫПЛЕНОК.

Итак, интерпретатор адреса готов приняться за цыпленка. Однако прежде, как и в случае со словом ВТОРОЕ, увеличим указатель настолько, чтобы он при возврате показывал на РИС. После этого код слова ЦЫПЛЕНОК заносит указатель в стек возвратов и помещает pfa слова ЦЫПЛЕНОК в указатель интерпретатора.

По мере того как мы наслаждаемся нашим аппетитным цыпленком, последовательно исполняется определение, его составляющие. Рано или поздно, обрабатывая слово ЦЫПЛЕНОК, мы подойдем к EXIT. Слово EXIT берет число из вершины стека возвратов и вносит его в указатель интерпретатора. Далее интерпретатор адреса продолжает процесс, выполняя слово РИС, покончив с последним. Несомненно, в конце концов, EXIT в слове ВТОРОЕ поместит значение из стека возвратов в указатель интерпретатора, и мы с вами созреем для десерта.



ВОЗМОЖНОСТИ МАСШТАБИРОВАНИЯ


Решим простую задачу, допустим, что нужно вычислить 2/з от 171. По существу, есть два способа нахождения результата:

1 Для специалистов Существует и более быстрое определение

: R% ( n % -- результат ) 50 */ 1+ 2/ ;

1. Определить значение дроби 2/3 путем деления числа 2 на число 3. При этом мы получим периодическую десятичную дробь, .666666... Далее можно умножить полученное значение на 171. Результат составит 113.9999999 и т. д., что не совсем точно, но он может быть округлен до 114.

2. Перемножить 171 и 2, а затем разделить полученное число 342 на 3, что дает в результате 114.

Заметим, что второй способ проще и намного точнее

Большинство машинных языков поддерживает первый способ. В компьютере не могут храниться такие дроби, как 2/3, на всякий случай. Их нужно выражать в виде десятичной дроби, например .666666...

Форт поддерживает второй способ. Операция */ позволяет вам получать дроби, аналогичные 2/3, как в следующем примере:

171 2 3 */

Теперь, когда вы имеете некоторое представление о масштабировании, рассмотрим несколько более сложный пример. Допустим, требуется разделить 150 дол. в заданной пропорции :7.105 ?

5.145 ? ------ --- 12.250 150

Эту задачу можно решить так: (7.105/12.250) х 150 и (5.145/12.250) х 150

Однако для обеспечения большей точности мы должны написать (7.105 х 150)/12.250 и (5.145 х 150)/12.250

На Форте это будет выглядеть следующим образом: 7105 150 12250 */ . 87 ok

и 5145 150 12250 */ . 63 ok

Мы можем сказать, что значения 87 и 63 выбраны в «масштабе» к 7105 и 5145. Вычисление процентов, выполненное нами ранее, также представляет собой форму масштабирования. По этой причине операция */ называется операцией масштабирования.

Еще одной операцией масштабирования является операция */MOD:*/MOD ( n1 n2 nЗ -- Умножение, затем деление (n1*n2/n3). n-остаток n-результат ) Помещение на стек остатка и частного. Для промежуточного результата используется слово двойной длины.

Самостоятельно придумайте хороший пример на выполнение операции */MOD



ВВЕДЕНИЕ В БЛОК-СХЕМЫ ФОРТА


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

Рассматриваемые здесь диаграммы основаны на так называемых D-схемах. Последовательные операторы записывают один под другим, не соединяя их линиями и не заключая в рамки: оператор следующий оператор следующий оператор

Линии же служат для того, чтобы показать, что действия выполняются не в порядке очередности, а либо в зависимости от некоторого условия (условные операторы), либо неоднократно (операторы цикла). Условный оператор Форта условие IF истина ELSE ложь THEN оператор

изображается следующей диаграммой:

Если выражение в какой-либо альтернативной ветви опущено, то в этом месте проводится сплошная вертикальная линия: Расположение истинной и ложной ветвей (слева и справа) не имеет значения.

Структура BEGIN ... UNTIL изображается так:

Циклическая структура располагается справа от основной линии вычислений и соединяется с ней горизонтальной линией в верхней своей части. Если нужно показать вложенные циклы, то они должны быть расположены правее. Черная точка является признаком конца цикла. Она означает, что управление

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

Цикл вида BEGIN ... WHILE ... REPEAT имеет аналогичную диаграмму:

Итак, мы кратко осветили вопрос о применении блок-схем при программировании на Форте и теперь можем наглядно представить вам структуру двух очень важных слов.



ВВОД ИЗ ВХОДНОГО ПОТОКА


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

Предположим, что пользователь хотел бы иметь возможность задавать свое имя, используя слово Я, например: Я BACЯ<return>

Пользователь должен набрать фрагмент Я ВАСЯ на одной строке, а затем нажать клавишу RETURN, Нам нужно, чтобы слово Я помещало имя пользователя в массив ИМЯ-ПОЛЬЗОВАТЕЛЯ. Но фрагмент ВАСЯ находится впереди по входному потоку и слово Я, следовательно, не может «ожидать» его с помощью EXPECT, так как он уже введен. Вместо этого необходимо найти средство для чтения опережающего слова.

Таким средством является слово WORD (СЛОВО). Оно сканирует входной поток в поисках фрагмента текста, ограниченного символом, код ASCII которого хранится в вершине стека. Например, выражение BL WORD

будет просматривать входной поток в поисках фрагмента текста, ограниченного пробелами. Найденную подстроку WORD поместит в свой собственный временный буфер вместе со счетчиком символов в первом байте буфера. В Форте строка символов, предваряемая одним байтом, в котором содержится число символов данной строки, называется строкой со счетчиком. Затем WORD вносит в вершину стека адрес своего временного буфера1.

Слово WORD - важный элемент текстового интерпретатора Форта, в котором выражение BL WORD применяется для сканирования входного потока в поисках слов и чисел.

Пересылая фрагмент текста, WORD дополняет его в конце пробелом, но этот пробел не учитывается в счетчике. Вы можете создать определение Я следующим образом (используя массив, созданный ранее для слова ВСТРЕЧА): : Я ( имя-пользователя ( -- ) ИМЯ-ПОЛЬЗОВАТЕЛЯ 40 BLANK BL WORD COUNT ИМЯ-ПОЛЬЗОВАТЕЛЯ SWAP CMOVE ;

Чтобы видеть вводимое имя, можно воспользоваться ранее определенным словом .ПОЛЬЗОВАТЕЛЬ.


Каковы функции слова COUNT? Это слово разделяет адрес строки со счетчиком на адрес и счетчик. Полученный адрес указывает начало текста, а не байт со счетчиком. Например, при заданном в вершине стека адресе строки со счетчиком ПРИВЕТ



1 Для пользователей систем фиг-Форта. Ваша версия слова WORD ничего в вершине стека не оставляет. Для обеспечения совместимости с текстом рассматриваемого примера переопределите его:

: WORD ( -- a) WORD HERE ;

слово COUNT заносит в стек счетчик, увеличивает адрес:



и в вершине стека остаются адрес строки и значение счетчика, которые могут служить аргументами для слов TYPE, CMOVE и т. д.

В нашем определении Я слово WORD оставляет в вершине стека адрес строки со счетчиком, а слово COUNT разделяет этот адрес на адрес и счетчик. Слово АДРЕС-ПОЛЬЗОВАТЕЛЯ обеспечивает адрес назначения. Слово SWAP расставляет аргументы в требуемом порядке (источник - получатель - счетчик) для CMOVE.

Обратите внимание на необычную стековую нотацию слова Я. По существующему соглашению описание, предшествующее стековому комментарию, указывает фрагмент, поиск которого во входном потоке осуществляется в данном определении. Может применяться также символ «\ »: : Я \ имя-пользователя ( -- ) ИМЯ-ПОЛЬЗОВАТЕЛЯ 40 BLANK BL WORD COUNT ИМЯ-ПОЛЬЗОВАТЕЛЯ SWAP CMOVE ;

Отметим две особенности слова WORD. Первая особенность связана с тем, что, поскольку это слово используется текстовым интерпретатором Форта, найденный им фрагмент будет затерт при чтении следующего фрагмента из входного потока. Введите выражение BL WORD HI COUNT TYPE

Выражение BL WORD прочитает фрагмент HI и поместит его во временный буфер, но во время интерпретации слова COUNT фрагмент HI будет затерт фрагментом COUNT и в первом байте окажется значение счетчика 5, а не 2. При выполнении слова COUNT в вершину стека заносится значение счетчика 5. Наконец, при интерпретации слова TYPE фрагмент TYPE затрет предыдущий и выведется на экран (включая пробел пятым символом).

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



Другая особенность слова WORD состоит в том, что оно не воспринимает начальные вхождения символа-ограничителя. Если

в начале некоторого фрагмента набраны пробелы или если слова разделены пробелами, то выражение BL WORD осуществляет поиск до первого значащего символа и считывает фрагмент до первого пробела. Помещаются во временный буфер и учитываются в счетчике только значащие символы. Указанная особенность может вызывать затруднения при работе со словом WORD. Например, в гл. 3 было введено слово .(, обеспечивающее непосредственный вывод на экран очередного фрагмента из входного потока до правой круглой скобки. Это слово может быть определено следующим образом:: .( \ текст) ( -- ) ASCII ) WORD COUNT TYPE ;

Комментарий означает, что в данном определении фрагмент будет считываться до правой круглой скобки «)». Но в таком определении не предусмотрена ситуация с пустой строкой (без символов): .() CR CR

Наше определение не воспримет правую круглую скобку, поскольку она является первым просматриваемым символом, а посчитает фрагмент CR CR за строку, которая должна быть выведена на экран. Для подобных ситуаций в ряде Форт-систем имеется слово PARSE (РАЗБОР), функционирующее аналогично слову WORD, но воспринимающее начальные вхождения символа-ограничителя. Помните, что PARSE оставляет в вершине стека адрес строки и значение счетчика, а не адрес строки со счетчиком, как это делает WORD.

Ниже приводится слово, которым вы можете воспользоваться: : TEXT ( с) PAD 80 BLANK WORD COUNT PAD SWAP CMOVE> ;

Подобно WORD, слово TEXT выбирает из стека символ-ограничитель и сканирует входной поток до тех пор, пока из него не будет считан фрагмент, ограниченный этим символом. Затем фрагмент помещается в рабочую область (PAD). Отличительной чертой TEXT является то, что рабочая область перед занесением строки" заполняется пробелами, что облегчает выполнение слов TYPE и

-TRALLING.

WORD

( с -- а)

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

COUNT

( a -- a+1 #)

Преобразование адреса строки со счетчиком (длина которой находится в первом строки) в формат, соответствующий использования словом TYPE а именно: в стек заносится адрес начала текста строки и ее длина.


ВВОД С КЛАВИАТУРЫ


Слово KEY (КЛАВИША) ожидает, пока вы нажмете какую-либо клавишу на панели терминала и оставляет в вершине стека в младшем по порядку байте эквивалент символа, соответствующего этой клавише, в коде ASCII.

Наберите на клавиатуре: KEY<return>

Курсор продвинется на одну позицию, но «ok» на терминале не высветится: система ждет ввода вашего символа. Нажмите, к примеру, клавишу А и Форт-система ответит вам: «ok». Теперь в вершине стека находится значение литеры А в коде ASCII, поэтому введите .<return> 65 ok

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

Вы можете также включить KEY в состав определения. При встрече слова KEY выполнение данного определения приостановится до тех пор, пока не будет введен некоторый символ. Например, следующее определение выводит на печать по порядку заданное число блоков, начиная с текущего, но, прежде чем приступить к распечатке очередного блока, ожидает нажатия любой клавиши: : БЛОКИ ( # -- ) SCR @ + SCR @ DO I LIST KEY DROP LOOP ;

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

В ряде систем имеется нестандартное слово с именем KEY? (в более ранних системах ?TERMINAL), которое помещает в вершину стека значение истины при нажатии на одну из клавиш, не останавливая вычисления и не ожидая ввода самого символа.

Допустим, вы выполняете бесконечный цикл на прибавление единицы: : БЕСКОНЕЧНЫЙ 0 BESIN DUP . 1+ FALSE UNTIL DROP ;

Можно организовать выход из такого цикла, заменив слово FALSE на KEY?: : СКАЖИ-КОГДА 0 BEBIN DUP . 1+ KEY? UNTIL DROP KEY DROP ;

Слово KEY? не считывает значение символа, соответствующего нажатой клавише, а лишь сигнализирует о том, что одна из клавиш была нажата. Чтобы считать ее значение, вы должны обратиться к слову KEY. В этом месте вы можете либо определить значение нажатой клавиши (при необходимости), либо просто выполнить выражение KEY DROP. (Во многих системах, где есть этап предварительного чтения в буфер, невозможность применять связку KEY KEY?, по существу, сводит на нет описываемый универсальный прием и вынуждает прибегать к системно-зависимым «ухищрениям»).


Если слово KEY ожидает ввода одного символа, то EXPECT (ОЖИДАТЬ) ожидает ввода с клавиатуры целой строки. На самом деле это адекватно использованию KEY в цикле. Цикл заканчивается по достижении заданного числа нажатий клавиш (обычно 80) или при нажатии клавиши возврата каретки. Кроме того, слово EXPECT способно распознать значение клавиши «Забой» и возвратить назад как курсор, так и внутренний указатель слова. С помощью EXPECT Форт-система ожидает ввода вашей команды.





EXPECT выбирает из стека два аргумента: адрес, по которому нужно запомнить вводимый текст, и максимальное значение счетчика. Например, выражение TIB 80 EXPECT

ожидает ввода до 80 символов или нажатия клавиши RETURN и после завершения набора помещает введенный текст в буфер входного текста. Приведенное выражение содержится в определении слова QUERY (ЗАПРОС), используемое, как было показано выше, словом QUIT.

С помощью EXPECT вы можете сделать запрос на ввод из определения1. Ниже дается слово, которое при своем выполнении запрашивает имя пользователя, а затем выводит введенное имя вместе с приветствием:CREATE ИМЯ-ПОЛЬЗОВАТЕЛЯ 40 ALLOT : .ПОЛЬЗОВАТЕЛЬ ИМЯ-ПОЛЬЗОВАТЕЛЯ 40 -TRAILING TYPE ; : ПОЛУЧЕНИЕ-ИМЕНИ ИМЯ-ПОЛЬЗОВАТЕЛЯ 40 BLANK ИМЯ-ПОЛЬЗОВАТЕЛЯ 40 EXPECT ; : ВСТРЕЧА CR ." Пожалуйста, введите свое имя: " ПОЛУЧЕНИЕ-ИМЕНИ CR ." Привет, " .ПОЛЬЗОВАТЕЛЬ ." , Я говорю на Форте." ;

В результате вы получаете2:ВСТРЕЧА

Пожалуйста, введите свое имя:
ВАСЯ Привет, ВАСЯ, Я говорю на Форте.

1 для специалистов. Вы можете использовать EXPECT для снятия данных с последовательной шины, например, некоторого измерительного устройства. Так как у вас задействованы адрес и счетчик, такие данные могут быть считаны непосредственно в массив. Если вы являетесь единственным пользованием системы, ю перед записью на диск можете считать данные в буфер. В случае же мультизадачной системы вы должны применить TIB и уже потом пересылать данные в указанный буфер, поскольку «вашим» буфером может воспользоваться другая задача.



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

-TRAILING здесь воспринимает нуль как невыводимый на печать символ и при его выводе печатается пробел. Во избежание этого нужно ввести текст посредством EXPECT в рабочую область (PAD), после чего скопировать его, используя SPAN, в слово ИМЯ-ПОЛЬЗОВАТЕЛЯ с требуемым числом символов:

: ПОЛУЧЕНИЕ-ИМЕНИ ИМЯ-ПОЛЬЗОВАТЕЛЯ 40 BLANK PAD 40 EXPECT PAD ИМЯ-ПОЛЬЗОВАТЕЛЯ SPAN @ CMOVE ;



SPAN - пользовательская переменная, в которой содержится фактическое число символов, полученных словом EXPECT,

KEY

( -- с)

Занесение на стек значения в коде ASCII очередного доступного символа на текущем устройстве ввода.

EXPECT

( а u --)

Ожидание и символов (или нажатий клавиши RETURN) с клавиатуры и запоминание их в участок памяти, начинающийся с адреса a и продолжавшийся сторону увеличения адресов. На нажатие клавиши ЗАБОЙ осуществляется возврат курсора.

SPAN

( -- a)

Содержится количество символов, полученных, словом EXPECT


ВЫЧЛЕНЕНИЕ ОПРЕДЕЛЕНИЙ


Рассмотрим теперь проблему разбиения применительно к определениям Форта. Мы только что привели пример, в котором разбиение определений упростило нам решение задачи.

Наше первое определение слова РАЗМЕР-ЯИЦ (см. гл. 4) сортировало яйца по весу и выводило на печать соответствующие категории яиц. В предыдущем примере мы разбили «сортировку» и «печать» на два отдельных слова. Вы можете использовать слово КАТЕГОРИЯ с целью выработки аргумента как для слова, инициирующего печать, так и для слова, осуществляющего подсчет (или для обоих вместе). Можно применить слово МАРКИРОВКА, обеспечивающее вывод на печать, и для слова РАЗМЕР-ЯИЦ, и для слова СВОДКА.

Ч. Мур, автор языка Форта, утверждает, что «хороший словарь Форта содержит большое число небольших определений. Недостаточно разбить некоторую задачу на небольшие фрагменты. Суть дела в том, чтобы выделить слова, которые можно повторно использовать». Например, в следующем рецепте Взять банку с томатным соусом. Открыть ее. Выложить томатный соус на сковороду. Взять банку с грибами Открыть ее. Выложить грибы на сковороду

вы можете «вычленить» действия: взять, открыть, выложить и объединить их в одном месте, так как они являются общими по отношению и к банке с томатным соусом, и к банке с грибами. После этого вы можете присвоить процессу в целом имя и в дальнейшем просто писать: ТОМАТ ДОБАВИТЬ ГРИБЫ ДОБАВИТЬ

и любой шеф-повар, окончивший «постфиксную» кулинарную школу, хорошо поймет, что вы имеете в виду.

Вычленение определений не только упрощает написание программы (и ее отладку), но и позволяет экономить память. Повторно используемое слово, например добавить, нужно определить только один раз. Чем сложнее программа, тем больше мы экономим при ее разбиении. Прежде чем покинуть птицеферму, приведем еще одно соображение по поводу стиля программирования на Форте Вспомним наше определение слова РАЗМЕР-ЯИЦ: : РАЗМЕР-ЯИЦ ( вес-на-дюжину — ) КАТЕГОРИЯ DUP МАРКИРОВКА УЧЕТ;


Слово КАТЕГОРИЯ доставляет значение, которое нам хотелось бы передать как слову МАРКИРОВКА, так и слову УЧЕТ, поэтому мы включаем сюда операцию DUP. Чтобы сделать определение более ясным, рискнем вынести из него DUP и поместим его в самое начало определения МАРКИРОВКА. Таким образом, можно написать: : МАРКИРОВКА ( номер-категории — номер-категории) и т.д. : РАЗМЕР-ЯИЦ ( в ее-на-дюжину — ) КАТЕГОРИЯ МАРКИРОВКА УЧЕТ ;

где КАТЕГОРИЯ передает значение слову МАРКИРОВКА, а МАРКИРОВКА передает его слову УЧЕТ. Несомненно, этот вариант должен «сработать». Но впоследствии при определении СВОДКА мы вынуждены будем применить выражение I МАРКИРОВКА DROP вместо простого I МАРКИРОВКА.

Программирующим на Форте рекомендуется придерживаться следующего соглашения: там, где это возможно, слова должны уничтожать свои параметры. Вообще лучше помещать DUP в «вызывающем» определении (РАЗМЕР-ЯИЦ), чем в «вызываемом» (МАРКИРОВКА).


ВЫХОД ИЗ ЦИКЛА (LEAVE) И ВЕТВЛЕНИЕ (BRANCH)


Существует способ написания цикла со счетчиком, при котором выполнение цикла может закончиться раньше, чем будет достигнуто заданное значение границы. Для этого нужно с помощью слова LEAVE запрограммировать внутри цикла DO изменение условия с «истины» на «ложь». LEAVE заставляет цикл немедленно завершиться1.

Перепишем наше прежнее определение слова СЛОЖНЫЕ-ПРОЦЕНТЫ. Теперь мы не будем запускать цикл ровно 20 раз, а организуем завершение этого цикла либо после его 20-го выполнения, либо после удвоения денежной суммы — в зависимости от того, какое из событий произойдет раньше.

Добавим следующую фразу: 2000 > IF LEAVE THEN

как в следующем тексте:: УДВОЕНО ( вклад процент — ) SWAP 21 1 DО CR ." Год " I 2 .R 3 SPACES 2DUP R% + DUP ." Сумма " . DUP 2000 > IF CR ." Более чем удвоено через" I . ." лет " LEAVE THEN LOOP 2DROP ;

1 Для пользователей систем, разработанных до принятия Стандарта-83. Ранее слово LEAVE вызывало завершение цикла при выполнении очередного слова LOOP или +LOOP.

В результате получим: 1000 6 УДВОЕНО

Год 1 Сумма 1060 Год 2 Сумма 1124 Год 3 Сумма 1191 Год 4 Сумма 1262 Год 5 Сумма 1338 Год 6 Сумма 1418 Год 7 Сумма 1503 Год 8 Сумма 1593 Год 9 Сумма 1689 Год 10 Сумма 1790 Год 11 Сумма 1897 Год 12 Сумма 2011

Более чем удвоено через 12 лет

В одном из упражнений в конце главы вам предлагается переработать слово УДВОЕНО таким образом, чтобы оно выбирало из стека в виде аргументов норму процента и начальную сумму и выполняло бы вычисления до получения удвоенной начальной суммы, после чего слово LEAVE завершало бы эти вычисления.

Слово LEAVE ведет себя, как и положено при использовании его во вложенных операторах цикла DO: оно просто «покидает» цикл, в котором находится. В одном цикле может употребляться несколько слов LEAVE. Цикл завершается при встрече первого из них.

Согласно Стандарту-83, любой код, размещенный после сочетания IF LEAVE THEN, не будет исполнен на последнем шаге.
Как правило, лучше применять это сочетание в качестве последнего выражения перед LOOP.

Еще одно предупреждение: как и все условные операторы, слово LEAVE должно находиться в том же определении, что и условия, по которым оно покидает цикл (DO и LOOP). Запомните, что нельзя помещать LEAVE в определении слова, вызываемого из цикла. Приведем пример неправильного использования слова LEAVE:: ВЫБОР ВАРИАНТ1 IF ВАРИАНТ2 LEAVE THEN ; : НЕПРАВИЛЬНЫЙ-ЦИКЛ 1000 0 DO ВЫЧИСЛЕНИЯ ВЫБОР LOOP ;

Здесь LEAVE используется вне цикла. Эти определения будут скомпилированы, но при выполнении приведут к непредсказуемому результату (возможно, даже к разрушению системы).

Два полезных приема. PAGE и QUIT. Для того чтобы придать более аккуратный вид данным, выводимым циклически (таким как таблицы и геометрические фигуры), перед выводом информации вам, возможно, придется очистить экран с помощью слова PAGE (СТРАНИЦА). Вы можете использовать слово PAGE непосредственно всякий раз, когда нужно очистить экран

PAGE ПРЯМОУГОЛЬНИК

При этом экран будет очищаться перед выводом прямоугольника, который вы определили ранее. А можно поместить слово PAGE один раз в начало определения:: ПРЯМОУГОЛЬНИК РAGЕ 256 0 DO I 16 MOD 0= IF CR THEN ." *" LOOP ;

Если вы не хотите, чтобы по завершении вычисления на экране появилось приглашение ok, примените слово QUIT (ВЫЙТИ). Вы можете использовать QUIT непосредственно:

ПРЯМОУГОЛЬНИК QUIT

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

Ниже дается перечень слов Форта, приводимых в настоящей главе. DO ... LOOP DO: ( граница Организация цикла со счетчиком по задан- индекс -- ) ному диапазону индексов. LOOP: ( -- )

DO ... +LOOP DO: ( граница Аналогично DO ... LOOP . Только к ин- индекс — ) дексу на каждом шаге добавляется значение +LOOP: ( n -- ) n (а не как всегда единица ).

LEAVE ( -- ) Немедленное завершение выполнения цикла LOOP или +LOOP. (Используетея только внутри цикла.)

BEGIN ... UNTIL: ( ? -- ) Организация цикла с условием, который за- UNTIL вершается, когда ? принимает значение истина.

BEGIN ххх WHILE: ( ? -- ) Организация цикла с условием, причем ххх WHILE ууу выполняется всегда, а ууу—только REPEAT если ? истинно.

.R ( u ширина- Вывод числа одинарной точности без зна- поля -- ) кa. Число выровнено справа по границе поля.

PAGE ( -- ) Чистка экрана дисплея и установка курсора в верхний левый угол.

QUIT ( -- ) Завершение выполнения текущей задачи и возврат управления на терминал.


ВЫХОД НА ВЕРХНИЙ УРОВЕНЬ


Вам, вероятно, интересно узнать, что произойдет после того, как мы исполнили последний EXIT в слове ОБЕД Чей адрес возврата находится в стеке? Куда предстоит вернуться.

Напомним, что слово ОБЕД было запущено на выполнение словом EXECUTE, которое является компонентой слова INTERPRET. Последнее осуществляет циклический процесс непрерывной проверки входного потока. Допустим, вы уже отобедали, и во входном потоке ничего нет для интерпретирования.

В этом случае при выполнении EXIT слова INTERPRET вы выходите на определение самого верхнего уровня с именем QUIT. В упрощенной форме QUIT выглядит следующим образом:: QUIT BEGIN RESET QUERY INTERPRET ." ok" CR FALSE UNTIL ; где RESET очищает стек возвратов, а QUERY настраивает входной поток на буфер входного текста.

(Определение QUIT в вашей системе может отличаться от приведенной.) Как видите, после слова INTERPRET следует сообщение точки - кавычки «ok» и CR, что и должно появиться на экране после завершения интерпретации. Далее идет выражение FALSE UNTIL, которое, безусловно, обеспечивает возврат в начало цикла, где вы очищаете стек возврата и снова ожидаете ввода.

Если исполнить QUIT на любом уровне выполнения, то немедленно прекратится вычисление по нашей программе и принудительно начнется выполнение цикла QUIT. Стек возвратов очистится (независимо от того, сколько в нем находится уровней адресов возврата, после чего вы никогда больше не сможете воспользоваться ни одним из них), а система будет ожидать ввода. Теперь вам понятно, почему слово QUIT может применяться для того, чтобы сообщение «ok» не выдавалось.

Другие способы использования QUIT. Еще два важных слова обращаются к QUIT: ABORT, которое, кроме всего прочего, очищает стек данных, и ABORT". Второе слово

выбирает из стека флаг и определяет по нему, нужно ли выполнять ABORT (при истинном значении нужно);

перед аварийным завершением выдает сообщение, предписанное вами для конкретной аварийной ситуации.

Слово ABORT" было введено в конце гл. 4. Как правило, оно применяется в начале определений пользовательских слов (наивысшего уровня) для того, чтобы проверить хотя бы наличие аргументов в стеке, требуемых для выполнения этих слов. Например, вы можете написать следующее определение: : LIST ( n -- ) ГЛУБИНА-СТЕКА 1 < ABORT" Нет номера блока " LIST ;

или в более общем виде:: АРГУМЕНТЫ ( n -- ) ГЛУБИНА-СТЕКА < NOT ABORT" Значения?" ;

что может быть использовано так: : LIST ( n -- ) 1 АРГУМЕНТЫ LIST ;



ВЫПОЛНЕНИЕ АРИФМЕТИЧЕСКИХ ОПЕРАЦИЙ


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



ВЫВОД ТЕКСТА С ДИСКА


Ранее уже отмечалось, что слово BLOCK копирует заданный блок в некоторый доступный буфер и оставляет адрес последнего в вершине стека. Приняв этот адрес за исходный, мы можем добраться посредством индексирования до каждого из 1024 байтов данного буфера и вывести любую строку. К примеру, для того чтобы вывести строку 0 блока 214, вы можете ввести CR 214 BLOCK 64 TYPE<return>

( ЭТО БЛОК 214 ) ok

Для вывода строки 8 нужно добавить к исходному адресу 512 (8x64): CR 214 BLOCK 512 + 64 TYPE

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

-TRAILING

( a u1 -- a u2)

Удаление незначащих пробелов из строки с заданным адресом путем уменьшения значения счетчика от u1 (счетчик исходных байтов) до u2 (счетчик байтов, полученных в результате вычеркивания пробелов).

>TYPE

( a # -- )

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

Слово -TRAILING, используемое непосредственно перед командой TYPE, изменяет значение счетчика таким образом, что незначащие пробелы не выводятся. Так, если вы вставите это слово в приведенный выше пример (с блоком 214), то получите:CR 214 BLOCK 64 -TRAILING TYPE<return>

( ЭТО БЛОК 214 ) ок

Это слово удаляет незначащие пробелы в конце (32 в коде ASCII), но не препятствует другим не выводимым на печать символам (например, 0 в коде ASCII).

Слово >TYPE применяется только в мультипрограммных системах для вывода строк из буферов, находящихся на диске. Вместо того чтобы непосредственно выдавать строку с заданного адреса, оно предварительно перекачивает строку целиком в рабочую область и затем выводит ее оттуда. Поскольку все пользователи разделяют одни и те же буферы, система не может гарантировать, что к тому времени, когда TYPE завершит вывод содержимого какого-то буфера, последний будет все еще хранить прежний блок.
Однако вы можете быть уверены в том, что данный буфер содержит один и тот же блок во время перекачки этого буфера в рабочую область1. Так как каждой задаче отведена своя рабочая область, >TYPE может выводить из нее информацию без риска получить не те данные.
В приведенном ниже примере используется слово TYPE, но вы при необходимости можете подставить вместо него >TYPE. В конце раздела мы покажем вам полезный прием с применением генератора случайных чисел.Block # 231 0 ( Генератор бессмысленных сообщений ) 1 : АБРЕД ( -- а) 232 BLOCK ; 2 : БРЕД ( строка# столбец# -- а) 3 20 * SWAP 64 * + АБРЕД + ; 4 : .БРЕД ( столбец# колонка# -- ) БРЕД 2И -TRAILING TYPE ; 5 : 1ПРИЛАГАТЕЛЬНОЕ 10 CHOOSE 0 .БРЕД ; 6 : 2ПРИЛАГАТЕЛЬНОЕ 10 CHOOSE 1 .БРЕД ; 7 : СУЩЕСТВИТЕЛЬНОЕ 10 CHOOSE 2 .БРЕД ; 8 : ФРАЗА 1ПРИЛАГАТЕЛЬНОЕ SPACE 2ПРИЛАГАТЕЛЬНОЕ SPACE СУЩЕСТВИТЕЛЬНОЕ ; 9 : СООБЩЕНИЕ 10 CR ." Применяя " ФРАЗА ." имея в виду " 11 CR ФРАЗА ." представляется возможным даже несмотря на " 12 CR ФРАЗА . " функционировать как " 13 CR ФРАЗА ." при существующих ограничениях на " 14 CR ФРАЗА ." . " ; 15 СООБЩЕНИЕ
Block # 232 0 высокий культурный уровень 1 общий производственный интерес 2 автоматизированный наукоемкий комплекс 3 запланированный валовой объем 4 интегрированный цифровой коэффициент 5 квалифицированный многоотраслевой принцип 6 представительный химический генератор 7 автономный аппаратный интерфейс 8 цифровой независимый автомат 9 синхронизированный функциональный критерий 10 систематизированный коротковолновой проект 11 12 13 14 15
1 Для специалистов. В мультипрограммной системе задача передает управление центрального процессора следующей задаче только на время ввода-вывода или по специальной команде, которая преднамеренно не включена в определение слова, пересылающего строки.
После загрузки блока (в нашем примере блока 231) вы получите следующий текст, хотя некоторые слова при выполнении слова СООБЩЕНИЕ всякий раз будут меняться: применяя высокий функциональный критерий имея в виду цифровой производственный комплекс представляется возможным даже несмотря на представительный независимый коэффициент функционировать как автоматизированный наукоемкий уровень при существующих ограничениях на квалифицированный Функциональный автомат.


Как видите, определение слова СООБЩЕНИЕ состоит из ряда сочетаний ." текст", перемежаемых словом ФРАЗА. Если вы выполните слово ФРАЗА отдельно, то получите ФРАЗА автоматизированный культурный объем ok
т. е. одно слово выбирается случайным образом из столбца 0 блока 232, другое - из столбца 1, а третье - из столбца 2.
Вы, конечно, заметили, что в определении слова ФРАЗА есть обращения к трем словам верхнего уровня: 1 ПРИЛАГАТЕЛЬНОЕ, 2ПРИЛАГАТЕЛБНОЕ и СУЩЕСТВИТЕЛЬНОЕ. Каждое из перечисленных слов в свою очередь обращается к слову .БРЕД, которому в качестве аргументов необходимо взять из стека номер строки (0-9) и номер столбца (0-2), определяющие выводимое слово или фразу. Случайное число, получаемое при выполнении выражения "10 CHOOSE", определяет номер строки. Каждая часть речи обеспечивает уникальный номер столбца.
Слово .БРЕД обращается к слову БРЕД для вычисления адреса бессмысленного сообщения, передавая максимальное значение счетчика, равное 20, слову TYPE. Но прежде слово -TRAILING сокращает значение 20 до фактического числа значащих символов в строке, удаляя тем самым незначащие пробелы в конце. Слово БРЕД вычисляет величину смещения в блоке путем умножения номера столбца на 20 (каждый столбец занимает 20 символов) и номера строки на 64 (каждая строка состоит из 64 символов), после чего складывает полученное смещение с. адресом начала, которое доставляется словом АБРЕД. Вообще хороший стиль программирования предполагает разделение программ, вычисляющих адреса, и программ, эти адреса использующие (так как адреса зачастую оказываются необходимыми для других целей).
Адрес начала обеспечивает слово АБРЕД, которое просто вызывает BLOCK. Здесь мы снова выделили получение адреса в отдельное действие на тот случай, если местоположение базы данных с бессмысленными выражениями изменится. Например, может измениться номер блока. Такое разбиение не мешает нам даже поместить базу данных в словарь (путем переопределения слова АБРЕД). CREATE АБРЕД 64 10 * ALLOT
Полезный прием. Генератор случайных чисел. Этот простой генератор случайных чисел может быть использован в играх. Для более же серьезных применений, например моделирования, существуют улучшенные варианты такого генератора.
( Генератор случайных чисел - верхний уровень )
VARIABLE RND HERE RND ! : RANDOM ( -- ) RND в 31421 * 6927 + DUP RND ! ; : CHOOSE ( u1 -- u2 ) RANDOM UM* SWAP DROP ; ( где CHOOSE оставляет на стеке случайное число в диапазоне от 0 до u1-1 )
Для получения некоторого случайного числа в диапазоне от 0 до 10 (само число 10 сюда не входит) просто наберите на клавиатуре
10 CHOOSE
и в вершине стека останется случайное число.

ЗАДАЧИ НА ВЫПОЛНЕНИЕ ОПЕРАЦИИ СО СТЕКОМ И АРИФМЕТИЧЕСКИХ ОПЕРАЦИЙ (УПРАЖНЕНИЕ 2-В)


1. Напишите предложение, которое позволит расположить три элемента стека в обратном порядке: вместо a b с — с b а.

2. Напишите предложение, аналогичное по результату выполнения операции OVER (но без использования OVER).

Напишите определения для следующих выражений, что должно привести к указанному состоянию стека (по его исходному состоянию): n + 1 3. ----- ( n - результат) n 4. x(7x+5) ( x - результат) 5. 9as2-ba ( a b - результат)

Ответы к упражнению 2-B1. SWAP ROT 2. SWAP DUP ROT SWAP 3. : 2B3 DUP 1 + SWAP / ; или : 2B3 DUP 1+ SWAP / ; 4. : 2B4 DUP 7 * 5 + * ; 5. : 2B5 OVER 9 * SWAP - * ;



ЗАГРУЗКА ПРОГРАММ


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

Допустим, вы загружаете несколько раз некоторый блок, изменяя в нем по одному определению. В результате ваш словарь будет содержать по варианту каждого слова данного блока для каждой загрузки. Простейший способ избежать этого состоит в применении слова FORGET. Например, если вы только что внесли исправления в определение слова F в блоке 50 и хотите вновь загрузить последний, то вы должны набрать на клавиатуре следующее:

FORGET STAR ok

50 LOAD ok

Помните, что слово FORGET забывает само указанное слово и все, что было определено после него.

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

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

В Форте три перечисленных варианта фрагментов называются оверлейными структурами, так как они при исполнении не пересекаются и взаимозаменяемы в памяти. Рассмотрим их более подробно.

Последнее определение основы программы должно содержать только имя, например: : VARIATIONS ;

Это определение называется нулевым, поскольку оно всего лишь отмечает место в вашем словаре. Включите в начало каждого альтернативного фрагмента выражение FORGET VARIATIONS : VARIATIONS ;

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


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

2 Для работающих с системами, в которых есть слово EMPTY. Слово EMPTY «забывает» все созданные вами определения. В мультипрограммной системе они составляют лишь ваше собственное расширение словаря.

Мы ввели команду LOAD для загрузки одного блока, а как загружать программу, состоящую из нескольких блоков? Используйте слово THRU, которое загружает заданный диапазон блоков. Например, выражение 180 189 THRU

означает загрузку каждого блока, начиная со 180-го и кончая 189-м. Многие системы при выполнении слова THRU выводят номера загружаемых блоков, что помогает вам следить за ходом загрузки.

Существует еще один пример. Вам известно, что слово .", помещенное в определение, приводит к выводу некоторого сообщения при выполнении данного определения. Другое слово .( можно использовать за пределами определения. Наберите на клавиатуре .( Что со мной, доктор?)

и нажмите клавишу RETURN. Вы увидите, что набранный текст высветится на экране. Это слово применяется в тех случаях, когда требуется сообщение от некоторого блока во время его загрузки. Например: Block# 10

0 \ Моя программа 1 2 CR .( Загрузка моей программы...) 3 20 37 THRU \ ШТУЧКИ 4 40 45 THRU \ ДРЮЧКИ 5 50 58 THRU \ ШПУЛЬКИ 6

7 CR .( Моя программа загружена ) 8 9


Мы завершаем рассмотрение текстового интерпретатора


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



INTERPRET
( -- )

Интерпретация текста из входного потока по указателю >IN до исчерпания входного потока.

Несмотря на то что это слово первоначально предназначалось для использования самой Форт-системой, оно может применяться и в ваших программах. Предположим, вы написали цикл, а он выполняется не так, как вам нужно. Для отладки в этой ситуации пригодилось бы слово, которое останавливало бы выполнение программы на каждом шаге цикла и позволяло бы ввести ряд команд в диалоговом режиме, причем после нажатия клавиши RETURN выполнение цикла должно продолжаться. Подобное отладочное средство можно организовать с помощью INTERPRET.
Создадим следующее определение: : ТЕСТ 0 BEGIN DUP . 1+ QUERY INTERPRET 0 UNTIL ;
Введите слово ТЕСТ. Оно выведет нуль, остановится и будет ждать. Если вы нажмете клавишу RETURN, цикл продолжит свое выполнение, и на экране высветится единица. До нажатия клавиши RETURN вы можете ввести любую команду. Прежде чем продолжить выполнение цикла, INTERPRET ее выполнит. Если вы хотите завершить цикл, введите QUIT или сделайте ошибку, вызывающую ABORT. Любое из этих действий очистит стек возвратов и тем самым приведет к выходу как из INTERPRET, так и из ТЕСТ. (Это средство можно использовать гораздо шире [1].)
В разных диалектах Форта слово INTERPRET определено по-разному, но суть этого слова можно передать, изобразив алгоритм его выполнения с помощью D-схемы.


Алгоритм выполнения слова INTERPRET можно описать следующим образом. Начинаем цикл. В теле цикла выбираем очередное слово из входного потока и осуществляем поиск его определения в словаре. Если определение найдено, исполняем слово. Затем проверяем, не исчерпан ли стек.
(В том случае, когда стек исчерпан, завершаем цикл посредством EXIT и выдаем аварийное сообщение.) Если слово в словаре не найдено, пытаемся преобразовать введенный фрагмент в число и внести его значение в вершину стека.
Итак, вы познакомились с масштабированием, методами округления и аппроксимации вещественных чисел и операциями над числами с фиксированной точкой. Для того чтобы вы чувствовали себя увереннее при решении сложных математических задач, где необходимо следить за правильностью выбора масштаба в процессе решения посмотрите второй пример в гл. 12.
Как уже упоминалось, вам ничто не мешает дополнительно ввести в Форт операции над числами с плавающей точкой Но такие средства не очень подходят Форту, поскольку его достоинства - это компактность, высокая скорость выполнения программ, простота и элегантность. Он как бы «отторгает» от себя все то, что не является насущной необходимостью. Разумно используя масштабирование и числа двойной длины, вы избавитесь от дорогостоящих операций над числами с плавающей точкой.
Операции над числами с плавающей точкой имеет смысл применять в следующих случаях:
если вы хотите использовать ваш компьютер как калькулятор для одноразовых вычислений;
когда время программирования представляется более существенным фактором, нежели время выполнения вычислений в вашей программе;
для обработки данных в большом динамическом диапазоне (превышающем диапазон от -2 биллионов до +2 биллионов).
Все перечисленные доводы являются серьезными. Однако существует целый ряд систем, где вы не должны платить за возможность выполнения операций над числами с плавающей точкой.
Ниже приводится список слов Форта, используемых в данной главе: 1+ ( n -- n+1 ) Добавление единицы.
1- ( n -- n-1 ) Вычитание единицы.
2+ ( n -- n+2 ) Добавление двойки.
2- ( n -- n-2 ) Вычитание двойки.
2* ( n -- n*2 ) Умножение на два (арифметический сдвиг влево).
2/ ( n -- n/2 ) Деление на два (арифметический сдвиг вправо).
ABS ( n -- |n| ) Помещение в стек абсолютной величины заданного числа.
NEGATE ( n -- -n ) Изменение знака на противоположный.
MIN ( n1 n2 -- min) Помещение в стек минимального из двух заданных чисел.
МАХ ( n1 n2 -- max) Помещение в стек максимального из двух
>R ( n -- ) Выборка значения из стека данных и занесение его в стек возвратов.
R> ( -- n ) Выборка значения из стека возвратов и занесение его в стек данных.
R@ ( -- n ) Копирование содержимого вершины стека возвратов без изменения его значения.
*/ ( nl n2 nЗ -- Умножение, затем деление (n1*n2/nЗ). результат ) Промежуточный результат 32-разрядный.
*/MOD ( n1 n2 nЗ -- Умножение, затем деление (n1*n2/nЗ). n-остаток n-результат) Помещение на стек остатка и частного. Для промежуточного результата используется слово двойной длины.

ЖИВОЙ ЯЗЫК


Вообразите, что вы — управляющий офисом и только что взяли себе нового энергичного помощника. В первый день вы учите его оформлять вашу корреспонденцию по соответствующему формату (допустим, ваш помощник уже умеет печатать на машинке). В конце дня вы уже можете просто сказать ему: «Пожалуйста, отпечатайте это». На второй день вы показываете своему помощнику систему регистрации. Все утро у вас уходит на соответствующие разъяснения, но уже после обеда вам достаточно отдать короткое распоряжение: «Пожалуйста, зарегистрируйте это». К концу недели вы будете понимать друг друга с полуслова. Так, если вы скажете своему помощнику: «Пожалуйста, отправьте это письмо», он поймет, что письмо нужно напечатать, дать вам на подпись, сделать с него фотокопию и зарегистрировать ее, а оригинал отослать по почте. Подобные взаимоотношения позволят как вам, так и вашему помощнику выполнять свою работу более эффективно и доставят вам обоим больше удовольствия, чем любые другие.

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

определить круг задач и присвоить каждой задаче имя;

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

Форт дает вам возможность аналогичным образом организовать ваши собственные процедуры и передать их компьютеру таким же способом (разве что не говорить ему: «пожалуйста»). В качестве примера можно привести стиральную машину, управляемую микропроцессором с программой на Форте. Заключительной командой в нашем примере будет команда, которой мы присвоим имя СТИРАЛЬНАЯ-МАШИНА. Ниже дается определение команды СТИРАЛЬНАЯ-МАШИНА так, как оно выглядит на Форте: : СТИРАЛЬНАЯ-МАШИНА СТИРАТЬ ВЫКРУЧИВАТЬ ПОЛОСКАТЬ ВЫКРУЧИВАТЬ ;

На языке Форт двоеточие означает начало нового определения. Первое слово после двоеточия, СТИРАЛЬНАЯ-МАШИНА, является именем новой процедуры. Остальные слова, СТИРАТЬ, ВЫКРУЧИВАТЬ, ПОЛОСКАТЬ, ВЫКРУЧИВАТЬ, составляют «определение» этой новой процедуры.
Наконец, точкой с запятой отмечается конец определения.



Каждое слово, входящее в состав определения СТИРАЛЬНАЯ-МАШИНА в нашей программе, описывающей стиральную машину, уже специфицировано. В частности, посмотрим, как записывается определение команды ПОЛОСКАТЬ. : ПОЛОСКАТЬ НАЛИТЬ-ВОДУ СТИРАТЬ ВЫЛИТЬ-ВОДУ ;

Как видите, определение ПОЛОСКАТЬ состоит из группы слов: НАЛИТЬ-ВОДУ, СТИРАТЬ и ВЫЛИТЬ-ВОДУ. Опять-таки каждое из этих слов уже где-то специфицировано в программе, описывающей стиральную машину. Определение команды НАЛИТЬ-ВОДУ может быть таким: : НАЛИТЬ-ВОДУ КРАНЫ ОТКРЫТЬ ДО-НАПОЛНЕНИЯ КРАНЫ ЗАКРЫТЬ ;

В приведенном определении мы ссылаемся как на объекты (краны), так и на действия (открыть и закрыть). Слово ДО-НАПОЛНЕНИЯ введено для создания «Цикла задержки», чтобы контролировать включение индикатора уровня заполнения емкости стиральной машины водой.

Если мы проследим эти определения в обратном порядке, то в конечном итоге обнаружим, что все они специфицированы в терминах группы команд, которые образуют основу всех Форт-систем. Например, полиФорт включает около 300 таких команд. Одни из них сами определены через двоеточие, как было показано выше, другие — непосредственно в терминах машинного языка конкретного компьютера. В языке Форт специфицированная таким образом команда называется словом.

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

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

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