Язык программирования Форт


Хранение чисел в памяти


До сих пор числа, которые использовались Форт-программами, хранились в стеке. Если вы поработали с такими языками программирования, как Бейсик, фортран или Паскаль, это покажется вам достаточно странным, большинство языков не используют стек непосредственно, и числа в них хранятся в переменных. Переменные представляют собой ячейки памяти, которые используются для хранения чисел, в большинстве языков предусматриваются размещение и автоматические манипуляции с числами в памяти. При этом стек фактически также используется, но программисту не приходится им управлять. Рассмотрим следующую программу на Бейсике: 10 А=5 20 В=6 30 А=А+В 40 PRINT A

Число 5 запоминается в ячейке памяти, обозначенной как переменная А, число 6 - в ячейке, отведенной для переменной В. Когда значения А и В складываются, они на самом деле выбираются из памяти, складываются почти так же, как в Форте, а затем результат снова помещается в ячейку, отведенную для переменной А. Хотя стек, может быть, и использовался для операции сложения, программист и не подозревает об этом. Программа на Форте напечатает сумму чисел 5 и 6 следующим образом: 5 6 + .

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

Какими же достоинствами обладают переменные, если вы можете обходиться стеком? Переменные служат для трех целей.
Во-первых, если у вас имеется большое количество данных, которые нужно где-то сохранить, пока они не потребуются программе. Во-вторых, с целью упрощения операций в стеке путем запоминания промежуточных результатов в виде переменных, а не в самом стеке. Наконец, в третьих, для присваивания с помощью переменных имени числу или группе чисел. Так же как переменные в алгебраических уравнениях, именованные переменные позволяют пользоваться математическими абстракциями, с помощью которых облегчается формализация алгоритма программы, а написанную программу проще понять. Если у вас есть переменные с именами SECONDS (секунды) и MINUTES (минуты), то гораздо проще помнить, что представляют собой эти числа, чем безымянные числа в стеке.
В более общей формулировке переменная представляет собой наименованную ячейку (адрес) памяти, которая может содержать число. Переменная в Форте- это слово, которое возвращает в стек адрес, по которому может храниться число. Для перемещения содержимого ячеек памяти, имеющих имя, в стек и обратно можно использовать слова @ (взять) и ! (занести), которые вам уже известны из гл.З. Мы рассмотрим сначала основные (но не самые удобные) способы для выполнения этих операций, а затем перейдем к более рациональным методам.
Создание переменных
Где можно надежно хранить переменные в памяти так, чтобы они случайным образом не изменились? Мы уже знаем, что Форт отводит часть памяти для своего словаря, в котором содержатся названия слов и их определения (в том смысле, что определения сообщают машине, какие операции она должна делать при исполнении каждого слова). Мы имеем возможность создавать в словаре слова, которые содержат числа. Когда такое слово исполняется, то единственное, что оно должно делать,- это помещать в стек адрес числа, которое оно содержит, что обеспечивает доступ к нему с помощью операторов ! и @.
Слово CREATE (создать) помещает на вершину словаря имя переменной и еще некоторую информацию (которая, например позволяет Форту искать в словаре другие слова).


Имя и эта информация представляют собой последовательность из нескольких байтов, называемую заголовком указанного слова. Когда исполняется слово, определенное словом CREATE, то в стек кладется адрес, следующий непосредственно после адреса заголовка. Слово ALLOT (от ALLOcaTe- разместить) резервирует определенное число байтов в словаре. Например, CREATE INCHES 2 ALLOT создает в словаре слово с именем INCHES (дюймы) и резервирует, или размещает, 2 байта, в которых может быть помещено слово одинарной длины. Если вы введете 12 INCHES ! слово INCHES положит в стек зарезервированный адрес, а ! запишет в него число 12. INCHES @ . затем возвратит число 12 в стек и напечатает его. Это так просто. Между прочим, слово CREATE имеет более сложные и изощренные применения, которые будут описаны в гл.14,


В этом месте мы совершим небольшое отвлечение в сторону, чтобы познакомиться с некоторыми словами для перемещения чисел между памятью и стеком. Для всех таких слов предварительно нужно задать адрес в памяти, с которым они работают. Вам уже знакомы слова @, !, С@ и С!. Слова 2@ и 2! соответственно извлекают и запоминают числа двойной длины. В некоторых версиях и в MMSFORTH определены также слова 4@ и 4! для работы с 4-байтовыми числами, с помощью которых представляются числа с плавающей запятой. Несколько отличаются слова ? и +!. Слово ? (обязательное в Форт-79, но не включенное в Форт-83) может быть определено просто как : ? ( addr - ) @ . ;
Оно извлекает число из ячейки памяти и печатает его на экране. Аналогично можно определить слово С? (имеющееся в MMSFORTH и других версиях) для извлечения и печати одного байта. Стандартное слово +! добавляет второе число из стека к числу, содержащемуся в ячейке с адресом, который находится на вершине стека. Так, если в ячейке памяти с адресом 22345 содержится число 250, то 5 22345 +! изменит содержимое на 255. Это равносильно следующим действиям : 22345 @ 5 + 22345 ! но выполняется значительно быстрее. Очевидно, что с помощью ALLOT можно резервировать место для размещения более двух байтов.


CREATE INCHES 4 ALLOT отводит место для хранения одного двойного слова, доступного с помощью слов 2! и 2@. Впрочем, более важно то, что в отведенном (резервированном) месте могут храниться несколько чисел, входящих в одно слово. Если выполнить CREATE YARDS 10 ALLOT будет зарезервировано место, достаточное для хранения пяти одинарных слов (10 байтов). Пусть теперь вы определили слова : !YARDS 2* YARDS + ! ; и : @YARDS 2* YARDS + @ ;
Тогда если ввести 23 2 !YARDS то в четвертом и пятом байтах будет запомнено число 23 (если принять нумерацию с нулевого байта), зарезервированное словом YARDS. Операция 2 @YARDS возвращает в стек число 23. Другими словами, вы можете зарезервировать место для хранения последовательности или списка чисел и затем определить слова, с помощью которых к ним можно обращаться. Такой список чисел называется массивом. Массив 1,3,45,671,23, каждое число которого называют элементом, называется одномерным, линейным массивом или вектором. Можно хранить массив в слове YARDS следующим образом: 1 0 !YARDS 3 1 !YARDS 45 2 !YARDS 671 3 !YARDS 23 4 !YARDS а обращаться к числу можно с помощью слова @YARDS. Так, 3 @YARDS . напечатает 671. По некоторым причинам многие считают массивы чем-то сложным и непонятным, на самом деле это просто список чисел и ничего больше.
Слово , (запятая) упрощает создание переменных и массивов. Оно резервирует два байта так же, как 2 ALLOT, а затем запоминает число, находящееся на вершине стека, в отведенных двух байтах. Поэтому CREATE INCHES 7 , будет эквивалентно CREATE INCHES 2 ALLOT INCHES 7 !
В обоих случаях будет создана переменная INCHES, которой будет присвоено значение 7. Слово (запятая) особенно полезно при определении массива. CREATE YARDS 1 , 3 , 45 , 671 , 23 , создает массив YARDS и инициализирует его содержимое гораздо проще, чем было показано раньше. В особенности оно полезно для создания таблиц данных, которые не должны изменяться. Наконец упомянем определенное во многих версиях Форта, и в том числе в MMSFORTH, слово С,.


Оно выполняет то же, что и , (запятая), но резервирует место и запоминает число в диапазоне 0- 255 только в одном байте. (Некоторые процессоры имеют адресацию памяти, не допускающую использование слова С,.)
Упражнения
1. Создайте две переменные, FEET (футы) и INCHES (дюймы). Теперь определите слово F->Т для преобразования числа из FEET в число, выраженное в дюймах, результат должен помещаться в переменную INCHES. 2. Проделайте упражнение 1 для чисел двойной длины. 3. Дайте определение слова 2!, назвав его NEW2! , которое действовало бы как ! , но с числами двойной длины. 4. Дайте новое определение слова +! с именем NEW+!. (В Форте для ускорения оно определено в машинных кодах.) 5. Используя С@, дайте другое (но более медленно работающее) определение слова @, назвав его NEW@. 6. По аналогии определите слово NEW!, используя С! . 7. Определите слово VARSWAP (переставить переменные), которое должно переставлять содержимое двух переменных, адреса которых находятся в стеке. Тогда INCHES FEET VARSWAP занесет содержимое переменной INCHES по адресу переменной FEET и наоборот. (Указание: используйте PAD.) 8. Определите два массива, из 7 элементов каждый, 1WEEK (первая неделя) и 2WEEK (вторая неделя), пользуясь словом , (запятая) для инициализации всех элементов нулями. 9. Определите семь слов !SUN(!BCKp), !МОN(!пнд), !TUE(!втр) и т.д. так, что, если им предшествует число (например, сумма дневной выручки) и имя недели, они записывали бы это число в соответствующий элемент. Таким образом, 5 1WEEK !TUE запомнит число 5 во втором элементе (вторник) массива 1WEEK (первая неделя). 10. Напишите слова, соответствующие словам упражнения 9, которые способны извлекать содержимое переменных и класть их в стек. 11. Определите слово ESWAP (переставить элементы), которое должно переставлять два элемента именованного массива. Например, 1 3 1WEEK ESWAP должно переставить значения для понедельника (MONday) и среды (WEdnesday) массива 1WEEK (первая_неделя). Используйте слово VARSWAP. 12.


Создайте счетный массив однобайтовых элементов CNT (счетчик), в котором должно записываться, сколько раз числа запоминаются в различных элементах массивов 1WEEK и 2WEEK. 13. Определите слова +SUN, +MON и т.д., которые должны добавлять числа к соответствующим элементам именованных массивов. Так, например, 7 1WEEK +THUR должно добавить 7 к четвертому (THURsday- четверг) элементу массива 1WEEK. Эти слова должны также добавлять по единице к соответствующему элементу счетчика CNT. 14. Предположим, что все элементы массивов 1WEEK и 2WEEK были инициализированы нулями и что изменение их содержимого было произведено только с помощью операций +MON, +TUE и т.д. Тогда напишите слово DAY-AVE (сред-нее_за_день), которое выдавало бы в стек среднее значение чисел, добавленных к конкретным элементам массивов 1WEEK и 2WEEK. Так, если элемент 3 из массива 1WEEK содержит 20, элемент 3 из массива 2WEEK содержит 30, а элемент 3 массива CNT содержит 5, то выражение 3 DAY-AVE должно выдать в стек 10.
Вы написали простую, но в то же время достаточно хитроумную программу, заслуживающую внимательного рассмотрения производимых ею действий. Набор задач был составлен так, чтобы в конечном счете получилась программа, которая определяет средние значения выручки за определенный день недели по двум неделям, причем как ежедневные поступления, так и итоги за каждый день хранятся в двух массивах. В этом состояла наша цель. В большинстве языков программирования, чтобы добиться поставленной цели, надо написать программу со всеми деталями и отладить ее как единое целое. На Форте можно написать отдельные программы для каждой задачи и проверить их по отдельности. Разработка и написание программы целиком со всеми деталями чаще всего по блок-схеме называется программированием сверху вниз, и некоторые считают такую методику обязательной. В противоположность этому Форт позволяет разрабатывать программу более гибко и экспериментировать с ней, и вследствие этого каждое слово или задача программируются и проверяются отдельно, чтобы решить некоторую часть общей задачи.


В самом деле, вам не нужно было знать, приступая к упражнениям, какова их конечная цель.
Перемещение и заполнение содержимого массивов
Предположим, что имеется два массива по 20 элементов каждый, 1DATA и 2DATA, и нужно сделать так, чтобы содержимое массива 1DATA было равно содержимому массива 2DATA. Можно проделать это следующим образом: 1DATA 2DATA 20 CMOVE
Слово CMOVE ожидает, что в стеке должно быть два адреса и число (адр1 адр2 n -- ), тогда оно перешлет n байтов, начиная с адреса адр1, на адрес адр2. Причем сделает это очень быстро. Первая буква в слове CMOVE ассоциируется со словом character, т.е. литера, поскольку это слово часто используется для пересылки байтов, представляющих литеры в литерных строках. Слово MOVE (переместить), которое имеется в Форт-79 и большинстве реализации, но отсутствует в Форт-83, действует так же, но пересылает указанное число ячеек, а не байтов. Таким образом, 10 MOVE действует так же, как 20 CMOVE
Слово CMOVE обрабатывает байт за байтом. Это значит, что байт из ячейки с адресом адр1 переносится в ячейку с адресом адр2, байт из ячейки с адресом адр1+1 переносится в адр2+1 и т.д. Теперь рассмотрим случай,.когда адр2 находится между адр1 и адр1+n, т.е. область, в которую производится копирование, перекрывается с областью, откуда производится копирование. Теперь допустим, что первый байт в массиве 1DATA равен 219. Если напечатать 1DATA 1DATA 1+ 19 CMOVE то число 219 будет скопировано из ячейки с адресом 0 в ячейку с адресом 1, затем из ячейки 1 в ячейку 2, затем из 2 в 3 и, наконец, из ячейки 18 в ячейку 19. Другими словами, массив 1DATA будет целиком заполнен байтами со значением 219. А теперь предположим, что нужно установить значения всех элементов массива равными 0. Можно сделать это с помощью CMOVE. Вам нужно просто установить первый элемент массива равным 0, а затем скопировать его во все остальные байты массива. Вот необходимая последовательность действий: 0 1DATA С! 1DATA 1DATA 1+ 19 CMOVE
Действие CMOVE, в данном случае неправильное, можно наглядно показать на диаграмме.


Предположим, что слово CMOVE применяется к 4 байтам, содержащим в начале числа 10,11,12,13, и оно перемещает байт с адресом 0 на адрес 1 и т.д. Процесс будет происходить так, как показано на диаграмме 10 11 12 13 10 10 12 13 10 10 10 13 10 10 10 10
А нам нужно, чтобы из байта с адресом 1 число переместилось в ячейку 2; исходное число байта с адресом 2- в ячейку с адресом 3 и т.д. без наложения байтов друг на друга. 'Это значит, что из начального массива 10 11 12 13 должен в конце получиться массив 10 10 11 12 13. Слово, которое позволяет сделать это, называется (в Форт-79 оно необязательное). Слово
В данном случае перемещение элементов массива производится байт за байтом, но наложения не происходит. Очевидно, слово
Имеется также слово FILL (заполнить), которое заполняет некоторую область памяти байтами с указанным значением. Также 1DATA 20 0 FILL заполнит 20 байтов нулями, начиная с адреса 1DATA. Слово FILL- используется, чтобы занести число в определенное число байтов, начиная с некоторого известного адреса. В MMSFORTH и некоторых версиях есть еще слово ERASE (стереть), которое заполняет память последовательностью из нулей.
Таким образом, программа для предыдущего примера эквивалентна следующей: 1DATA 20 ERASE
Еще одно слово в MMSFORTH BLANK (пробел) заполняет область памяти кодами ASCII "пробел". Это равносильно 32 FILL
Очевидно вы сами можете дополнить приведенные слова собственными, имеющими специфическое назначение.
Упражнения
1. Определите слово FILL (назовите его NEWFILL), используя слово CMOVE. 2. Определите слово ERASE (назовите его NEWERASE), используя слово FILL. 3. Определите слово INITIALIZE (инициализировать), чтобы установить все элементы массива чисел одинарной длины в нуль, т.е. 1DATA 7 INITIALIZE должно установить все семь элементов массива 1DATA в нуль. Определение тривиально. 4. Напишите слово ARR-COPY (копировать массив), которое бы копировало содержимое массива одинарных чисел длиной п элементов в другой массив такой же длины, т.е. 1DATA 2DATA 7 ARR-COPY должно скопировать массив 1DATA в массив 2DATA.


Дайте два определения, используя как CMOVE, так и MOVE. 5. Напишите слово ARR_EXCH (копировать массив), которое производит обмен содержимого двух массивов одинарных чисел размерности n. Таким образом, 1DATA 2DATA 7 ARR-EXCH перенесет массив 1DATA в 2DATA и наоборот. (Указание: воcпользуйтесь словами PAD и MOVE. Возможно, потребуется временно хранить где-либо количество чисел.)
Переменная, константа и связанные с ними слова
До сих пор в этой главе мы рассказывали вам о создании переменных окольным путем. Это делалось для того, чтобы вы привыкли думать о переменных как ячейках памяти, что представляется неудобным, если вы знакомы с другими языками программирования. Существуют, однако, болей простые средства для обращения с переменными и массивами.
Стандартное слово VARIABLE (переменная) применяется для определения имени переменной и резервирования по ее значение двух байтов. Таким образом VARIABLE INCHES Производит те же действия, что и CREATE INCHES 2 ALLOT но немного удобнее, что более важно: применение слова VARIABLE в программе вместо CREATE делает ее более удобочитаемой и понятной. С переменной, которая определена, действуют так же, как с переменной, созданной словом CREATE. (Напомним снова, что слово CREATE включено в словарь Форта не только для того, чтобы создавать переменные, массивы и т.д. Оно также служит и для других целей.)
Для обращения с двойными числами имеется стандартное слово 2VARIABLE. В некоторых реализациях Форта есть другие разновидности слова VARIABLE, например CVARIABLE для хранения байтов, 4VARIABLE для переменных с плавающей запятой. Вам должно, быть понятно, что собственно слово VARIABLE (переменная) определяется следующим образом: : VARIABLE CREATE 2 ALLOT ;
Эта конструкция работает потому, что слово CREATE создает переменную при исполнении, а не при компиляции, т.е. не во время добавления определяемого слова VARIABLE к словарю. Массивы также могут определяться словом VARIABLE. Если ввести с клавиатуры VARIABLE INCHES 8 ALLOT то это будет равносильно CREATE INCHES 10 ALLOT причем вместо 8 используется 10, потому что два байта уже были резервированы словом VARIABLE.


С массивами, определенными любым из двух методов, можно обращаться одинаково.
Мы видим, что определение массивов как переменной словом VAMABLE несколько обескураживает. Оно не имеет никаких преимуществ перед использованием слова CREATE и, кроме того, не делает различия между переменной и массивом, а программа воспринимается труднее.
Похожим на слово VARIABLE является слово CONSTANT (константа). Оно используется для хранения таких чисел, которые внутри программы не будут изменяться (хотя иногда это правило нарушается). Для него требуется число в стеке, а после слова CONSTANT - имя слова, в котором будет храниться это число. При исполнении слова в стек кладется его содержимое. Поэтому после cлова, определенного как константа через слово CONSTANT, не требуется операция @ (извлечение содержимого). Например, если определить 5280 CONSTANT FT/MILE то при исполнении слова FT/MILE в стек будет помещено число 5280. Слова, определенные с использованием слова CONSTANT, лучше всего применять для таких чисел, как коэффициенты пересчета единиц измерения, константы уравнений. Конечно, с таким же успехом можно ввести число непосредственно в программу, но если программа подвергается изменениям, тогда каждое вхождение этого числа также должно быть изменено, в то время как при использовании константы число потребуется изменить только однократно. Кроме того, использование значащего слова вместо числа способствует облегчению понимания программы. Если вы знакомы с языком Бейсик, то можете подметить, что идея константы в языке Форт несколько отличается. В Бейсике константа обычно рассматривается как число, включенное в выражение, например 56.5 в строке 90 А = 56.5 * В
В языке Форт константа- это именованная ячейка памяти, фактически это разновидность переменной, которая посылает в стек не свой адрес, а содержимое.
Для переменных двойной длины в стандарте Форта предусматривается слово 2CONSTANT, во многих версиях (в том числе в MMS.FORTH) имеются слова CCONSTANT (однобайтовая константа) и 4CONSTANT для чисел с плавающей запятой.


Смысл назначения константы- определять величину, которая остается неизменной в программе, теряется, если константа изменяется. Вы должны иметь возможность узнать значение константы из ее определения, не прибегая к поиску того места, где она, изменилась. Это абсолютно обязательно, если Форт-программа должна быть записана в ПЗУ. Поэтому в большинстве случаев константа не должна изменяться. Но иногда бывают случаи, когда необходимо изменить константу. Один из них возникает при отладке программы, другой случай, когда константа по-разному используется в основной части программы и в другой. Наиболее важный случай, когда константа может быть изменена, если таким путем достигается сокращение времени. Для помещения константы в стек требуется несколько меньше времени, чем для извлечения значения переменной, и это нужно иметь в виду, если время исполнения программы для вас становится критичным. Поэтому вам следует знать, как изменить значение константы. В стандарте Форт-79 и большинстве версий, не придерживающихся стандарта Форт-83, обязательным является слово ' (произносится "тик"), которое кладет в стек адрес содержимого слова. Так, * FT/MILE выдает, адрес ячейки памяти, где хранится число под именем FT/MILE, будь оно константой или переменной. Поэтому для изменения значения константы единственное, что нужно сделать,- это поменять содержимое ячейки по этому адресу, например нужно ввести 6076 ' FT/MILE ! чтобы изменить сухопутные мили на морские. Теперь, если ввести FT/MILE, в стек будет заслано число 6076. (Заметьте, что точно такая же процедура (с излишними издержками) может применяться для изменения значения переменной.
В стандарте Форт-83 процедура сложнее, так как слово ' возвращает не адрес содержимого указанного слова, а другой адрес, связанный с этим словом, что может привести к путанице (вскоре вы поймете, что этот адрес полезен, но не для изменения константы). Однако если адрес, извлекаемый словом ', в стандарте Форт-83 известен, то слово >BODY даст нужный нам адрес, т.е.


тот, который извлекается в стандарте Форт-79 словом '. Таким образом, при вводе 6076 ' FT/MILE >BODY ! в стандарте Форт-83 мы получим то же самое, что при вводе 6076 ' FT/MILE ! в стандарте Форт-79. Дополнительное усложнение в Форт-83 состоит в том, что слово ' работает, как описано, только вне определения слов через двоеточие, т.е. оно является немедленно исполняемым. Все хорошо, если вы. хотите изменить константу, когда некоторая часть программы загружена.
Но если необходимо определить слово для изменения константы через двоеточие, то вместо слова ' нужно пользоваться словом [']. Так, например. : MAKE-NAUTICAL 6076 ['] FT/MILE >BOOY ! ; это определение слова, которое производит такое же действие, как слово, определенное следующим образом : : MAKE-NAUTICAL 6076 ' FT/HILE ! ; в стандарте Форт-79, потому что в данном случае не различается действие слова ', стоит ли оно в определении или исполняется непосредственно. Причины таких различий кроются глубоко в тонкостях работы Форт-системы. Короче говоря, несколько слов вроде ' в Форт-79 должны работать по-разному в состоянии компиляции и исполнения, хотя кажется, что их действие одинаково. Такие слова называют зависимыми от состояния. В стандарте Форт-79 разрешены слова, зависимые от состояния, в то время как в Форт-83 не разрешены, поэтому требуются два различных слова: одно для состояния компиляции, другое для исполнения. Подробнее мы на этом остановимся в гл. 15. Здесь, возможно возникнет недоумение, зачем нам потребовались и константы, и переменные? Почему бы не определить слово FT/FURLONG (футы_в_восьмую_часть_мили) таким образом, что когда вы введете 660 FT/FURLONG ! то число будет запомнено, как в примере с FT/MILE. В то же время, если вы просто введете FT/FURLONG, в стек будет выдано число 660 без операции @, как в случае константы. Если подумать, то при этом слово FT/PURLONG должно вести себя двумя различными способами: одним, когда оно используется само по себе, и другим, когда за ним следует операция @.


В MMSFORTH можно определять слова, в которых соединены свойства констант и переменных. Примерно таким способом эта возможность может быть встроена и в другие версии. В MMSFORTH имеется ключевое слово QUAN (от quantity- количество). Если ввести QUAN FT/FURLONG а после этого 660 IS FT/FURLONG то слово FT/FURLONG будет иметь свойства константы в том смысле, что оно возвратит число, если ввести слово. Но в то же время оно сходно с переменной, поскольку слово IS просто записывает число из стека в FT/FURLONG. Если вы хотите узнать, где хранится это число, то предложение AT FT/FURLONG! положит в стек его адрес. Таким образом, 660 AT FT/FURLONG! выполнит то же действие, что и 660 IS FT/FURLONG в то время как AT FT/FURLONG @ равносильно тому, что вы введете FT/FURLONG. Предусмотрены также слова CQUAN (для байтов), 2QUAN (для двойных чисел) и 4QUAN (для чисел с плавающей запятой), назначение которых очевидно. Кроме того, в языке предусмотрены также массивы QUAN и специальные слова типа QUAN для хранения чисел с плавающей запятой сопроцессора типа 8087 и для работы с массивами в расширенной памяти компьютера IBM. Уменьшение времени исполнения программы и требуемого числа ячеек памяти благодаря слову QUAN по сравнению с использованием переменных происходит, если QUAN применяется в программе более двух раз. Если в вашем распоряжении имеется MMSFORTH, то дальнейшие детали вы найдете в его документации.
Упражнения
1. Определите слова СVARIABLE и 4VARIABLE. 2. Определите слово ARRAY, которое при исполнении будет определять массив переменных, состоящий из 16 различных чисел, т.е. массив, с которым можно обращаться так же, как, например, с массивом, созданным предложением CREATE FT/HILE 20 ALLOT за исключением того, что он должен быть создан выражением 10 ARRAY FT/MILE (Заметьте, что перед словом ARRAY в стеке должно стоять количество чисел, а не байтов, которое содержится в массиве.) 3. Определите по аналогии слова CARRAY, 2ARRAY, 4ARRAY. 4. Определите две переменные 1LENGTH (длина) и 2LENGTH для помещения в них значений длины в разных единицах, например в сантиметрах и метрах или дюймах и футах.


Определите константу (1->2), которая переводила бы содержимое переменной из 1LENGTH в единицы длины переменной 2LENGTH, т.е. если в 1LENGTH даны значения в дюймах, а в 2LENGTH в футах, то значение, которое выдаст слово 1->2, должно быть равно 12. Напишите слово для преобразования содержимого 1LENGTH и запоминания его в переменной 2LENGTH. Как изменить программу, чтобы вместо дюймов и футов слово для преобразования единиц длины работало бы с сантиметрами и метрами? Это покажет вам, насколько полезны константы и почему им нужно присваивать значения при исходном определении. 5. Напишите слово с именем X->Y, которое находило бы значение Y, если в стек кладется значение Х в соответствии с уравнением Y = AX + В , считая, что А и В предварительно определены как переменные. Понятно ли вам, как можно изменить действие слова X->Y, не меняя его определения? Понятно ли вам также, почему использование переменных делает программу более гибкой и удобочитаемой ? 6. Если слово INCHES (дюймы)- это переменная, в чем различие исполнения операций INCHES и ' INCHES в стандарте Форт-79? 7. Предположим, что слово TO-INCHES (в_дюймы) представляет константу, в которой хранится коэффициент преобразования единиц, т.е. в нем должно содержаться число 12. если требуется преобразование числа футов в число дюймов, и число 36,- если преобразование числа ярдов в число дюймов. В предположении, что вы пользуетесь стандартом Форт-83, дайте определение двух слов SET-FEET (установить_футы) и SET-YARDS (установить_ярды) для записи соответственно чисел 12 36 в константу TO-INCHES. Можно ли использовать слова ' и >BODY?
О векторном исполнении операторов
Очень важным применением массивов (векторов) является управление программой для избирательного исполнения одного из нескольких возможных слов. Каждое слово имеет свой адрес, связанный с ним, который можно использовать для исполнения слова, фактически не вызывая его по имени. Чтобы понять, как это делается, определим сначала слово : MESSAGE ." Message" ; и затем введем FIND MESSAGE


Это слово определено в Форт-79; в Форт- 83 перед словом MESSAGE нужно ввести ' с клавиатуры либо ['], если слово встречается в определении через двоеточие; слова ' и ['] в Форт-83 действуют так же, как слово FIND в Форт-79. Слово же FIND в Форт-83 имеет совершенно другое назначение (см. гл. 14). Поэтому для Форт-83 нужно заменить слово FIND на ' или ['] в последующем сложении.
Если заглянуть в стек, то мы обнаружим, что в него был помещен некоторый адрес. Теперь, имея этот адрес в стеке, если напечатать EXECUTE (исполнить), вы увидите на экране слово "Message" (сообщение). Слово FIND (или ' и ['] в Форт-83) возвращает адрес, который дает возможность слову EXECUTE выполнить это слово. Таким образом, FIND NESSAGE EXECUTE производит то же самое, т.е. выдает на экран Message что делает слово MESSAGE само по себе. Это означает, что можно исполнить слово не только по имени, но также косвенно с помощью FIND, находя в словаре его адрес и затем используя слово-команду EXECUTE. При векторном исполнении применяется вектор (или переменная), в котором содержатся адреса слов, найденные словом FIND. Затем соответствующий элемент засылается в стек и слово EXECUTE исполняет нужное слово. Рассмотрим пример. Создадим массив из трех элементов: CREATE CHOICE 6 ALLOT и теперь определим три слова : 1PROG ." Program 1" ; : 2PROG ." Program 2" ; : 3PROG ." Program 3" ; Теперь запишем адреса этих слов в массив, вводя с клавиатуры FIND 1PROG CHOICE 0 + ! FIND 2PROG CHOICE 2 + ! FIND 3PROG CHOICE 4 + ! Если теперь введем CHOICE 2 + @ EXECUTE на экране появится сообщение "Program2", т.е. было исполнено слово 2PROG.
Предположим теперь, что вы хотите сделать выбор одной из программ 1PROG, 2PROG или 3PROG, нажимая одну из трех клавиш А, В или С. Вы можете сделать это, вводя коды ASCII клавиш с помощью слова KEY, преобразуя затем их в числа 0, 1 или 2 соответственно, пользуясь которыми можно выбрать и исполнить соответствующий элемент массива.


Следующее слово выполнит эту задачу: : CHOOSE KEY 65 - 2* CHOICE + @ EXECUTE :
Конечно, нужно быть внимательным, чтобы ошибочно не нажать другую клавишу; слово CHOOSE (выбрать) должно быть определено так, чтобы имелась защита от неправильного ввода. Чтобы запомнить адрес слова, которое должно быть исполнено, вместо массива может быть также использована переменная (в конце концов, переменная это всего лишь массив из одного элемента).
Предположим, что вы хотите иметь одно слово, которое исполняло бы одну из нескольких различных задач, например 1TASK, 2TASK и т.д. Когда вы составляете программу, то не знаете точно, какие это будут задачи. Можно в самом начале программы объявить переменную, например WHICH-TASK (какая_задача). Затем можно определить слово : DO-TASK ( addr - ) WHICH-TASK @ EXECUTE , которое исполнит то слово, адрес которого записан в переменной WHICH- TASK. Впоследствии при составлении программы вы определите 1TASK, 2TASK и т.д. Теперь, если вы хотите, чтобы слово DO-TASK (делать_задачу) исполнило задачу 1TASK, нужно ввести FIND 1TASK WHICH-TASK ! и слово DO-TASK- то же самое, как если бы вы ввели 1TASK. С другой стороны, если ввести FIND 2TASK WHICH-TASK ! то тогда DO-TASK исполнит задачу 2TASK. Другими словами, DO-TASK будет исполнять то слово, адрес которого находится в переменной WHICH-TASK. Вводом одного слова можно обеспечить выполнение одной из нескольких различных задач, в зависимости от значения содержимого переменной.
Некоторые Форт-системы очень широко применяют векторное исполнение, используя переменную или константу, которая должна содержать исполнительный адрес базовых слов ядра Форт-системы. Например, слово EMIT (вывести) может быть определено как : EMIT (EMIT) EXECUTE : где слово (EMIT) является константой, которая содержит исполнительный адрес. Вы можете определить теперь новое слово, например PRINTER (печать), которое в соответствии с вашим желанием изменило бы действие слова EMIT (возможно, определенное в машинных кодах), переводя вывод с экрана на печатающее устройство.


Тогда вы можете определить (EMIT) CONSTANT SCREEN чтобы сохранить старое определение слова EMIT. Теперь можно определить слова : PRINTER-ON FIND PRINTER (EMIT) ! : и : PRINTER-OFF SCREEN (EMIT) ' ,
Когда исполняется слово PRINTER-ON, вывод будет идти на печатающее устройство, а после PRINTER-OFF - снова на экран.
Существует множество других способов выполнения подобного рода задач с помощью векторного исполнения, которого мы едва коснулись. Вы найдете новые примеры в упражнениях, а более сложные его применения освещаются в книге Броуди "Thinking FORTH" (1984).
Другое очень полезное применение массива, подобное векторному исполнению,- это поисковая таблица, специализированной формой которой является таблица перекодировки. Предположим, что вы пользуетесь терминалом, который неправильно понимает стандартные коды ASCIl, т.e., например, забой влево, код которого 8, а на вашем дисплее это 22, вместо кода перевода строки 13 у терминала он равен 2, ваша программа посылает 1, чтобы очистить экран, в то время как терминал для этой функции ожидает код 24.
Определим массив следующим образом: CREATE TRANSLATE 24 С, 2 С, З С, 4 С, 5 С, 6 С, 7 С, 22 С, 9 С, 10 С, 11 С, 12 С, 23 С, 14 С, 15 С, 16 С, 17 С, 18 С, 19 С, 20 С, 21 С, 22 С, 23 С, 24 С, 25 С, 26 С, 27 С,
Теперь определим слово NEWEMIT, которое в вашей программе должно будет использоваться вместо EMIT: : NEWEMIT 1- TRANSLATE + С@ EMIT :
Тогда вместо того, чтобы непосредственно выводить на экран коды, которые программа кладет в стек, слово NEWEMIT будет просматривать в таблице TRANSLATE (перевести), каким новым управляющим символом должен быть замещен символ из стека, прежде чем он будет выведен на терминал. Таблицы перекодировки особенно удобны в применении к программам редактирования или управления терминалами, в которых управляющие клавиши могут по вашему желанию выполнять сложные специализированные функции.
Упражнения
1. Определите три слова, которые должны печатать "Dear Sir : ", "Dear Madame : " или "Dear Sir or Madame : " (Глубокоуважаемый сэр, Глубокоуважаемая мадам, Глубокоуважаемый(ая) сэр или мадам).


Определите переменную, которая должна содержать адрес одного из этих трех слов. Теперь определите слово SALUTATION (приветствие), которое печатало бы один из трех вариантов приветствия, в зависимости от значения переменной. Предположим, что вы хотите изменить выводимый текст во время исполнения программы. Как это может быть сделано? 2. Вы пишете программу с меню, которое позволяет по выбору пользователя сделать вывод на экран, нажимая клавишу 1, на печатающее устройство, нажимая клавишу 2, и на оба устройства одновременно, нажимая клавишу 3. В вашей Форт-системе имеются слова PRINT (печать), PCRT (печать и экран). CRT (экран), определенные, как описано в гл. 6. Определите слова, нужные для векторного исполнения, реагирующие на код нажатой клавиши в соответствии с меню. Вам нужно определить массив из трех элементов и инициализировать его адресами для слов PRINT, PCRT и CRT, а затем определить слово с помощью KEY и EXECUTE, которое исполняло бы одно из этих слов, когда нажималась клавиша 1, 2 или 3. 3. Оператор взвешивает болты и записывает их массу в компьютер. Оператору нужно узнать, сколько было болтов, имеющих вес менее 100 г, сколько- вес от 100 до 200 г и сколько- тяжелее 200 г. Кроме того, он хочет знать общий вес болтов каждой группы. Каждый раз после взвешивания болта оператор вводит его вес и затем слово BW (вес_болта). Для каждой группы болтов имеются две переменные: COUNT1 (счетчик1), COUNT2, COUNT3- для счета числа WT1 (вес1), WT2, WT3 -для общего веса болтов. Дайте определение этих переменных и инициализируйте их нулями: а) определите слово CLASS (группа), которое выдавало бы 0, если вес болта меньше 100 г, 1- если он находится в интервале 100-200 г и т.д. т.е. 125 CLASS должно выдать 1. (Указание: используйте оператор деления /.); б) определите массив, который содержит адреса ячеек, где хранятся числа болтов, и другой массив, где хранятся адреса суммарных весов. Заполните массивы соответствующей информацией. Эти массивы представляют собой поисковые таблицы; в) теперь определите слово BW, которое оператор вводит после веса очередного болта.


Это слово должно добавлять 1 в соответствующую переменную счетчика числа болтов и добавлять вес болта к соответствующей переменной суммарного веса. Для него потребуется слово CLASS; г) определите слово SUMMARY (итог), которое должно печатать количество, общий вес и средний вес в каждой группе. Таким образом, при вводе 0 SUMMARY должно печататься количество, суммарный вес и средний вес болтов, весящих меньше 100 г. 4. Напишите программу, которая выполняла бы функции, заданные в упражнении 3, но на этот раз запомните число болтов в трехэлементном массиве COUNTS и веса в трехэлементном массиве WEIGHTS. Это будет более эффективно, чем использование отдельных переменных. Можете ли вы объяснить, почему? 5. Теперь определите три слова, которые печатали бы менее 100 г от 100 до 200 г больше 200 г и поместите их адреса в массив, пригодный для векторного исполнения слов. 6. Определите три слова: CNT (счетчик), WEIGHT (вес) и AVERAGE (среднее) и три константы SMALL (малый), MEDIUM (средний) и LARGE (большой) так, что, если оператор вводит, например, MEDIUM WEIGHT на экране должно появиться сообщение Суммарный вес болтов от 100 до 200 г равен 139 или SMALL CNT выведет Число болтов весом меньше 100 г равно 33 и т .д.
Упражнения 3-6 иллюстрируют несколько принципиальных моментов: 1) Чтобы Форт-программа была полезной, не обязательно, чтобы она работала непрерывно. Чаще всего прикладные Форт-программы лучше всего реализуются путем создания инструментального набора слов, которые могут быть использованы, как в обычном калькуляторе. Так, например, слова BW и CNT, которые являются частью программы, но их исполнение происходит только тогда, когда эти слова вводятся; в то же время массивы постоянно отслеживают соответствующие данные; 2) имеется несколько способов реализации отдельных частей программы. Можно использовать как массивы, так и переменные, чтобы запоминать количество и вес, однако если. как правило, имеется несколько различных групп данных, то более эффективно применение массивов; 3) векторное исполнение основано на использовании поисковой таблицы, в которой записаны адреса слов; 4) поисковые таблицы можно использовать для других целей, например для того, чтобы познакомиться с методами хранения чисел в переменных. 7.


Вы пишете программу на языке Форт для обработки текста. После того как программа написана, вы решаете добавить возможность работы оператора с клавиатуры Дворака вместо стандартной клавиатуры QWERTY (стандарт IBM). Клавиатура Дворака показана на рис. 6.1.
{ & % # ! ( ) @ $ ^ * + [ 7 5 3 1 9 0 2 4 6 8 = " < > P Y F G C R L ? } , ' . / ] A O E U I D H T N S - ~
| : Q J K X B M W V Z \ ;
Рис.6.1 ( см. файл ris6_1.bmp )
а) напишите таблицу перекодировки, которая преобразует символы клавиатуры QWERTY в символы клавиатуры Дворака, т.е. если. например, нажата клавиша К. то в стек должна посылаться литера Т; б) переделайте слово KEY в DKEY, которое должно не просто принимать символ с клавиатуры и выдавать в стек его код: DKEY должно воспринимать входной код как символ клавиатуры Дворака, находить его код в таблице и помещать его в стек. Новое определение должно иметь примерно следующий вид: : DKEY KEY (Слова, с помощью которых производится просмотр); В каком месте вашей программы будет использоваться новое определение DKEY? 8. Теперь вы решили ввести возможность работы оператора по выбору с клавиатуры Дворака или QWERTY. В начале программы нужно ввести меню Введите 1 для клавиатуры Дворака Введите 2 для клавиатуры QWERTY и в зависимости от того, какое число введено: 1 или 2 в переменную ?KBD, должен быть записан адрес, которым определяется тип используемой клавиатуры. Определите теперь слово NEWKEY таким образом, чтобы при вводе 2 оно действовало бы так же, как и KEY, а если была введена 1, то производила бы просмотр таблицы и изменение значения кода как требуется.
Если эти упражнения показались вам трудными, не падайте духом! Они являются примером достаточно высокого уровня программирования. Они также продемонстрировали вам, как можно делать то, что почти недоступно другим языкам программирования. Вам следует внимательно разобраться в этих упражнениях и поэкспериментировать с применением аналогичных приемов, чтобы хорошо прочувствовать возможности векторного исполнения и поисковых таблиц, когда они вам потребуются.


Еще о массивах и матрицах
В MMSFORTH и некоторых версиях включено слово ARRAY (массив), которое упрощает обращение с массивами (это слово не является эквивалентом слова ARRAY, определенного нами выше). В данном случае, если ввести 15 ARRAY DATA-STORE вы организуете массив из 16 элементов с именем DATA-STORE (хранение_данных). Если затем ввести 5 DATA-STORE в стек будет положен адрес пятого элемента (считая с 0). Поэтому 293 5 DATA-STORE ! занесет число 293 в пятый элемент массива и 5 DATA-STORE @ . выведет на экран число 293.
Слово ARRAY может быть определено в других версиях с некоторыми отличиями, поэтому нужно смотреть вашу документацию. Одна деталь может несколько смутить вас. В математике принято нумеровать элементы массива, начиная с единицы, в MMSFORTH нумерация начинается с нуля. Этим объясняется, почему 15 ARRAY DATA-STORE создает массив из 16 элементов: как было сказано, номер последнего элемента равен 15, а так как нумерация начинается с 0, всего получается 16 элементов. В MMSFORTH имеются также слова CARRAY (байтовый массив) и DARRAY (массив чисел двойной длины), назначение которых очевидно по аналогии с CVARIABLE и 2VARIABLE.
Форт позволяет также пользоваться двумерными массивами, т.е. матрицами. Глубокое рассмотрение применения матриц выходит за рамки нашего рассмотрения; интересующимся рекомендуем книгу "Essential Computer Mathematics" (Lipshutz, 1982). Однако здесь мы должны познакомиться с ними вкратце.
Матрицы можно просто представлять себе как таблицу, содержащую некоторое число рядов и столбцов. Рассмотрим, например, таблицу 5 293 1982 823 5 56 99 211 5
Она представляет собой квадратную матрицу размерности 3х3; на диагонали матрицы находятся пятерки. Элементы матрицы обозначаются двумя индексами, показывающими их положение (строка, столбец) следующим образом: x1,1 x1,2 x1,3 x2,1 x2,2 x2,3 x3,1 x3,2 x3,3 Матрица может быть представлена в памяти как линейный массив. В данном случае имеется 9 элементов. Поэтому можно определить CREATE MATA 18 ALLOT После этого программист должен взять на себя ответственность за установление связи между положением элемента в строке и столбце и номером элемента в линейном массиве.


Полезно иметь универсальную формулу. Если обозначить положение элемента в строке через i, а в столбце через j и за начальный (1,1) принять элемент, находящийся в левом верхнем углу, тогда номер элемента в линейном массиве определится как е = n(i-1) +j , где n - число столбцов в матрице. Таким образом, элемент (2,3) в матрице МАТА в линейном массиве будет иметь номер 6. Учитывая это, можно определить слово для вычисления положения элемента в матрице МАТА, если в стеке находятся значения i и j: : MATAADR SWAP 1- 3 * + 1- 2* ; следовательно, чтобы записать, например, число 1234 в элемент матрицы (3,1) , нужно ввести 1234 МАТА 3 1 MATAADR + !
Такова стандартная форма обращения с матрицами в Форте, однако в MMSFORTH и других версиях имеются более удобные средства.
Слово 2ARRAY в MMSFORTH позволяет определить и организовать доступ к элементам матрицы без дополнительных хлопот с обработкой индексов; будьте внимательны и не путайте слово 2ARRAY (двумерный массив, матрица) и DARRAY (линейный массив чисел двойной длины). Чтобы определить матрицу 2МАТА размерности 3х3, нужно ввести 2 2 2ARRAY 2МАТА Числа, определяющие размерность матрицы, которые помещаются в стек, должны быть на 1 меньше числа строк ц столбцов, так же как при создании линейного массива число в стеке должно быть на 1 меньше его размерности. После того как матрица (в данном случае 2МАТА) определена,адрес ее элемента можно найти, вводя имя матрицы, если номера строки и столбца уже находятся в стеке. Таким образом, чтобы напечатать элемент (2,3) матрицы 2МАТА, нужно ввести 1 2 2МАТА ? Понятно ли вам, почему числа в стеке на 1 меньше фактических индексов элемента матрицы?
В MMSFORTH имеются также слова 2ARRAY и 2CARRAY. Их назначение очевидно. В гл. 11 будет рассмотрено, как можно определить такие слова.
Векторы и матрицы широко используются в разделе математики, который называется линейной алгеброй и занимается решением систем уравнений. Они также широко используются в науке и технике, в экономике для решения систем дифференциальных уравнений.


В некоторых языках программирования, например Фортране, АПЛ, фактически имеется целый набор стандартных функций для работы с матрицами. Более сложные применения матриц выходят за рамки нашего обзора, однако есть множество задач повседневной практики, в которых полезно применяются матрицы, обычно в том же виде, как для хранения табличных данных. Мы рассмотрим несколько таких приложений в следующих упражнениях.
Упражнения
Упражнения 1-4 похожи на то, что мы уже делали, тем не менее проделайте их для подготовки к упражнению 5, которое показывает матрицу как таблицу. Считайте, что можно пользоваться словами MMSFORTH для работы с массивами. 1. Определите массив из 10 элементов под именем NUMBERS (числа). Теперь определите слово COUNT (счетчик), которое должно добавлять единицу к элементу матрицы, в зависимости от введенного веса, находящегося в пределах 0- 100. Если вес от 1 до 10, то нужно прибавить к элементу 1, если вес от 9 до 20, то к элементу 2, если между 19 и 30- к элементу 3 и т.д. (Указание: используйте операцию деления /.) 2. Теперь определите второй массив, WTSUM (суммарный вес> и слово !W, которое будет добавлять вес к соответствующему элементу массива WTSUM так же, как числа добавляются к массиву NUMBERS. 3. Определите слова .TOT-#S (суммарное количество), .TOT-WT (суммарный вес) и .AVE-WT (средний вес)," которые соответственно будут печатать суммарное количество, общий вес и средний вес по группам в одной строке. Примените форматный вывод. (Используйте цикл DO, если же вы не уверены, что сможете это сделать, обратитесь к ответам.) 4. Определите слово !WT таким образом, чтобы каждый раз, когда оно исполняется, исполнялось бы слово !WT и при этом суммарный вес, число деталей и средний вес по группам представлялись бы на экране друг под другом в виде таблицы из трех строк и 10 столбцов. 5. Определите матрицу размерности 2х10, в каждом столбце которой содержится весовая группа, а в строках- общее количество и суммарный вес соответственно. Теперь проделайте упражнения 1-4 снова, используя не линейный массив, а матрицу; при этом необходимо, чтобы все соответствующие элементы матрицы обновлялись после каждого исполнения слова !W. 6.


Может оказаться очень удобным дополнительный стек. Создайте массив из 160 элементов с именем NEWSTACK, который вел бы себя как дополнительный стек. Теперь определите переменную STACKPOS (положение в стеке) для расчета текущего положения указателя стека, т.е. адреса вершины стека. Инициализируйте переменную в STACKPOS, чтобы она указывала на первый элемент в NEWSTACK. Затем определите два слова: XPUSH и ХРОР ; XPUSH (послать в стек) должно брать число из обычного стека и засылать его в NEWSTACK, изменяя соответственно указатель. Слово ХРОР должно изымать число из стека NEWSTACK и помещать его в обычный стек, вновь изменяя указатель. Модифицируйте слова XPUSH и ХРОР, назвав их PUSH и POP, используя слова МАХ и MIN так, чтобы указатель не мог выйти за границы массива стека. 7. Определите слова NEWDROP, NEWDUP и NEWSWAP, которые делали бы то же самое, что и слова DROP. DUP и SWAP в обычном стеке.
О разном
Кроме переменных, создаваемых программистом, имеются различные переменные в самой Форт-системе, например BASE (основание системы счисления). Исторически переменные, которые являются частью самого языка Форт, называются переменными пользователя, поскольку в многопользовательской системе каждый пользователь может иметь свой собственный набор этих переменных, Мы понимаем, что это не совсем удачное название, поскольку в большинстве языков программирования переменная пользователя определяется им самим, а переменные, являющиеся частью самой системы, например константа число п (3.1415...), называются системными переменными. Тем не менее, следуя терминологии Форта, мы будем называть переменные, входящие в Форт-систему, пользовательскими переменными. На самом деле пользовательские переменные это совсем не то же самое, что переменные, которые определяет пользователь. Переменные, которые вы определяете, в качестве части своего определения могут включать число. Переменная пользователя содержит число, которое может храниться в какой-либо.удаленной от определения ячейке, и на практике не нужно беспокоиться о том, где хранится эта переменная, потому что она ведет себя точно так же, как и переменные, определенные программистом.


Можно рассматривать переменную пользователя как константу, значение которой представляет адрес, по которому хранится значение переменной. Поэтому к ней применимы операции @ (извлечь содержимое) и ! (записать), как к обычным переменным.
Второй вопрос также относится к терминологии. Вы уже заметили, что некоторые слова языка Форт используются только для определения других слов. К ним относятся : (двоеточие), VARIABLE (переменная), CONSTANT (константа), QUAN и ARRAY (массив). Такие слова называются определяющими словами. Они применяются для составления программ. Слово CREATE (создать)- также определяющее слово, но это особое определяющее слово, потому что оно может быть использовано для создания других определяющих слов. Вы уже видели, как с помощью слова CREATE можно определить другое определяющее слово VARIABLE, которое, в свою очередь, используется для определения других слов. Как вы увидите в гл. 11, применяя его совместно со словом DOES>CREATE, можно полностью изменить характер работы вашего языка Форт.
Следует сказать несколько слов о стиле программирования. Если вы пишете программу, нужно поделить ее на логические секции, каждая из которых имеет определенное назначение. Довольно существенно определить заранее все константы, переменные и массивы, по крайней мере в начале каждой программной секции, а еще лучше- в самом начале программы. Объявления констант и переменных (так иногда называют их определение) группируются вместе, что облегчает внесение исправлений и модификацию программы, если это необходимо.
Еще одно замечание о стиле программирования. Большинство новичков в Форте стараются использовать переменные неоправданно часто. Это в особенности присуще тем, кто знаком с языками программирования, где переменные обязательны. Всегда при использовании переменной быстродействие получается ниже, чем при использовании стека. И это одна из главных причин предпочтительного использования стека в Форте. Хотя может показаться, что переменными пользоваться проще, так как при этом не нужно следить за тем, что делается в стеке, за это приходится платить ценой увеличения затрат памяти и времени.


Практически переменные (или константы и массивы) следует использовать для достижения одной из следующих целей: 1) для размещения больших объемов данных, которые невозможно хранить в стеке из-за того, что длина стека непомерно увеличивается; 2) для размещения чисел, которые используются неоднократно в разных сильно разнесенных секциях программы, или 3) для улучшения удобочитаемости программы.
Фактически есть еще одна причина для использования переменных. Если вы пишете программу, которой воспользуетесь всего несколько раз и которая без введения переменных потребует очень сложных манипуляций в стеке, то, может быть, использование переменных будет окуплено сокращением времени написания программы в ущерб времени ее исполнения. Но нужно сознавать, что если это делать постоянно, то развиваются вредные привычки в программировании.
Выводы
Хотя в отличие от других языков программирования Форт применяет стек для большинства манипуляций с числами и передачи аргументов из одного слова в другое, это не исключает использования переменных (а также констант и массивов), как и в других языках- Форт также обеспечивает более глубокое управление этими средствами хранения данных, позволяя создавать собственные конструкции с помощью слова CREATE. А с помощью слов типа CMOVE, ' , FIND и EXECUTE можно сделать то, что вообще невозможно в других языках программирования.
И в отличие от других языков переменные и массивы в Форте не только запоминают данные, но также сами управляют программами и языком. Векторное исполнение программ дает мощное средство программирования, которого нет в других языках. Например, переопределение слов EMIT или KEY равносильно тому, чтобы совершенно изменить действие операторов PRINT и INPUT в Бейсике. Это в некоторой степени должно объяснить вам, почему во введении мы говорили, что Форт дает большую мощность в управлении вашим компьютером, чем другие языки. Вы еще убедитесь в этом более ощутимо в последних главах книги, где речь идет о создании новых определяющих слов и использовании Форт-ассемблера.Но пока мы и не рассмотрели множество более простых свойств языка.
Обязательной принадлежностью любого языка программирования являются управляющие структуры, т.е. процедуры типа IF...THEN (если- то), которые позволяют принимать решения о дальнейшем ходе программы на основании определенных условий. Мы рассмотрим такие управляющие структуры в гл. 7.

Содержание раздела