KRIEGSSPIELE!
Вы хотите отреагировать на этот пост ? Создайте аккаунт всего в несколько кликов или войдите на форум.

Броуди. Начальный курс программирования на языке ФОРТ. 1990

Страница 1 из 2 1, 2  Следующий

Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Вс Апр 11, 2021 12:29 am

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

Л.БРОУДИ
НАЧАЛЬНЫЙ КУРС ПРОГРАММИРОВАНИЯ НА ЯЗЫКЕ ФОРТ
Перевод с английского В.А.Кондратенко
М.: "ФИНАНСЫ И СТАТИСТИКА", 1990

К СОВЕТСКОМУ ЧИТАТЕЛЮ

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

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

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

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

Применяя Форт, пожалуйста, помните, что если вы делаете задачи сложнее, чем они есть на самом деле, то ничего в результате не выигрываете. Эффективная реализация простых алгоритмов, простота пользовательского интерфейса и представление простых предсказуемых результатов - только это имеет значение. Изучите Форт - и ваш компьютер поможет вам полностью решить все свои проблемы.
/Чак Мур, Вудсайд СА, Сентябрь 1988/

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

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

Надеюсь, вы по достоинству оцените машинный язык, который создан господином Муром, и, на мой взгляд, является гениальной разработкой.
/Лео Броуди/

ПРЕДИСЛОВИЕ К РУССКОМУ ИЗДАНИЮ

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

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

Книга Л.Броуди в этом отношении построена очень хорошо. Приемы "занимательной игры" в ней как бы блокируют наш предшествующий серьезный опыт и первоначально "вводят в нас" систему нужных понятий как нечто новое, вне зависимости от приобретенных ранее навыков. Однако занимательность не делает материал менее серьезным - книга даст вам полную и детальную информацию обо всех важнейших конструкциях языка. Кстати, следует отметить, что вашему вниманию предлагается второе издание книги, исправленное и дополненное в соответствии со Стандартом-83. Первое издание, ориентированное на пользователей систем фиг-Форта, вероятно, известно специалистам по оригиналу, а также по довольно широко распространенному переводу, выполненному Всесоюзным центром переводов. Когда вы прочтете достаточно много и Форт вам понравится, у вас возникнут естественные вопросы: "А как у нас? Есть ли в Советском Союзе подобные системы, развивались ли такие идеи? Используется ли Форт в нашей стране и каковы перспективы его дальнейшего применения?"

Прежде всего напомним читателю, что язык, аналогичный Форту, предлагался и в Советском Союзе. В Московском университете, о чем упоминается в книге Н.П.Брусенцова "Микрокомпьютеры" (М.: Наука, 1985), в экспериментальной ЭВМ "Сетунь-70" были реализованы подобные технические идеи (двухстековой организации). Здесь же, в МГУ, на базе этих идей и уже с учетом Форта разработана диалоговая система структурного программирования.

В американском варианте, ставшем международным, Форт начал развиваться у нас в 80-е годы, главным образом в Ленинграде. Работы велись в разных местах почти одновременно, в том числе на математико-механическом факультете Ленинградского университета. Первоначально усилия были направлены на создание инструментальных средств для всех выпускаемых в стране процессоров (ЕС, CM, K580, К1810, БЭСМ-6, "Эльбрус"). Описания отдельных групп и систем можно найти в книге С.Н.Баранова и Н.Р.Ноздрунова "Язык Форт и его реализации" (М.: Машиностроение, 1988 ). Недавно значительная часть ленинградской группы объединилась в научно-производственный кооператив "Форт-Инфо", ставящий перед собой цели развития и пропаганды средств программирования с использованием Форт-систем.

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

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

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

Следует упомянуть еще об одном развитии идеи Форта, не успевшем попасть в книгу Л.Броуди. И за рубежом, и в нашей стране успешно конструируются специальные "Форт-процессоры" - вычислительные микросхемы, в которые многие базовые операции языка Форт входят как машинные команды. Высокие технические характеристики уже разработанных процессоров побуждают к еще большему развитию и программных Форт-систем.
/И.В.Романовский, д-р физ.-мат. наук, проф./

ПРЕДИСЛОВИЕ

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

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

Свои первые результаты я получил в то время, когда работал с моделью IBM 1130 - ЭВМ "третьего поколения". Они показались мне настолько значительными, что я посчитал новый язык "языком машин четвертого (fourth) поколения" и назвал бы его FOURTH (четвертым), если бы модель 1130 допускала шестисимвольные идентификаторы. Таким образом FOURTH превратился в FORTH [Вперед.- Примеч.пер.] (Форт) - своеобразная игра слов.

"Сохранять простоту языка" - так примерно формулируется основной принцип, которым я руководствовался в процессе работы над Фортом и продолжаю руководствоваться при его применении. Элегантность решения - в его простоте. Простота достигается при глубоком изучении СУЩЕСТВА проблемы и осознается при получении правильного решения. Это положение следует особо подчеркнуть, поскольку оно противоречит устоявшемуся мнению о том, что мощность языка увеличивается с возрастанием его сложности. Простота гарантирует надежность, компактность и быстродействие Форт-программ, а значит, и доверие к ним пользователей.

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

Хотя я - единственный человек, перед которым никогда не стояла задача изучить Форт, мне хорошо известно, с какими это сопряжено трудностями. Как и при изучении любого естественного языка, приходится запоминать правила употребления множества слов. Шутливые пояснения и великолепно подобранные примеры существенно облегчают задачу для начинающих. Для тех же, кто уже имел дело с Фортом, просмотр книги будет подобен приятной прогулке и поможет освежить в памяти "знакомый пейзаж". Однако книга не настолько проста и популярна, чтобы показаться тривиальной. Безусловно, читая ее, вы иногда можете испытывать затруднения, но непременно узнаете много нового и о компьютерах и компиляторах, впрочем, как и о программировании.

Форт предоставляет человеку удобные средства общения с "умными" машинами, которыми он себя окружает. Следовательно, этот язык должен обладать свойствами естественных языков, включая компактность, разносторонность и расширяемость. Я считаю его лучшим языком для написания программ, изложения алгоритмов или объяснения работы ЭВМ и думаю, что, когда вы прочтете эту книгу, вам придется со мной согласиться.
/Чарльз Мур, автор языка Форт/

ОТ АВТОРА

В 1980г., когда готовилось первое издание книги, популярность Форта была еще не столь велика. Сегодня положение существенно изменилось. И если интерес к другим языкам то возрастал, то падал (пик популярности Паскаля прошел, на какое-то время воцарился язык Си, но уже главным претендентом "на трон" является Модула 2), к Форту он рос стабильно год от года. Несмотря на то что распространение Форта продолжается в свойственном для него темпе, выпуск Форт-процессора Ч.Мура, как нам кажется, вызовет новый взрыв эмоций у инженеров и программистов.

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

Уже после выхода первого издания был принят и получил признание Стандарт Форт-83. Это явилось главной причиной, побудившей нас приступить к переработке книги, так как она не соответствовала новому стандарту. Что касается синтаксиса, то, поскольку в настоящее время еще существует множество систем, написанных на Фиг-Форте, я включил в текст второго издания сноски для пользователей таких систем, где указаны расхождения со Стандартом-83. В остальном же я старался следовать более строгому стилю написания программ и принципам, изложенным в моей книге "Думаем на Форте" (Brodie, Leo, Thinking Forth. Englewood Cliffs, N.I.: Prentice-Hall, 1984). Необходимо также отметить, что мы перестали употреблять слово "экран" вместо слова "блок". Если ранее такое разделение имело смысл ("экран" обычно рассматривается как "блок", содержащий исходный текст), то теперь многие начинающие приходят в замешательство от наличия двух терминов.

В заключение мне хотелось бы выразить благодарность всем, кто так или иначе принимал участие в подготовке первого издания книги: Д.Сэндерсону, М.ЛаМанне, Дж.Дьювею, Э.К.Конклину, Э.Д.Разер - за консультации по технике и стилю программирования на Форте, К.Хэррису - за советы по методике обучения Форту и ряд написанных им упражнений, К.А.Розенберг - за редактирование и профессиональные замечания, а также за большую работу по форматизации страниц, С.Линстрот, К.Льюбисих, К.Уэвер, К.Кэролин, С.Б.Броуди, Д.Робертсу, Дж.Ритсчер, Д.Разер, В.Шоуз, Н.Эльберт, Б.Робертс, Дж.Дотсону, Б.Патерсону и Г.Фридлендеру - за техническую помощь. Я весьма признателен С.Б.Броуди за ценные критические замечания и конструктивные предложения и, конечно, Ч.Ш.Муру за создание Форта.

При подготовке второго издания существенную помощь мне оказали Дж.Дж.Кэссэди, представивший исходную версию ассемблера 8080, К.Хамбэкер, отрецензировавший переработанную рукопись книги, и М. Хэм, приславший мне множество писем с критическими замечаниями.
/Лео Броуди/

КОРОТКО О КНИГЕ

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

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

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

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

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


Последний раз редактировалось: Gudleifr (Вс Апр 18, 2021 12:53 am), всего редактировалось 2 раз(а)
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Вс Апр 11, 2021 12:33 am

ВВЕДЕНИЕ

ЧТО ТАКОЕ МАШИННЫЙ ЯЗЫК? (ВВЕДЕНИЕ ДЛЯ НАЧИНАЮЩИХ)

Новичок, впервые столкнувшийся с термином "машинный язык", может подумать: "На каком же таком языке, разговаривает компьютер? Наверное, человеку чрезвычайно трудно его понять. Выглядит этот язык, вероятно, как-нибудь так:

976#!@NX714&+

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b04810

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

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

Упомянутые выше языки, включая Форт, относятся к языкам высокого уровня. Начинающему важно понять разницу между языком высокого уровня и языком, понятным компьютеру. Все языки высокого уровня выглядят для программиста одинаково, независимо от того, на компьютере какой марки или модели будет выполняться соответствующая программа. Но каждый компьютер имеет свой внутренний, или "машинный", язык. Что же представляет собой машинный язык? Обратимся снова к примеру с марионеткой.

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

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b04910

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

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

ОБЛАСТИ ПРИМЕНЕНИЯ ФОРТА (ВВЕДЕНИЕ ДЛЯ ПРОФЕССИОНАЛОВ)

Особенно популярен Форт стал с 1978г., хотя применялся в основных областях науки, экономики и производства с начала 70-х годов. Где бы вы ни работали, скорее всего, ваша прикладная программа, написанная на Форте, будет выполняться более эффективно, чем на том языке, который вы применяете сейчас. Для того чтобы это понять, вы должны прочитать настоящую книгу, по возможности найти Форт-систему и попытаться с ней поэкспериментировать. В данном разделе вы найдете для себя ответы на два вопроса: "Что такое Форт?" и "Где он может использоваться?".
Форт многогранен. Его можно рассматривать как
- язык высокого уровня:
- язык Ассемблера;
- операционную систему;
- инструментарий для создания программ;
- некоторую концепцию разработки программного обеспечения.

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

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

Диалог - неотъемлемое свойство Форта. Новые слова могут быть скомпилированы таким образом, что программист получит возможность сразу же проверять каждую новую команду и следить за состоянием своей программы посредством немедленной обратной связи. (Во многих языках программирования для этого требуется загрузка текстового редактора, редактирование, выход из текстового редактора, загрузка компилятора, компиляция, выход из компилятора, загрузка редактора связей и т.д.). ИТЕРАЦИОННЫЙ ПОДХОД, при котором оптимальное решение находится в процессе тестирования программных моделей, наиболее приспособлен к интегрированной среде с небольшим временем реагирования на запросы, а именно такую среду и обеспечивает Форт.

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

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

Помимо всего код Форта компактен. Прикладные программы, написанные на Форте, занимают меньший объем памяти, чем аналогичные программы, созданные с помощью традиционного АССЕМБЛЕРА! Написанная на Форте операционная система вместе со стандартным набором слов занимает менее 8К байт. Вся среда Форта спокойно умещается в пространстве, составляющем 16-32К. Динамическая среда для объектной прикладной программы может потребовать объем памяти менее 1К байт.

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

Ниже приводится несколько примеров применения Форта.

ИСКУССТВО. Форт используется для управления оборудованием при съемках видеоклипов некоторых рок-групп, а также бегущей строкой рекламы шоу Била Косби. В музыкальной студии электронных инструментов Государственного университета Сан-Хосе язык MASC, являющийся расширением Форта, позволяет композиторам писать музыку для аналоговых синтезаторов.

ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ДЛЯ ПЕРСОНАЛЬНЫХ КОМПЬЮТЕРОВ И БИЗНЕСА. Форт применялся при разработке учетных программ "Назад к Бейсику" фирмы Peachtree, пакета СУБД фирмы Savvy, системы Симплекс фирмы Quest Research (интегрированная база данных со средствами текстовой обработки и передачи сообщений, макинтошеподобными окнами и графикой) и комплексов Дэйта Эйс и Мастер Тайп. Фирма Bell Canada выбрала Форт для реализации программ аварийной диагностики телефонной связи и сети баз данных, где единственный процессор 68000 обслуживает 32 терминала и базу данных объемом 200МБ. Фирма Cycledata использует Форт для создания и сопровождения базы данных курса акций с выдачей информации клиентам в реальном времени.

СБОР И АНАЛИЗ ДАННЫХ. Форт применяется во многих крупных обсерваториях планеты. Например, компьютер PDP-11/34 с Форт-системой полностью управляет обсерваторией, в том числе телескопом, куполом, несколькими электронно-лучевыми трубками, строчнопечатающим устройством, дисководами с гибкими дисками, и в то же время обеспечивает сбор данных по инфракрасному излучению из космоса, анализ этих данных и выдачу результатов на графический монитор. Лесная служба США с помощью системы распознавания образов, написанной на Форте, производит анализ и переработку контурных карт. Фирма Dysan применяет написанную на Форте инструментальную систему управления качеством, которая в среде IBM PC работает с битовыми шаблонами, хранящимися на гибких дисках. Устройства измерения глубины, управляемые Фортом, используются на буксирах Миссисипи. NASA, Центр систем океана ВМС США и Центр вооружений ВМС США применяют Форт для различных видов сложного анализа данных. В прикладные программы такого рода часто включаются написанные на Форте программы быстрого преобразования Фурье и Уолша, численного интегрирования, а также Форт-программы, реализующие другие математические методы.

ЭКСПЕРТНЫЕ СИСТЕМЫ. Для компании General Electric Corporate Research and Development на Форте была создана экспертная система диагностики дизель-электровозов. Форт также использовался компанией Applied Intelligence Systems and IRI, Inc. для промышленного прогнозирования. Центр исследований проблем сна Станфордского университета применяет созданную на Форте экспертную систему в целях идентификации моделей сна.

ГРАФИКА. Программа Изель и ее приемник Люмена, созданные фирмой Time Arts, Inc.,- программы-художники. Обе написаны на Форте и продаются в сочетании с известными графическими системами и системами автоматизированного проектирования.

МЕДИЦИНА. Единственный в Главном госпитале компьютер PDP-11 дает возможность одновременно обслуживать большую базу данных, где хранится информация о пациентах, управлять 32 терминалами и оптическим считывателем, делать анализ крови и измерять пульс больного в реальном времени, осуществлять статистический анализ информации из базы данных для установления соответствия между физическими симптомами заболевания, правильностью диагноза и результатами лечения. Отдел медицинских систем NCR в своей системе 9300, применяемой в больничном обслуживании, также используют Форт. Административный центр по исследованию и совершенствованию реабилитации ветеранов применяет Форт для создания устройств обслуживания инвалидов, включая ультразвуковой детектор, который переводит команды, посылаемые человеком (кивок, поворот головы), в сигналы управления устройством, например креслом на колесах с электроприводом.

ПЕРЕНОСНЫЕ "РАЗУМНЫЕ" УСТРОЙСТВА. Существует множество разных приборов с встроенными Форт-программами: прибор для диагностики заболеваний сердечно-сосудистой системы, самоходный датчик воспламенения, созданный двумя компаниями, прибор для определения относительной влажности различных сортов зерна, транслятор с языка Craig и т.д.

УПРАВЛЕНИЕ ПРОЦЕССАМИ. Лаборатория Jet Propulsion и компания McDonnel Douglas Astronoutics независимо друг от друга выбрали Форт для разработки приборов, применяющихся при создании промышленных материалов в условиях невесомости. Lockheed California и TRW каждая по-своему используют Форт при создании радарных антенн. Фирма Northrup применяет Форт в качестве стандартного языка тестирования. С помощью Форта фирма Johnson Filaments управляет лазерным микроизмерительным роботом при измерении диаметра пластиковых волокон. На предприятиях Union Carbide Форт используется при разработке лабораторных средств автоматизации и создании автоматизированных систем управления процессами.

РОБОТЫ. Управляемая голосом сервосистема, применяемая на TRW, написана на Форте. С помощью подвесной автоматической видеокамеры Скайкэм осуществляется трансляция футбольных матчей, а камера фирмы Elicon создает фрагменты для повторного показа. Диапазон других применений Форта - от управления разгрузкой и погрузкой багажа на главной авиалинии США до сортировки персиков на Калифорнийском консервном заводе.

ФОРТ-ПРОЦЕССОРЫ. R65F11 и R65F12, созданные Rockwell International Corp., представляют собой восьмиразрядные процессоры, размещающие 133 Форт-слова на одном кристалле ПЗУ. Вспомогательные кристаллы ПЗУ содержат дополнительные слова Форта, полезные для создания программного обеспечения. Семейство МА2000 National Semiconductor Corp. состоит из набора модулей с интерфейсом через стек, формирующих в совокупности полный автономный Форт-компьютер. Семейство высокоскоростных процессоров Novix NC4000 представляет собой Форт-кристаллы, в которых команды Форта высокого уровня выполняются за один такт.

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

Если все изложенное здесь вас заинтересовало, значит, вы уже готовы приступить к изучению Форта.
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Пн Апр 12, 2021 12:32 am

ГЛАВА 1. ОСНОВЫ ФОРТА

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

ЖИВОЙ ЯЗЫК

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

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

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

: СТИРАЛЬНАЯ-МАШИНА СТИРАТЬ ВЫКРУЧИВАТЬ ПОЛОСКАТЬ ВЫКРУЧИВАТЬ ;

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b02310

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

: ПОЛОСКАТЬ НАЛИТЬ-ВОДУ СТИРАТЬ ВЫЛИТЬ-ВОДУ ;

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

: НАЛИТЬ-ВОДУ КРАНЫ ОТКРЫТЬ ДО-НАПОЛНЕНИЯ КРАНЫ ЗАКРЫТЬ ;

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

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

Возможность определять слова в терминах других слов называется РАСШИРЯЕМОСТЬЮ. Расширяемость является основой хорошего стиля программирования и позволяет достичь необходимого уровня мощности языка.

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

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

ДИАЛОГ

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

Форт называется ДИАЛОГОВЫМ языком, потому что его команды выполняются сразу, как только вы их вводите. В качестве примера (вы можете его выполнить самостоятельно) рассмотрим процесс объединения простых команд в более сложные. Мы будем использовать некоторые слова Форта для управления экраном дисплея или печатающим устройством. Но прежде познакомимся с механизмом "диалога" посредством клавиатуры терминала.

Займите место за своим терминалом (для некоторых он будет воображаемым). Возможно, кто-нибудь великодушно предоставит вам условия для занятий, в противном случае вам придется внимательно интерпретировать самому все команды, предназначенные вашему компьютеру. Нажмите клавишу RETURN. Компьютер ответит: _ok ("все в порядке"), что означает приглашение к работе. С помощью клавиши RETURN вы передаете компьютеру свой запрос. Ответ _ok свидетельствует о том, что ваш запрос выполнен, причем без единой ошибки. Мы пока ни о чем не просили, поэтому компьютер послушно ничего не выполнил и выдал приглашение _ok.

Теперь введите

15 SPACES

что означает 15 ПРОБЕЛОВ.

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

В дальнейшем мы будем использовать обозначение <return> в тех местах, где вы должны нажимать клавишу RETURN, и подчеркивать ответы компьютера, чтобы отличать их от других символов (даже если сам компьютер и не подчеркивает свои ответы).
Что произойдет в такой ситуации:

15 SPACES<return>_______________ok

Как только вы нажали клавишу ввода, компьютер выведет 15 пробелов и затем, выполнив ваш запрос, выдает: ok (после 15-го пробела).
Наберите на клавиатуре следующее:

42 EMIT<return>_*ok

Фраза "42 EMIT" приказывает компьютеру вывести символ * (мы обсудим эту команду позднее). Компьютер выводит требуемый символ, а затем ok.
На одной строке мы можем помещать несколько команд. Например:

15 SPACES 42 EMIT 42 EMIT<return>_______________**ok

На этот раз компьютер выводит 15 пробелов и две звездочки. Отметим, что при вводе слов и/или чисел их можно разделять любым количеством пробелов (как вам удобно), но между ними должен быть ХОТЯ БЫ ОДИН ПРОБЕЛ, чтобы компьютер мог различать слова и/или числа.
Вместо того чтобы всякий раз вводить фразу

42 EMIT

давайте определим ее как слово STAR (ЗВЕЗДОЧКА). Итак, введите:

: STAR 42 EMIT ;<return>_ok

Здесь STAR - имя, а 42 EMIT - определение. Заметьте, что мы отделили двоеточие и точку с запятой от соседних с ними слов одним пробелом. Чтобы определения Форта легче воспринимались, условимся отделять имя определения от собственно определения тремя пробелами.

После того как вы наберете на клавиатуре приведенное выше определение и нажмете клавишу RETURN, компьютер ответит вам: ok, т.е. он распознал ваше определение и запомнил его. Введите далее:

STAR<return>_*ok

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

Другим определенным системой словом является CR, которое обеспечивает возврат каретки и перевод строки на вашем терминале. Обязательно почувствуйте разницу в использовании клавиши RETURN и словом Форта CR. В качестве примера наберите на клавиатуре

CR<return>_
_ok

Компьютер осуществил возврат каретки, а затем вывел ok (на следующей строке). Введите такой текст:

CR STAR CR STAR CR STAR<return>
_*
_*
_*ok

Поместим CR в определение слова MARGIN (ПОЛЕ ПРОБЕЛОВ):

: MARGIN CR 30 SPACES ;<return>_ok

Теперь вы можете ввести следующее:

MARGIN STAR MARGIN STAR MARGIN STAR<return>

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

Комбинация слов MARGIN STAR пригодится нам в дальнейшем, поэтому введем определение BLIP (ТОЧКА):

: BLIP MARGIN STAR ;<return>_ok

Нам также предстоит выводить горизонтальные последовательности звездочек. Для этой цели введем следующее определение (его назначение мы объясним позднее):

: STARS 0 DO STAR LOOP ;<return>_ok

Итак, мы можем ввести:

5 STARS<return>_*****ok

или

35 STARS<return>_***********************************ok

или любое представимое число звездочек! Однако нельзя задавать сочетание "0 STARS", в особенности для систем Форта-83. Подробнее об этом речь пойдет в гл.6.

Нам необходимо слово, которое выполняет команду MARGIN, а затем выводит пять звездочек. Определим слово BAR (ПОЛОСКА):

: BAR MARGIN 5 STARS ;<return>_ok

после чего можно ввести строку:

BAR BLIP BAR BLIP BLIP CR

В результате вы получите букву F, составленную из звездочек:

*****
*
*****
*
*

В заключение определим слово для этой новой процедуры. Назовем его F:

: F BAR BLIP BAR BLIP BLIP CR ;<return>_ok

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

0 ( Большая буква F )
1 : STAR 42 EMIT ;
2 : STARS 0 DO STAR LOOP ;
3 : MARGIN CR 30 SPACES ;
4 : BLIP MARGIN STAR ;
5 : BAR MARGIN 5 STARS;
6 : F BAR BLIP BAR BLIP BLIP CR ;
7
8

СЛОВАРЬ

Каждое слово (его имя и определение) заносится в так называемый словарь Форта. Этот словарь, когда вы начинаете писать на Форте, уже содержит какое-то количество слов, но вы можете помещать в него и "свои" слова. При определении нового слова оно переводится в словарную форму и добавляется в словарь. Такой процесс называется КОМПИЛЯЦИЕЙ.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b02410

К примеру, если вы вводите строку

: STAR 42 ЕМIТ ;<return>

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

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

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

STAR 30 SPACES<return>

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b02510

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b02610 Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b02710

Найдя нужное слово в словаре, интерпретатор передает его определение слову с именем EXECUTE [Слово EXECUTE (ВЫПОЛНИТЬ) в одном из вариантов переводится как "казнить". Отсюда ассоциации с палачом.- Примеч.пер.], которое выполняет это определение (в нашем случае оно инициирует печать звездочки). Интерпретатор вновь выдает вам _ok.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b02810

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

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

XLERB<return>_XLERB_?

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b02910

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

Упоминали ли мы о том, что символ ":" является словом? Так, если вы вводите этот символ:

: STAR 42 EMIT ;<return>

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b03010

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b03110 Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b03210

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b03310 Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b03410

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

КАК ПРАВИЛЬНО "ОБЪЯСНЯТЬСЯ" НА ФОРТЕ?

В Форте словом считается отдельный символ или группа символов, имеющие определение. Почти любое сочетание символов может употребляться для именования слов. Ниже приводятся группы символов, которые нельзя использовать для этих целей, поскольку компьютер может "подумать" [Для философов: конечно, компьютер не "думает", но, к сожалению, очень трудно подобрать слово, точно отражающее его действия. Поэтому условимся считать, что компьютер "думает"], что вы собираетесь выполнить одну из следующих операций:
- RETURN - завершить набор символов;
- BACKSPACE - исправить ошибочно набранный символ;
- SPACE (пробел) - указать конец слова.

Теперь разберем слово, имя которого состоит из двух знаков пунктуации. Это слово ." и произносится оно как "точка-кавычка". Вы можете применять его внутри определения для вывода "строки" текста на своем терминале. Например [В Форте определен базовый набор слов. В книге такие слова даются на языке оригинала. В большинстве приводимых здесь примеров слова, определяемые пользователем, написаны по-русски. Версии Форта, распространенные в СССР, позволяют это делать.- Примеч.ред.]:

: ВСТРЕЧА ." Привет, я говорю на Форте " ;<return>_ok

Мы только что определили слово с именем ВСТРЕЧА. Его определение состоит только из одного слова, ." , за которым следует текст, подлежащий выводу. Знак кавычки в КОНЦЕ текста не будет выведен; он отмечает конец текста и называется ОГРАНИЧИТЕЛЕМ. Специфицируя слово ВСТРЕЧА, не забудьте в конце поставить символ ; - знак конца определения.
Давайте выполним слово ВСТРЕЧА:

BCTPEЧA<return>_Привет,_я_говорю_на_Форте_ok

ПЕРИОД ИСПОЛНЕНИЯ И ПЕРИОД КОМПИЛЯЦИИ

При изучении Форта вам встретятся два термина: ПЕРИОД ИСПОЛНЕНИЯ и ПЕРИОД КОМПИЛЯЦИИ. Смысл их достаточно прост. Если вы компилируете определение некоторого слова, то речь идет о периоде его компиляции, а если вы выполняете слово - о периоде исполнения.

Например, в только что приведенном определении ВСТРЕЧА компьютер во время компиляции выдал вам просто "ok", а во время выполнения указанного определения вывел на экран следующее: "Привет, я говорю на Форте". В случае предварительно определенных слов, таких, как CR, период компиляции приходится на время создания Форт-системы, а период выполнения наступает всякий раз, когда вы обращаетесь к этим словам (или к словам, которые в свою очередь обращаются к ним).

Посмотрим на эту ситуацию с другой стороны. Когда мы компилируем слово ВСТРЕЧА, компилятор Форта (или набор так называемых "компилирующих слов", осуществляющих компиляцию) на самом деле исполняется. Поэтому для слова ВСТРЕЧА это период компиляции, а для компилирующих слов Форта - период исполнения.

Термины ПЕРИОД КОМПИЛЯЦИИ и ПЕРИОД ИСПОЛНЕНИЯ будут вам особенно полезны при рассмотрении более сложных слов.
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Пн Апр 12, 2021 12:54 am

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

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b03510

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b03610

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b03710

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b03810

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b03910

Многие калькуляторы и компьютеры решают арифметические задачи аналогичным способом. Вы можете этого и не знать, но компьютер действительно запоминает числа в различных участках своей оперативной памяти и затем выполняет над ними необходимые операции. Центральный участок памяти, где временно запоминаются числа, над которыми затем будет выполнена операция, называется СТЕКОМ. Числа "вталкиваются в стек" и ВПОСЛЕДСТВИИ операции выполняются именно над данными числами [Для пользователей карманных калькуляторов. В обычных калькуляторах, как правило, применяется и стек, и постфиксная запись арифметики]. Поясним это на примере. Введите со своего терминала строку

3 4 + .<return>_7_ok

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b04010

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b04110

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b04210

Следующее слово из входного потока ДОЛЖНО БЫТЬ найдено в словаре. Знак + был предварительно определен как последовательность таких действий: "взять два верхних числа из стека, просуммировать их и поместить результат обратно в стек".

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b04310

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

ПОСТФИКСНАЯ ЗАПИСЬ

"Но, позвольте,- скажете вы,- почему на Форте нужно писать

3 4 +

а не

3 + 4

как это принято?" Дело в том, что в Форте применяется ПОСТФИКСНАЯ запись (называемая так потому, что знак операции располагается ПОСЛЕ чисел) вместо ИНФИКСНОЙ (где знак операции располагается МЕЖДУ числами), поэтому все слова, которым "требуются" числа, могут их взять из стека. Например, слово + берет из стека два числа и складывает их, слово . берет одно число и выводит его, SPACES берет одно число и выводит соответствующее ему количество пробелов, EMIT берет число, которое изображает какой-либо символ, и выводит этот символ. Даже слово STARS, которое мы определили сами, берет число из стека и выводит заданное этим числом количество звездочек.

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

Ранее отмечалось, что Форт позволяет использовать слово любым из двух способов: либо называя его, либо применяя в определении другого слова, тем самым именуя то ДРУГОЕ слово. Постфиксная запись является частью механизма, который предоставляет вам такую возможность. Например, предположим, что вам требуется слово, которое всегда прибавляет число 4 к любому числу, находящемуся в стеке. Назовем это слово

ПЛЮС-ЧЕТЫРЕ

что означает "больше на четыре", и определим его следующим образом:

: ПЛЮС-ЧЕТЫРЕ 4 + ;<return>

Выполним проверки

3 ПЛЮС-ЧЕТЫРЕ .<return>_7_ok

и еще раз

-10 ПЛЮС-ЧЕТЫРЕ .<return>_-6_ok

Число 4, находящееся ВНУТРИ определения, помещается в стек таким же образом, как если бы оно находилось вне определения. Затем слово + складывает два числа, хранящихся в стеке. Так как операция сложения всегда выполняется над содержимым стека, тот факт, что число 4 пришло из определения, а 3 нет, не имеет значения.

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

РАБОТА СО СТЕКОМ

Мы уже продемонстрировали некоторые методы работы со стеком и постфиксную запись. Теперь рассмотрим этот вопрос более детально.

Стек Форта функционирует по принципу "последним занесен - первым выбран" (LIFO). Вы это видели в приведенном выше при мере. Число 3 было занесено в стек первым, а затем в него занесли число 4, "протолкнув" вглубь стека тройку. Позднее при выполнении операции сложения машина выбрала первым число 4, так как оно находилось в вершине стека.

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

2 4 6 8 . . . .<return>_8_6_4_2_ok

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b04410

Система читает символы из входного потока слева направо. При вводе крайнее правое значение на экране дисплея будет находиться последним в стеке, т.е. в его ВЕРШИНЕ. При выводе крайнее правое значение поступает на экран с самого ДНА стека.

Будьте внимательны, чтобы не допустить ошибки. Наберите на клавиатуре

10 20 30 . . . .

(четыре точки) и нажмите клавишу RETURN. В результате вы получите :

10 20 30 . . . .<return>_30_20_10_._Стек пуст

Каждая точка удаляет из стека одно значение. Четвертая точка "обнаружит", что в стеке нечего взять для вывода на дисплей, о чем вы и получите сообщение [Для любознательных. На самом деле на экран выводится всегда то значение, которое находится в вершине стека. Поэтому если в стеке ничего нет, выводится следующее значение, находящееся глубже последнего, а именно нуль. И только тогда обнаруживается ошибка. Ошибочное слово (в нашем случае точка) выводится на экран, а за ним - сообщение об ошибке].

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b04510

Такая ошибка называется потерей элемента стека. (Заметьте, что потеря элемента стека вовсе не "ok"). В противном случае, когда вы исчерпали емкость стека, возникает ситуация его переполнения. Однако стек настолько глубок, что такое событие практически нереально, если только вы не совершите что-то из ряда вон выходящее.

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

: СТОРОНЫ STAR 5 SPACES STAR ;

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

*_____*

Предположим, вы хотите научиться рисовать прямоугольник ЛЮБОГО размера. Тогда вы должны не заключать число 5 в ОПРЕДЕЛЕНИИ, а передавать его слову СТОРОНЫ как АРГУМЕНТ. В этом случае ваше определение будет выглядеть так:

: СТОРОНЫ STAR SPACES STAR ;

Теперь нужно обращаться к слову СТОРОНЫ, скажем, таким образом:

5 СТОРОНЫ

При этом несмотря на то, что при первом употреблении слова STAR в стек заносится число 42, предназначенное для EMIT, заданное вами число 5 будет благополучно дожидаться в стеке выполнения своего слова SPACES.

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

Передачу аргументов определениям легко и удобно производить через стек [Для знатоков семантики. В математике слово аргумент ассоциируется с независимой переменной некоторой функции. Компьютерные лингвисты заимствовали этот термин для обозначения значений, передаваемых функциям или подпрограммам. Кроме того, для описания аналогичных объектов используется слово параметры]. Но при пользовании стеком вся ответственность за соответствие его состояния так называемой "стековой нотации" ложится на вас. Иными словами, при определении некоторого слова вы должны представлять себе, какие аргументы это слово при своем выполнении выбирает из стека, а какие в нем оставляет или "возвращает", и при выполнении слова убедиться в том, что в действительности так и происходит.

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

Чтобы иметь возможность визуально согласовывать стековые эффекты при выполнении различных слов, программисты применяют специальную запись - СТЕКОВУЮ НОТАЦИЮ. Такой комментарий обязательно должен присутствовать в программных листингах и глоссариях (вид документации, в которой содержатся перечни слов, используемых в вашей прикладной программе). Прежде чем показать вам, как выглядит стековая нотация, обсудим вопрос о том, что представляют собой комментарии в Форте.

Комментарий - это некоторая информация, предназначенная только для человека: она ни выполняется, ни компилируется. В Форте слово ( , ЛЕВАЯ КРУГЛАЯ СКОБКА, символизирует начало комментария. Текстовому интерпретатору предписывается осуществлять пропуск последующего текста до тех пор, пока не встретится правая круглая скобка, означающая конец комментария. Поскольку ( является словом, вы обязаны отделять этот символ пробелом так же, как и в случае применения слова ." [Для начинающих. Закрывающая круглая скобка не является словом. Это просто символ, который служит ограничителем для слова ( . (Вспомните, что для слова ." ограничителем является символ " )].

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b04610

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

: НИЧЕГО ( это слово ничего не выполняет) ;

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

( -- )

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

: STAR ( -- ) 42 EMIT ;

Напоминаем, что стековая нотация ничего не значит для Форта, но очень помогает программисту, пытающемуся разобраться в конкретной программе. Если некоторое слово ДОЛЖНО ВЫБРАТЬ из стека аргументы, то эти аргументы перечисляются СЛЕВА от двойного дефиса. Например, стековая нотация для слова . ("точка") выглядит следующим образом:

( n -- )

(Буква n заменяет число). Если слово ВОЗВРАЩАЕТ стеку аргументы, то они перечисляются СПРАВА от двойного дефиса. Стековая нотация для слова + имеет вид:

( n1 n2 -- сумма )

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

Когда вы указываете несколько аргументов справа или слева от дефиса, необходимо строго соблюдать порядок их размещения. Запомните следующее правило: КРАЙНИЙ ПРАВЫЙ объект в стековой нотации является ВЕРХНИМ элементом стека.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b04710

Это легко запоминается, поскольку перечисление аргументов в стековой нотации совпадает с порядком их ввода. Если вы вводите в стек для некоторого слова числа 1 2 3, то стековый комментарий будет таков:

( 1 2 3 -- )

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

СЛОВА

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

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

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

ОСНОВНЫЕ ТЕРМИНЫ

ВЫПОЛНЕНИЕ -- Применительно к слову - это выполнение операций, заданных в скомпилированном определении данного слова.
ВХОДНОЙ ПОТОК --  Текст, который должен быть прочитан текстовым интерпретатором. Это может быть текст, только что набранный на терминале или ранее записанный на диск.
ГЛОССАРИЙ -- Список слов, определенных в Форте, с их стековыми нотациями (какого рода информация помещается в стек перед выполнением слова и что остается в стеке в качестве результата).
ИНТЕРПРЕТАЦИЯ -- Чтение из входного потока при обращении к интерпретатору текста и поиск каждого встретившегося слова в словаре. В случае неудачи это слово трактуется как число.
ИНФИКСНАЯ ЗАПИСЬ -- Метод записи: знак операции располагается между операндами, над которыми она выполняется, например 2 + 5.
КОМПИЛЯЦИЯ -- Генерация по исходному тексту элемента словаря (внутренняя форма определения). Следует отличать от EXECUTE.
ПЕРЕПОЛНЕНИЕ СТЕКА -- Аварийная ситуация, которая создается в том случае, когда вся область памяти, отведенная под стек, заполнена данными.
ПОСТФИКСНАЯ ЗАПИСЬ -- Метод записи: знак операции следует за операндами, над которыми эта операция выполняется, например 2 5 + . Также известен как обратная польская запись.
ПОТЕРЯ ЭЛЕМЕНТА СТЕКА -- Аварийная ситуация, которая возникает в том случае, когда для выполнения какой-либо операции требуется элемент из стека, а стек пуст.
РАСШИРЯЕМОСТЬ -- Характеристика языка, означающая, что программист может добавлять новые средства или модифицировать существующие.
СЛОВАРЬ -- В Форте это перечень слов и определений, включающий в себя как "системные" определения (созданные при генерации системы), так и "пользовательские" (которые создаете вы сами). Словарь размещается в памяти компьютера в компилируемой форме.
СЛОВО -- В Форте это имя определения.
СТЕК -- Участок памяти, в который данные помещаются и из которого они удаляются по принципу "последним пришел - первым обслужен" (LIFO).
LIFO -- Принцип функционирования стека (последним пришел - первым обслужен). Так, коробка для хранения теннисных мячей имеет структуру LIFO: последний помещенный в нее мяч вы выбираете первым.

УПРАЖНЕНИЯ
Прежде чем вы приступите к решению задач, запомните следующее простое правило: каждое из перечисленных ниже слов должно завершаться соответствующим символом:

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

[Очевидно, это неправильно, "точка с запятой" - не символ, а слово. Перед ним нужен пробел!- G.]

1.1. Определите слово с именем ДАР, которое при своем выполнении выдаст название какого-то подарка. Например, вы можете определить

: ДАР ." подставку для книг" ;

Теперь определите слово с именем ДАРИТЕЛЬ, которое выводит на печать имя некоего лица, а затем слово с именем СПАСИБО, определение которого включает вновь созданные слова ДАР и ДАРИТЕЛЬ и выводит на печать сообщение, аналогичное следующему:

_Дорогая_Маша,
____спасибо_за_подставку_для_книг._ok

2. Определите слово с именем МИНУС-ДЕСЯТЬ, которое выбирает некоторое число из стека, вычитает 10 и помещает полученный результат в стек. (ПОДСКАЗКА: вы можете использовать слово +). Не забудьте включить в определение стековую нотацию.

3. После того как вы введете слова при решении упр.1, переопределите слово ДАРИТЕЛЬ так, чтобы можно было вывести на печать чье-то другое имя (не переопределяя слово СПАСИБО), выполните последнее снова. Сможете ли вы объяснить, почему слово СПАСИБО выводит на печать имя первого дарителя?
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Вт Апр 13, 2021 12:31 am

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

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

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

Ниже приводятся четыре простейшие операции над целочисленными значениями, записанные на языке Форт [ДЛЯ НЕМАТЕМАТИКОВ. Хотя данная глава и напоминает немного учебник по алгебре, решение математических задач - всего лишь небольшая часть из того, что вы сможете делать с помощью Форта. Позднее вы познакомитесь с другими применениями Форта. Здесь же уместно напомнить, что целые числа - это такие круглые числа, как ..., - 3, -2, -1, 0, 1, 2, 3, ..., а целочисленная арифметика (что достаточно логично) - операции над целыми числами].

+ плюс ( n1 n2 -- сумма) сложение ( n1 + n2 )
- минус ( n1 n2 -- разность) вычитание ( n1 - n2 )
* звездочка ( n1 n2 -- произведение) умножение ( n1 * n2 )
/ слэш ( n1 n2 -- частное) деление ( n1 / n2 )

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

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

17 5 + . _22_ok

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

7 8 * . _56_ok

Как видите, знак операции следует за значениями. Если же вы производите вычитание и деление, необходимо учитывать ПОРЯДОК СЛЕДОВАНИЯ ЗНАЧЕНИЙ ("7 - 4" не эквивалентно "4 - 7").
Запомните следующее правило: для записи выражения в постфиксной форме достаточно передвинуть знак операции в конец этого выражения:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b00010

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

7 - 4 =

наберите на клавиатуре

7 4 - . _3_ok

[ДЛЯ НАЧИНАЮЩИХ, которым НРАВИТСЯ РАЗВЛЕКАТЬСЯ ЗА ТЕРМИНАЛОМ. Если вы из тех, кто любит постигать суть вещей, не читая руководства, вы неизбежно столкнетесь с рядом проблем. Во-первых, как уже отмечалось, описанные выше операции являются ЦЕЛОЧИСЛЕННЫМИ ОПЕРАЦИЯМИ. Это означает не только то, что вы не имеете права их выполнять над дробными числами, например

10.00 2.25 +

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

21 4 / . _5_ok , а не _5.25_ok

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

10000 10 *

или перемножить подобные большие числа, то получите неожиданный результат. Поэтому мы вас предупреждаем, что все операции, о которых шла речь выше, а также операция, для вывода результата могут выполняться только над числами, лежащими в диапазоне от -32768 до 32767. Эти числа называются ЧИСЛАМИ ОДИНАРНОЙ ДЛИНЫ СО ЗНАКОМ.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b00110

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

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

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

20 4 / . _5_ok

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b00210

Как поступить, если необходимо выполнить несколько операций? Например:

4 + (17 * 12)

Как известно, сначала нужно выполнить операцию, указанную в скобках, т.е. 17 умножить на 12, а ЗАТЕМ добавить четыре. На Форте это будет выглядеть так:

17 12 * 4 + . _208_ok

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b00310

Числа 17 и 12 помещаются в стек. Слово * перемножает их и возвращает результат в стек.
Далее число 4 помещается в стек над числом 204. Слово + "выкатывает" суммирующую машину и складывает эти два числа, а в стек возвращается только результат.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b00410

Предположим, вы хотите сложить пять чисел. Вы можете это сделать на Форте, скажем, так:

17 20 + 132 + 3 + 9 + . _181_ok

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b00510

Еще одна интересная задача:

(3 + 9) * (4 + 6)

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

3 9 + 4 6 + * . _120_ok

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b00610

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

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

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

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

ab + c = 2 * 3 + 4 = 10

У вас должно получиться следующее:

a b * c +

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

2 3 * 4 + . _10_ok

1. c(a+b) = 3 (4+5) = 27

2. ab/100 = (80*90)/100 = 72

3. (3a-b)/4 + c = ((3*9)-7)/4 + 2 = 7

4. (a+1)/4 = (7+1)/4 = 2

5. x(7x+5) = 10*((7*10)+5) = 750

Преобразуйте следующие выражения из постфиксной формы в инфиксную:

6. a b - b a + /

7. a b 10 * /

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

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

Допустим, вы хотите перевести различные единицы измерения в дюймы. Вам известно, что 1 ярд = 36 дюймам и 1 фут = 12 дюймам [1 дюйм = 2.54см, 1 фут = 30.48см, 1 ярд = 0.914м.- Примеч.пер.]. Поэтому вы можете определить эти два слова следующим образом:

: ЯРД>ДМ ( ярды -- дюймы) 36 * ; _ok
: фУТ>ДМ ( футы -- дюймы) 12 * ; _ok

Здесь определяемые имена означают "ярды-в-дюймы" и "футы-в-дюймы" соответственно. Ниже приводятся примеры выполнения указанных слов:

10 ЯРД>ДМ . _360_ok
2 ФУТ>ДМ . _24_ok

Если результат должен ВСЕГДА выражаться в дюймах, то необходимо ввести следующие определения:

: ЯРДОВ ( ярды -- дюймы) 36 * ; _ok
: ФУТОВ ( футы -- дюймы) 12 * ; _ok
: ДЮЙМОВ ( --) ; _ok

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

10 ЯРДОВ 5 ФУТОВ + 9 ДЮЙМОВ + . _429_ok

Отметим, что назначение слова ДЮЙМОВ - напомнит вам, для чего здесь поставлено число 9. Если вы хотите писать грамотно, то должны добавить три определения:

: ЯРД ЯРДОВ ; _ok
: ФУТ ФУТОВ ; _ok
: ДЮЙМ ; _ok

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

1 ЯРД 5 ФУТОВ + 1 ДЮЙМ + . _97_ok
6 ЯРДОВ 1 ФУТ + . _228_ok

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

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

17 20 + 132 + 3 + 9 + . _181_ok

однако можно набрать на клавиатуре и такую строку:

17 20 132 3 9 + + + + . _181_ok

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b00710

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

: 5#СУММА ( n1 n2 n3 n4 n5 -- сумма) + + + + ; _ok

и выполним слово, как показано ниже:

17 20 132 3 9 5#СУММА . _181_ok

Ниже приводится еще одно выражение, для которого нужно записать определение [ДЛЯ НАЧИНАЮЩИХ, которым нравятся задачи, сформулированные словесно. Реактивный самолет летит со средней скоростью 600миль/ч. Скорость попутного ветра составляет 25миль/ч. Сколько миль пролетит этот самолет при таких условиях за пять часов? Если мы определим слово

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

то можно затем ввести следующее:

5 600 25 РАССТОЯНИЕ . _3125_ok

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

(а + b) * с

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

с а b + *

Итак, мы можем записать наше определение:

: РЕШЕНИЕ ( с a b -- n) + * ; _ok

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

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

1. ab + c примет вид : 2Б1 * + ;

2. (a-4b)/6 + c;

3. a / 8b;

4. 0.5ab / 100;

5. a(2a+3);

6. (a-b)/c.

ОПЕРАЦИИ ДЕЛЕНИЯ

Слово / (слэш) отображает самую простую операцию деления в Форте. Слэш обеспечивает только частное; если в результате деления образуется остаток, он теряется. Набрав на клавиатуре

22 4 / . _5_ok

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

Но / - всего лишь одна из нескольких операций деления в Форте. Такое разнообразие операций дает пользователю широкие возможности. Во многих задачах округленный результат или результат двойной точности вам и не требуется. Допустим, вам нужно решить такую задачу: сколько банкнот достоинством в один доллар получается при размене 22 четвертей доллара? Ответ очевиден: пять (а не 5.5). Машинный меняла, к примеру, не будет знать, как выдать вам 5.5дол. Здесь необходимы операции, подобные операции / , но вычленяющие целые частное и остаток:

MOD ( n1 n2 -- n-остаток) Деление. В стек помещается остаток от деления.
/MOD ( n1 n2 -- n-остаток n-частное) Деление. В стек помещаются остаток и частное.

Итак, если вам требуется только частное, применяйте операцию / , если вам требуется только остаток - операцию MOD [ДЛЯ ЛЮБОЗНАТЕЛЬНЫХ. MOD - сокращение от modulo, что означает "остаток"], а если и остаток, и частное - операцию /MOD. Выполним в качестве примера операцию /MOD:

22 4 /MOD . . _5_2_ok

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b00810

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

: ДОЛ-ЧЕТВЕРТИ ( четверти -- четверти доллары) 4 /MOD ;
: .ДОЛЛАРЫ ( доллары --) . ." долларов " ;
: .ЧЕТВЕРТИ ( четверти --) . ." четверти " ;
: ЧЕТВЕРТИ ( четверти --)
ДОЛ-ЧЕТВЕРТИ ." Получается" .ДОЛЛАРЫ ." и " .ЧЕТВЕРТИ ;

Далее вы можете ввести

22 ЧЕТВЕРТИ

и получить

22 ЧЕТВЕРТИ _Получается_5_долларов_и_2_четверти_ok

В Стандарте Форт-83 во всех операциях деления частное НЕ ОКРУГЛЯЕТСЯ [ДЛЯ ТЕХ, КТО ИМЕЕТ СКЛОННОСТЬ К МАТЕМАТИКЕ. Хотите верьте, хотите нет, но в информатике мы сталкиваемся с противоречиями при решении даже такой простой задачи, как "-31 разделить на 7". Результатом может быть либо -4 при остатке -3 (-4 * 7 = -28; -28 + - 3 = 31), либо -5 при остатке 4 (-5 * 7 = -35; -35 + 4 = 31).

Группа по разработке стандарта Форт-83 приняла решение о том, что при выполнении операций деления частное не должно округляться. Иными словами, из двух целых чисел, между которыми находится дробное частное, выбирается меньшее. В нашем примере -5 меньше, чем -4, поэтому выбирается -5. При делении без округления частного знак остатка совпадает со знаком делителя. Таким образом, если мы делим -31 на 7 в среде Форт-83, то получаем частное -5 и остаток 4. Это правило относится к делению чисел со знаком и не приводит к противоречиям в окрестности нуля].

Что же делать, если в прикладных программах требуется округление? Не беспокойтесь - нужные средства легко создаются, как вы увидите в разд. "Округление" гл.5, путем комбинирования и расширения элементарных арифметических операций.

МАНИПУЛЯЦИИ СО СТЕКОМ

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

(a-b)/c

нельзя преобразовать в форму определения Форта без того, чтобы каким-то образом не поменять местами значения в стеке. "Каким-то образом" - это значит, что вы должны выполнить операцию преобразования стека, а именно ПЕРЕСТАНОВКУ - SWAP.

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b00910

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

DUP (ДУБЛИРОВАНИЕ)

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b01010

DUP *

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

ОПЕРАЦИЯСОДЕРЖИМОЕ СТЕКА
a
DUPa a
*a*a

OVER (ЧЕРЕЗ)

Теперь допустим, что кто-то попросил вас вычислить выражение

а * (а + b)

при следующем содержимом стека:

( a b --)

Вы скажете, что для этого потребуется новая операция со стеком, так как вам нужно два экземпляра а, и а должно находиться под b. OVER и есть та самая "новая" операция. OVER создает еще один экземпляр а, который "перепрыгивает", как при игре в чехарду, ЧЕРЕЗ b:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b05010

Теперь исходное выражение

а * (а + b)

может быть легко записано в виде

OVER + *

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b01110

ОПЕРАЦИЯСОДЕРЖИМОЕ СТЕКА
a b
OVERa b a
+a b+a
*a*(a+b)

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

а2 + ab

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

а * (а+b)

А такое выражение вы уже умеете вычислять.

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b01210

Например, если вам нужно вычислить выражение

ab - bс,

то сначала необходимо вынести b за скобки:

b * (а-с)

а затем, если начальное состояние стека таково:

( с b a --)

можно написать

ROT - *

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

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

DROP (ИСКЛЮЧИТЬ/УДАЛИТЬ)

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b01310

ПЕЧАТЬ БЕЗ ИЗМЕНЕНИЯ СОДЕРЖИМОГО СТЕКА

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

Давайте проверим:

1 2 3 .S _1_2_3_ok
ROT .S _2_3_1_ok

При изучении слов манипулирования со стеком начинающие иногда используют следующий прием:

: SWAP SWAP .S ;
: DUP DUP .S ;
: OVER OVER .S ;
: ROT ROT .S ;
: DROP DROP .S ;

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

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

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

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

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

3. (n+1)/n ( n -- результат)

4. x(7x+5) ( x -- результат)

5. 9as2 - ba ( a b - результат)

ДВОЙНЫЕ ОПЕРАЦИИ
[Стандарт Форт-83. Эти слова входят в "Дополнительный перечень слов двойной точности"].

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

2SWAP ( d1 d2 -- d2 d1) Перестановка двух верхних пар элементов стека.
2DUP ( d -- d d) Дублирование верхней пары элементов стека.
2OVER ( d1 d2 -- d1 d2 d1) Копирование второй пары элементов стека и размещение копии в вершине стека.
2DROP ( d --) Удаление верхней пары элементов из стека.

Префикс 2 означает, что перечисленные стековые операции выполняются над парами чисел [ДЛЯ СПЕЦИАЛИСТОВ. Эти операции также могут выполняться над числами двойной длины (32 бита, или разряда)], а буква d, используемая в стековой нотации,- что вместо нее подставляется "двойное" число. Это понятие имеет вполне конкретный смысл, который объясняется в гл.7.

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

СЛОВА

Ниже приводится перечень слов Форта, которые были введены в данной главе:

+ ( n1 n2 -- сумма) Сложение.
- ( n1 n2 -- разность) Вычитание (nl - n2).
* ( n1 n2 -- произведение) Умножение.
/ ( n1 n2 -- частное) Деление (n1/n2).
MOD (n1 n2 -- n-остаток) Деление. В стек заносится остаток от деления.
/MOD ( u1 u2 -- n-остаток n-частное) Деление. В стек заносятся остаток и частное.
SWAP ( n1 n2 -- n2 n1) Перестановка двух верхних элементов стека.
DUP ( n -- n n) Дублирование верхнего элемента стека.
OVER ( n1 n2 -- n1 n2 n1) Копирование второго элемента и размещение копии в вершине стека.
ROT ( n1 n2 n3 -- n2 n3 n1) Размещение третьего элемента в вершине стека.
DROP ( n --) Удаление из стека верхнего элемента.
2SWAP ( d1 d2 -- d2 d1) Перестановка двух верхних пар чисел.
2DUP ( d -- d d) Дублирование пары чисел, находящейся в вершине стека.
2OVER ( d1 d2 -- d1 d2 d1) Копирование второй пары чисел и размещение копии в вершине стека.
2DROP ( d -- ) Удаление из стека верхней пары элементов.

ОСНОВНЫЕ ТЕРМИНЫ

ЧИСЛА ДВОЙНОЙ ДЛИНЫ -- Целые числа в диапазоне от -2 биллионов до +2 биллионов (будут строго введены в гл.7).
ЧИСЛА ОДИНАРНОЙ ДЛИНЫ -- Целые числа в диапазоне от -32768 до +32767. Только эти числа можно использовать в качестве аргументов и получать в виде результата при выполнении любой из рассмотренных выше операций. (Этот на первый взгляд выбранный произвольно диапазон определяется структурой разработки компьютеров, как вы увидите позднее).

УПРАЖНЕНИЯ

1. В чем различие между DUP DUP и 2DUP?

2. Определите слово NIP (отщипнуть) для удаления второго элемента стека, т.е.

( a b -- b)

3. Определите слово TUCK (подобрать) для копирования верхнего элемента стека и размещения копии в стеке третьим элементом, т.е.

( a b -- b a b)

4. Определите слово -ROT, которое размещало бы верхний элемент под вторым и третьим (в противоположность ROT), т.е.

( a b c -- c a b)

5. Напишите предложение для перестановки четырех верхних элементов стека в обратном порядке, т.е.

( 1 2 3 4 -- 4 3 2 1)

6. Напишите слово с именем 3DUP, которое будет дублировать три верхних элемента стека, например

( 1 2 3 -- 1 2 3 1 2 3)

Напишите определения для следующих выражений в инфиксной форме с учетом указанной стековой нотации:

7. a2 + ab + c ( c a b -- результат)

8. (a-b)/(a*b) ( a b -- результат)

9. Представьте себе, что вы программист, занимающийся учетом продукции на птицеферме Мерайи. Определите слово с именем УПАКОВКА, которое снимает со стека значение, равное числу яиц, снесенных в день подсчета на данной ферме. В результате его выполнения на печать выдается число коробок, требуемых для упаковки этих яиц, из расчета по 12 штук на коробку, а также число яиц, оставшихся неупакованными из-за того, что их недостаточно для заполнения еще одной коробки.
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Ср Апр 14, 2021 12:28 am

ГЛАВА 3. КАК РАБОТАТЬ НА ФОРТЕ

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

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

ЧАСТЬ 1. ОБЩИЕ СВЕДЕНИЯ. ЕЩЕ РАЗ О СЛОВАРЕ

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

ОТКРЫТИЕ ПЕРВОЕ: вы можете дать одному и тому же слову несколько определений, приписывая ему всякий раз новый смысл, но выполняться будет только последнее определение.
Например, если вы определили слово ВСТРЕЧА:

: ВСТРЕЧА . " Привет. Я говорю на форте. " ; _ok

то при его выполнении получите следующий результат:

ВСТРЕЧА _Привет._Я_говорю_на Форте._ok

Но если вы переопределите это слово:

: ВСТРЕЧА ." Алло, я слушаю вас! " ; _ok

то при его выполнении сработает более позднее определение:

ВСТРЕЧА _Алло,_я_слушаю_вас._ok

Исчезло ли первое определение ВСТРЕЧА? Нет, оно сохранилось, но текстовый интерпретатор всегда начинает просмотр словаря с конца - с элемента, занесенного в словарь последним. Из нескольких определений с одним и тем же именем интерпретатор передает слову EXECUTE первое встретившееся. Вы можете убедиться в том, что прежнее определение ВСТРЕЧА все еще находится в словаре. Для этого наберите на клавиатуре

FORGET ВСТРЕЧА _ok

и

ВСТРЕЧА _Привет._Я говорю_на_Форте._ok

(Действует снова старое определение!)

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b05110

Слово FORGET (ЗАБЫТЬ) ищет указанное слово в словаре и удаляет из словаря (это его основная функция) само слово, а также все то, что вы успели определить после него. FORGET, как и интерпретатор, начинает свой поиск с конца: он удаляет только последний вариант определения данного слова (вместе со всеми словами, специфицированными после него). Поэтому теперь, если вы наберете на клавиатуре слово ВСТРЕЧА, интерпретатор найдет первоначальное слово ВСТРЕЧА. FORGET - очень полезное слово. Оно помогает вам очищать ваш словарь во избежание его переполнения. (Словарь занимает память, а мы должны ее экономить).

ОТКРЫТИЕ ВТОРОЕ: если вы вводите определение с помощью клавиатуры (как вы сейчас это делаете), то исходный текст [ДЛЯ НАЧИНАЮЩИХ. Исходным текстом называется текстовый вариант определения, например:

: ПЛЮС-ЧЕТЫРЕ ( n -- n+4) 4 + ;

Этот первоначальный, исходный, вариант преобразуется в словарную форму и становится элементом словаря] его не сохраняется.

В словаре запоминается только скомпилированная форма вашего определения. А как быть, если вы захотите внести изменение в уже определенное слово? Вы должны повторно набрать полностью все определение, внеся в него соответствующие изменения. Это может показаться вам достаточно утомительным, не так ли? И даже хуже:

ОТКРЫТИЕ ТРЕТЬЕ: если вы выключите компьютер, а затем снова включите его, то все набранные вами определения исчезнут.

Очевидно, что необходим какой-то способ запоминания исходного текста с тем, чтобы его можно было изменять и перекомпилировать в любое время. Здесь-то и приходит вам на помощь РЕДАКТОР. Он позволяет запомнить ваш исходный текст на диске. Поэтому давайте выясним, что представляет собой диск и как Форт-система работает с ним.

ИСПОЛЬЗОВАНИЕ ДИСКОВОЙ ПАМЯТИ

Почти все Форт-системы используют дисковую память. Чтобы понять, что такое дисковая память, ее можно сравнить с оперативной памятью (ЗУПВ - запоминающее устройство с произвольной выборкой). Они различаются между собой примерно так же, как стационарная картотека (шкаф) и вращающаяся. До сих пор вы имели дело с памятью компьютера, которая подобна вращающейся картотеке. Компьютер может получить доступ к этой памяти почти мгновенно, поэтому программы, хранимые в ОЗУ, выполняются очень быстро. К сожалению, объем такой памяти ограничен и ее использование обходится относительно дорого. Кроме того, при выключении компьютера содержимое памяти теряется.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b05210

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

Когда вы определяете некоторое слово, компилятор помещает определение этого слова в ЗУПВ, чтобы доступ к нему осуществлялся достаточно быстро. Постоянным же местом хранения исходного текста данного определения является диск. Вы можете средствами Форта записать ваш исходный текст на диске, а впоследствии считывать его с диска и передавать для обработки текстовому интерпретатору [ДЛЯ НАЧИНАЮЩИХ. Диск иногда используют для хранения самой Форт-системы. Когда вы активируете, или загружаете, Форт, это, как правило, означает его копирование из некоторого участка дисковой памяти в ЗУПВ, где он представляется в виде словаря. (В других системах Форт хранится в постоянной памяти, предназначенной только для чтения из нее (ПЗУ). Здесь он становится доступным сразу после включения питания].

Дисковая память Форт-системы подразделяется на так называемые БЛОКИ. Храниться на диске в виде блоков может любая информация. Существует соглашение, по которому блоки, содержащие исходный текст, вмещают 1024 символа и состоят из 16 строк по 64 символа каждая, что согласуется с размерами экрана вашего компьютера. (В некоторых случаях для обозначения блока с исходным текстом применяется термин ЭКРАН. Для простоты мы будем пользоваться только термином БЛОК).

Блок с исходным текстом выглядит следующим образом:

Block# 50
0 ( Большая буква "F" )
1 : STAR 42 EMIT ;
2 : STARS ( Количество ) 0 DO STAR LOOP ;
3 : MARGIN CR 30 SPACES ;
4 : BLIP MARGIN STAR ;
5 : BAR MARGIN 5 STARS ;
6 : F BAR BLIP BAR BLIP BLIP CR ;
7
8
9 F
10
11
12
13
14
15

Верхняя строка (Block 50) и числа слева от текста на диске отсутствуют. Они выводятся словом LIST, чтобы вам было легче ориентироваться. Для того чтобы вывести требуемой блок, наберите номер этого блока и слово LIST (РАСПЕЧАТАТЬ), например:

50 LIST

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

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

50 LOAD

Слово LOAD (ЗАГРУЗИТЬ) передает указанный блок текстовому интерпретатору, что приводит к компиляции всех находящихся в блоке определений.

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

Как уже отмечалось, текстовый интерпретатор сканирует ВХОДНОЙ ПОТОК. Ранее мы считали, что входным потоком является строка, вводимая с клавиатуры. Теперь вам известно, что можно ПЕРЕКЛЮЧАТЬ источник входного потока. Как правило, входной поток поступает с клавиатуры дисплея; когда же вы применяете слово LOAD, входной поток поступает с диска.
Итак, у входного потока два источника
: непосредственно клавиатура или диск.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b05310

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

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

По крайней мере до тех пор, пока вы не прочитаете гл.10, прежде чем снять диск, отключить питание или предпринять что-нибудь опасное, обязательно введите слово FLUSH [Относительно Форта, функционирующего под управлением других операционных систем. Многие Форт-системы работают под управлением других операционных систем, например, СР/М или MS-DOS. В них блоки Форта представляют собой участки размером в 1К в специально зарезервированном для этих целик файле (или файлах). Как открывать и закрывать такие файлы, объясняется в документации по вашей системе].

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

FORGET имя ( --) С помощью этого слова мы забываем (удаляем из словаря) указанное слово и все слова, внесенные в словарь после него.
LIST ( n --) Вывод на экран дискового блока.
LOAD ( n --) Загрузка дискового блока (компиляция или выполнение). Блок 0 обычно загружен быть не может.
FLUSH ( --) Запись всех обновленных дисковых буферов на диск, после чего освобождение этих буферов.

ПРАВИЛА ЗАПИСИ ФОРТ-ПРОГРАММ

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

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

Похожее слово \S (пропуск экрана) предписывает интерпретатору игнорировать дальнейший текст в данном блоке. В отдельных Форт-системах для таких целей применяется слово EXIT.

Ниже приводятся еще несколько правил, позволяющих сделать текст в блоке удобочитаемым.
1. Отделяйте комментарий с обеих сторон двумя пробелами. Если стековый комментарий отсутствует, отделяйте имя определения от содержательной части тремя пробелами.
2. Разбивайте определения на фрагменты, разделяемые двумя пробелами.
3. Если определение занимает более одной строки, делайте отступ во всех строках, кроме первой.
4. Не помещайте на одной строке более одного определения.
5. Определения должны быть краткими! В среднем определение должно занимать две строки.

В книге автора "Думаем на Форте" [1] хорошему стилю программирования на Форте посвящена целая глава.

Итак, запомните две команды, введенные в этом разделе:

\ ( --) Пропуск оставшегося текста данной строки.
\S ( --) Пропуск оставшегося текста экрана.

ОСОБЕННОСТИ ПРОГРАММИРОВАНИЯ НА ФОРТЕ

Программист пишет программу на Форте обычно в несколько этапов:
1. С помощью редактора Форта он делает доступным некоторый блок.
2. Набирает определение.
3. Завершает работу с редактором (при необходимости).
4. Загружает данный блок.
5. Проверяет новое слово.
6. Если слово выполняется неправильно, то программист забывает это определение посредством FORGET, редактирует его и повторно загружает. Если же слово выполняется правильно, программист возвращается к п.1 и принимается за следующее определение.

Основной причиной, по которой программирование на Форте происходит быстрее, чем на других языках, является быстрая оборачиваемость цикла "кодирование-загрузка-тестирование". Загрузка блока занимает менее секунды.

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

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

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b05410

FORGET STAR _ok
50 LOAD _ok

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b05510

В Форте три перечисленных варианта фрагментов называются ОВЕРЛЕЙНЫМИ СТРУКТУРАМИ [ДЛЯ СПЕЦИАЛИСТОВ. Более точно - оверлейными структурами компиляции. Некоторые Форт-системы предлагают возможность загрузки так называемых ДВОИЧНЫХ ОВЕРЛЕЙНЫХ СТРУКТУР, представляющих собой предварительно скомпилированные программные разделы, которые могут быть присоединены к резидентной части словаря], так как они при исполнении не пересекаются и взаимозаменяемы в памяти. Рассмотрим их более подробно. Последнее определение основы программы должно содержать только имя, например:

: VARIATIONS ;

Это определение называется НУЛЕВЫМ, поскольку оно всего лишь отмечает место в вашем словаре. Включите в начало каждого альтернативного фрагмента выражение

FORGET VARIATIONS : VARIATIONS ;

С помощью FORGET каждый раз при загрузке любого из альтернативных фрагментов система забывает все определения начиная с конца словаря до нашего нулевого определения и вновь компилирует нулевое определение, а затем определения, относящиеся к данному фрагменту. Загружая другой фрагмент, вы тем самым замещаете первый оверлейный сегмент вторым [ДЛЯ РАБОТАЮЩИХ С СИСТЕМАМИ, В КОТОРЫХ ЕСТЬ СЛОВО 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

ИНСТРУМЕНТАЛЬНЫЕ СРЕДСТВА РАБОТЫ С БЛОКАМИ

Мы знаем, что слово LIST инициирует печать только одного блока. Введем несколько слов, имеющихся в большинстве Форт-систем, для вывода группы блоков и для доступа к блокам.

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

30 TRIAD

обеспечит вывод блоков с номерами 30, 31 и 32. (Обращение к TRIAD с аргументами 31 и 32 приведет к выдаче той же самой триады блоков).

Слово SHOW выводит группу блоков со смежными номерами. Например, выражение

30 38 SHOW

приведет к выводу трех триад блоков, начинающихся с номеров 30, 33 и 36 соответственно.

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

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

PRINT 17 TRIADE

Слово PRINT (ПЕЧАТЬ) передает остаток строки, содержащей эту команду, ЗАДАЧЕ вывода на печатающее устройство, а выполнение терминальной задачи продолжается обычным путем.

В однозадачных системах указанные выше слова переназначают вывод на периферийные УСТРОЙСТВА (не задачи). В подобной системе вы можете ввести такое выражение:

PRINTER 17 LIST CONSOLE

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

: PRINT PRINTER INTERPRET CONSOLE ;

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

Еще одним важным словом является INDEX (КАТАЛОГ), которое распечатывает в блоках из заданного диапазона только строки комментария (нулевые). Например, выражение

30 38 INDEX

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

Ниже приводится список рассмотренных нами команд.

THRU ( нач кон --) Загрузка всех блоков с номерами из диапазона от нач до кон.
.( текст) ( --) Вывод текста сообщения, ограниченного правой круглой скобкой. Используется, как правило, за пределами определения через двоеточие.
TRIAD ( n --) Вывод трех блоков с номерами, включающими n, начиная с номера, делящегося без остатка на 3.
SHOW ( нач кон --) Вывод блоков с номерами из диапазона от нач до кон по три блока.
INDEX ( нам кон --) Вывод только комментария для блоков, номера которых входят в диапазон от нач до кон.
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Ср Апр 14, 2021 12:35 am

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

ЧАСТЬ 2. ТЕКСТОВЫЙ РЕДАКТОР ФОРТА

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

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

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

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

ПРОГРАММА-РЕДАКТОР

Для начала найдите пустой блок (если нужно, используйте INDEX) и распечатайте его следующим образом:

180 LIST

При распечатке пустого блока вы увидите слева на экране 16 строк (0-15), пронумерованных сверху вниз, без какой-либо информации. Приглашение ok в последней строке означает, что текстовый интерпретатор выполнил вашу команду на распечатку данного блока.

Распечатывая блок, вы тем самым выбираете его для дальнейшей работы:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b05610

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

L

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

Подготовив блок с помощью LIST к редактированию, неплохо было бы выполнить команду WIPE (СТЕРЕТЬ). Иногда никем не используемый блок, на первый взгляд пустой, содержит разного рода символы, что может воспрепятствовать его загрузке. Слово WIPE заполняет текущий блок пробелами, после чего тот становится действительно пустым.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b05710

Поскольку у нас есть текущий блок, выберем и текущую строку с помощью слова Т. Предположим, вы должны записать какую-то информацию в строку 3 [ДЛЯ ЛЮБОЗНАТЕЛЬНЫХ. На самом деле в качестве указателя выступает не номер строки, а позиция курсора. Более подробно это будет показано в следующих сносках]. Наберите на клавиатуре

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b05810

Слово T выделяет выбранную строку, заменяя ее изображение на негативное [Я буду брать выделенный кусок в звездочки.- G]. Теперь, после того, как вы зафиксировали место, куда будете вносить исправления, можно в текущую строку занести некоторый текст с помощью команды P (PUT - ВСТАВИТЬ):

P МЫ НАХОДИМСЯ ЗДЕСЬ<return>

P - ВСТАВЛЯЕТ текст, следующий за этой командой (вплоть до символа возврата каретки), в текущую строку.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b05910

Помните, что текущая позиция, установленная вами, остается прежней, так что если вы сейчас наберете

P А ТЕПЕРЬ МЫ ЗДЕСЬ<return>

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

P__<return>

КОМАНДЫ СИМВОЛЬНОГО РЕДАКТИРОВАНИЯ

Здесь мы рассмотрим, как вставлять и удалять текст в пределах строки.

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

*ЧЕМ*ЛУЧШЕ*КОМПЬЮТЕР,*ТЕМ*БОЛЬШЕ*ПАЯТЬ*

и вам нужно вставить недостающую букву М в слово ПАМЯТЬ. Курсор находится в начале строки. Для того чтобы переместить его за ПА, введите команду F (FIND - НАЙТИ) с фрагментом ПА:

F ПА<return>

Слово F будет просматривать текст, начиная с текущей позиции курсора, до тех пор, пока не НАЙДЕТ заданный фрагмент (в нашем случае ПА), после чего переместит курсор за найденный фрагмент.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b08010*ЧЕМ*ЛУЧШЕ*КОМПЬЮТЕР,*ТЕМ*БОЛЬШЕ*ПАЯТЬ*

... Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b08110 ...
ЧЕМ ЛУЧШЕ КОМПЬЮТЕР, ТЕМ БОЛЬШЕ ПАБроуди. Начальный курс программирования на языке ФОРТ. 1990 G9b08210*ЯТЬ*

I
Так как курсор подведен к требуемому месту, достаточно ввести

I M<return>

и I ВСТАВИТ (INSERT - ВСТАВИТЬ) символ М за курсором.

ЧЕМ ЛУЧШЕ КОМПЬЮТЕР, ТЕМ БОЛЬШЕ ПА*МЯТЬ*

E
Чтобы удалить фрагмент с помощью команды Е (ERASE - СТЕРЕТЬ), вы должны его сначала найти, послав команду F. Например, если вы хотите удалить слово ЛУЧШЕ, в первую очередь восстановите положение курсора:

3 T<return>

после чего введите

F ЛУЧШЕ<return>

ЧЕМ ЛУЧШЕ *КОМПЬЮТЕР,*ТЕМ*БОЛЬШЕ*ПАМЯТЬ*

и далее

E<return>

Слово E удалит фрагмент, который вы только что задали в команде F:

ЧЕМ ЛУЧШЕ *КОМПЬЮТЕР,*ТЕМ*БОЛЬШЕ*ПАМЯТЬ*

После этого E выведет исправленную строку:

ЧЕМ *КОМПЬЮТЕР,*ТЕМ*БОЛЬШЕ*ПАМЯТЬ*

Курсор указывает место, куда вы можете вставить другое слово:

I МОЩНЕЕ<return>

ЧЕМ МОЩНЕЕ *КОМПЬЮТЕР,*ТЕМ*БОЛЬШЕ*ПАМЯТЬ*

D
По команде D (DELETE - УДАЛИТЬ) находится и УДАЛЯЕТСЯ заданный фрагмент. В ней фактически сочетаются две команды: F и E. Например, если ваш курсор находится в таком положении:

ЧЕМ *МОЩНЕЕ*КОМПЬЮТЕР,*ТЕМ*БОЛЬШЕ*ПАМЯТЬ*

то вы можете удалить слово КОМПЬЮТЕР, набрав

D КОМПЬЮТЕР<return>

ЧЕМ МОЩНЕЕ *,*ТЕМ*БОЛЬШЕ*ПАМЯТЬ*

Однако вы снова можете вставить фрагмент текста в то место строки, на которое указывает сейчас курсор:

I ГОЛОВА<return>

ЧЕМ МОЩНЕЕ ГОЛОВА *,*ТЕМ*БОЛЬШЕ*ПАМЯТЬ*

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

R
По команде R (REPLACE - ЗАМЕНИТЬ) заменяется фрагмент текста, который вы только что нашли. Эта команда объединяет в себе команды Е и I. Например, если курсор показывает на фрагмент

*КОМПЬЮТЕРУ*НУЖЕН*ХОРОШИЙ*ТЕРМИНАЛ*

и вы ввели

F У НУЖЕН<return>
R CAM<return>

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

КОМПЬЮТЕР САМ *ХОРОШИЙ*ТЕРМИНАЛ*

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

*(*Учебные*определения)*__________________________MPTY*

то не так просто с помощью F найти для этой буквы место. Вы должны провести курсор через множество пробелов к требуемому (перед MPTY). В данной ситуации лучше воспользоваться таким приемом:

F MPTY<return>

затем

R EMPTY<return>

TILL (ДО)
Самой мощной командой удаления является TILL. Она удаляет все, начиная с текущего положения курсора ДО указанного фрагмента включительно. Например, после применения к строке

ПРОГРАММИРУЙТЕ НА *ЧЕМ*УГОДНО,*ТОЛЬКО*НЕ*НА*ФОРТЕ!*

(заметьте, где находится курсор) команды TILL

TILL HA<return>

останется лишь текст:

ПРОГРАММИРУЙТЕ НА *ФОРТЕ!*

Эта фраза звучит приятнее, не правда ли? С помощью TILL осуществляется поиск в пределах текущей строки, а не по всей оставшейся части блока.

БУФЕР ПОИСКА И БУФЕР ВСТАВОК

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

F КОМПЬЮТЕР<return>

то по команде F фрагмент КОМПЬЮТЕР прежде всего помещается в так называемый буфер поиска. С точки зрения компьютерной терминологий буфер - это участок памяти для временного размещения данных. Буфер поиска находится в оперативной памяти компьютера (ОЗУ).

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b07510

Команда F инициирует поиск такого фрагмента в строке, который соответствует содержимому буфера поиска.

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

F<return>

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b07610

Это позволяет находить многочисленные "вхождения" одного и того же фрагмента текста без его повторного набора. Например, предположим, что в строке 8 содержится следующее утверждение:

*И*СМЕХ,*И*СЛЕЗЫ,*И*ЛЮБОВЬ*

Курсор указывает начало строки, и вы хотите удалить последний союз И. Введите следующую фразу:

F И_<return>

И *СМЕХ,*И*СЛЕЗЫ,*И*ЛЮБОВЬ*

Теперь фрагмент И находится в буфере поиска и вы можете просто несколько раз подряд набрать команду F:

F<return>
F<return>

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

F _И_none

что означает: "В данной строке вхождений И нет". Иными, словами, команда F не нашла в строке фрагмента, соответствующего содержимому буфера поиска и поэтому возвратила вам слово И с сообщением об ошибке NONE.

Как уже отмечалось выше, команда D сочетает в себе две команды F и E, поэтому она также использует буфер поиска. Поместив курсор в начало строки

*И*СМЕХ,*И*СЛЕЗЫ,*И*ЛЮБОВЬ*

и фрагмент И_ в буфер поиска, вы можете удалить все вхождения И, введя несколько раз команду D:

D<return>
D<return>
D<return>
СМЕХ, СЛЕЗЫ, *ЛЮБОВЬ*

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

I<return>

[ДЛЯ ЛЮБОЗНАТЕЛЬНЫХ. Редактору достаточно хранить только позицию курсора, а не указатель текущей строки. Так как в строке помещается 64 символа, редактор с помощью слова /MOD всегда сможет вычислить положение курсора, скажем, для 16-го символа в строке 3: 208 64

/MOD . . _3_16_ok ].

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

*Я*ЖИВУ,*Я*ЛЮБЛЮ,*Я*СТРАДАЮ*

Теперь подведем курсор

F Я_<return>

Я *ЖИВУ,*Я*ЛЮБЛЮ,*Я*СТРАДАЮ*

и осуществим вставку;

I ВНОВЬ_<return>

Я ВНОВЬ *ЖИВУ,*Я*ЛЮБЛЮ,*Я*СТРАДАЮ*

В результате получим

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b06010

Наберите на клавиатуре

F<return>
I<return>
F<return>
I<return>

Я ВНОВЬ ЖИВУ, Я ВНОВЬ ЛЮБЛЮ, Я ВНОВЬ *СТРАДАЮ*

КОМАНДЫ РЕДАКТИРОВАНИЯ СТРОК

Итак, вы уже знаете способы изменения символов и слов и можете приступить к корректированию целых строк.

Р
Слово Р, которое мы уже ввели ранее, использует тот же буфер вставок, что и команда I. Предположим, что в вашем буфере вставок все еще находится фрагмент ВНОВЬ из предыдущего примера, а строка 14 все еще является текущей. Наберите на клавиатуре следующий текст:

P<return>

В результате прежнее содержимое строки 14 заменяется содержимым буфера вставок и в ней теперь находится только одно слово ВНОВЬ. Чтобы получить представление об этой команде, посмотрите три примера ее применения:
1. Р ВЕСЬ ЭТОТ TEKCT<return>
2. P__<return>
3. P<return>

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

U
Подобную функцию выполняет и слово U, Оно помещает содержимое буфера вставок НИЖЕ текущей строки. К примеру, допустим, что ваш блок имеет вид:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b06110

Если вы передвинете курсор ко второй строке:

2 Т

а затем наберете

U KAPЛИH<return>_ok
U KУПЕР<return>_ok

то получитe следующее:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b06210

Вместо того чтобы заменить текущую строку, команда U "втискивает" содержимое буфера вставок между текущей и последующими строками, передвигая их ближе к концу. Если бы в строке 15 находилась какая-то информация, она была бы вытеснена за пределы экрана и потеряна. Когда вы добавляете последовательность строк, проще иметь дело с командой U, а не Р, например:

1 Т P АДАМС<return>_ok
U БРАУН<return>_ok
U КЬЮДАХИ<return>_ok
U ДЭВИС<return>_ok

Перечисленные выше три способа использования команды Р применимы также и к команде U.

X
Команда X по своему действию противоположна команде U. Она ИЗВЛЕКАЕТ текущую строку. Если в рассмотренном выше примере мы сделаем строку 3 текущей (с помощью предложения 3 Т), а затем введем

X<return>

то строка 3 будет удалена, а нижние строки передвинутся вверх:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b06310

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

9 T<return>

и

P<return>

вы можете поместить КАРЛИН в строку 9.

Для того чтобы вставить новую нулевую строку, а прежнюю нулевую строку опустить ниже, нужно сначала поместить эту новую строку ПОД нулевой:

0 T U ЭТО НОВАЯ НУЛЕВАЯ СТРОКА.<return>

а затем первые две строки поменять местами:

0 T X U<return>

КОМБИНИРОВАННЫЕ КОМАНДЫ РЕДАКТИРОВАНИЯ

N и В
Вводя слово N, вы тем самым добавляете единицу к номеру текущего блока. Таким образом, очередная набранная комбинация символов

N L

вызовет печать СЛЕДУЮЩЕГО блока.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b06410

Аналогично слово В уменьшит на единицу номер текущего блока. Иными словами, комбинация

B L

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

В некоторых системах N объединена с командой L, так что, применяя N, мы переходим к следующему блоку и выводим его содержимое. Вы можете при необходимости переопределить команды N и В с тем, чтобы они выполнялись так же - Форт допускает переопределение команд применительно к ВАШИМ потребностям.

COPY
Слово COPY позволяет КОПИРОВАТЬ содержимое одного блока в другой. При этом вытесняются любые данные, находившиеся в приемном блоке до копирования. Эта команда имеет следующий формат:

откуда куда COPY

Если, к примеру, вы введете

153 200 COPY

то информация, находящаяся в блоке 153, будет скопирована в блок 200. Пусть у вас войдет в привычку вводить FLUSH после каждого выполнения COPY.

S
Команда S представляет собой расширенную версию команды F. Она дает возможность осуществлять ПОИСК указанного текста в пределах текущего блока, а затем и в пределах последующих блоков вплоть до того блока, который вы указали в команде. Например, если текущим является блок 180 и вы ввели предложение

185 S СОКРОВИЩА

то S инициирует поиск "сокровища" в блоках начиная с 180-го и заканчивая 184-м. Если искомый фрагмент обнаруживается, то на экран выводится блок, содержащий данный фрагмент, а курсор устанавливается в его начале.

Слово S оставляет в стеке номер блока, на котором должен закончиться поиск, так что если вам требуется продолжить поиск того же самого фрагмента, просто введите команду S.

G и BRING (ПЕРЕНОС)

Слово G берет одну строку из другого блока и вставляет ее ПЕРЕД текущей строкой (перемещая вниз текущую и все последующие строки).

Пусть курсор указывает на строку 3 следующего блока:

1
2 Куда ж ты, милая
3 , девалась? И где искать тебя теперь?

Пропущенный текст находится в строке 10 блока 38. Наберите на клавиатуре

38 10 G<return>

и вы получите следующее:

1
2 Куда ж ты, милая
3 пропущенная строка
4 , девалась? И где искать тебя теперь?

Слово BRING перемещает сразу группу строк. Выражение

38 10 14 BRING

переместит из блока 38 строки 10-14.

M
Некоторые разработчики Форта вместо команд G и BRING используют команду М, которая инициирует действия, противоположные вызываемым командой G. Она перемещает текущую строку в заданную строку экрана.

190 2 M<return>

K

Слово К меняет местами содержимое буферов поиска и вставок. Это полезно в тех ситуациях, когда вы случайно командой D удалили нужный вам текст. Так как удаленный текст находится в буфере поиска, вам достаточно ввести команду К, которая переместит его в буфер вставок, а затем команду I.

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

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

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

D ФРУКТЫ^ I ОРЕХИ<return>

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

D ФРУКТЫ<return>

и

I ОРЕХИ<return>

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

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

ОПРЕДЕЛЕНИЕ МЕСТОПОЛОЖЕНИЯ ИСХОДНОГО ТЕКСТА (ПОЛЕЗНЫЙ ПРИЕМ).

В некоторых Форт-системах имеется слово LOCATE (ОПРЕДЕЛИТЬ-МЕСТОПОЛОЖЕНИЕ) или VIEW. Если вы введете следующее предложение:

LOCATE РАЗМЕР-ЯИЦ

то получите распечатку текста блока, содержащего определение РАЗМЕР-ЯИЦ.

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

СЛОВА

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

FORGET имя ( --) С помощью этого слова мы забываем (удаляем из словаря) указанное слово и все слова, внесенные в словарь после него.
LIST ( n --) Вывод на экран дискового блока.
LOAD ( n --) Загрузка дискового блока (компиляция или выполнение). Блок 0 обычно загружен быть не может.
FLUSH ( --) Запись всех обновленных дисковых буферов на диск, после чего освобождение этих буферов.
\ ( --) Пропуск оставшегося текста данной строки.
\S ( --) Пропуск оставшегося текста экрана.
THRU ( нач кон --) Загрузка всех блоков с номерами из диапазона от нач до кон.
.( текст) ( --) Вывод текста сообщения, ограниченного правой круглой скобкой. Используется, как правило, за пределами определения через двоеточие.
THRIAD ( n --) Вывод трех блоков с номерами, включающими n, начиная с номера, делящегося вез остатка на 3.
SHOW ( нач кон --) Вывод блоков с номерами из диапазона от нач до кон по три блока.
INDEX ( нач кон -- ) Вывод только комментария для блоков, номера которых входят в диапазон от нач до кон.
LOCATE или VIEW xxx ( --) Вывод содержимого блока, из которого было загружено определение слова xxx.

Команды редактирования - работа со строками

T ( n --) Вывод заданной строки.
P , P__ или P xxx ( --) Копирование заданного фрагмента, еcли есть, в буфер вставок, после чего помещение копии буфера вставок в текущую строку.
U , U__ или U xxx ( --) Копирование заданной строки, если есть, в буфер вставок, после чего помещение копии буфера вставок в строку, следующую за текущей.
G ( блок строка --) Копирование заданной строки и помещение ее в строку перед текущей, со сдвигом текущей и всех последующих строк вниз.
BRING ( блок нач кон --) Получение строк в указанном диапазоне.
X ( --) Копирование текущей строки в буфер вставок и извлечение этой строки из блока.
F или F xxx ( --) Копирование указанной строки, если задана, в буфер поиска, после чего поиск данной строки в текущем блоке.
S или S xxx ( n --) или ( n -- n) Копирование указанной строки, если задана, в буфер поиска, после чего просмотр блоков от текущего до n-ного в поисках указанной строки. Если строка найдена, номер блока окончания поиска на стеке сохраняется.
E ( --) Используется следом за F. Удаляется столько символов перед курсором, сколько их в данный момент находится в буфере поиска.
D или D xxx ( --) Копирование указанной строки, если задана, в буфер поиска, поиск очередного вхождения этого фрагмента в текущей строке и удаление его.
TILL или TILL xxx ( --) Копирование указанной строки, если задана, в буфер поиска, после чего удаление всех символов, начиная от курсора и заканчивая последним символом заданной строки.
I или I xxx ( --) Копирование указанного фрагмента, если задан, в буфер вставок, после чего помещение содержимого буфера вставок сразу же после курсора.
R или R xxx ( --) Объединяются команды Е и I. Замещение найденного фрагмента заданным фрагментом или содержимым буфер вставок.
^ или что-то вроде ( --) [Не слово!] Отметка конца текста, помещаемого в буфер.

Комбинированные команды редактировании

WIPE ( --) Заполнение текущего блока пробелами.
L ( --) Вывод содержимого текущего блока.
N ( --) Делается текучим следующий блок.
B ( --) Делается текущим предыдущий блок.
COPY ( откуда куда --) Копирование содержимое одного блока в другой.
K ( --) Меняются местами содержимое буфера поиска и буфера вставок.

ОСНОВНЫЕ ТЕРМИНЫ

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

: ИМЯ ;

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

ОВЕРЛЕЙНЫЙ ФРАГМЕНТ -- Фрагмент прикладной программы, замещающий при загрузке другой фрагмент в словаре.
УКАЗАТЕЛЬ -- Участок памяти, куда может быть помещено число (или где оно может быть изменено), предназначенное в дальнейшем для ссылок.

УПРАЖНЕНИЯ

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

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

ЛИТЕРАТУРА
1. Brodie, Leo, Thinking Forth (Englewood Cliffs, N.J.: Prentice-Hall, 1984).
***

ЗАМЕЧАНИЯ

1. Описанный редактор (унаследованный от Мура Броуди. Начальный курс программирования на языке ФОРТ. 1990 Leaf10ТЕМА #3, АБЗАЦ #1275, #5.2.1Броуди. Начальный курс программирования на языке ФОРТ. 1990 Leaf10) является живым доказательством беспомощности Forth в проблемах ввода-вывода. По сути, такой примитивный редактор был в старых ОС (ed - в Unix, edlin - в DOS). Но, если в Unix он послужил толчком для разработки потокового редактора sed (позволяющего программам писать другие программы) и экранного редактора vi, то в BASIC-ориентированных DOS и Forth дальше этого примитива не пошло. Все было отдано на откуп сторонним разработчиком и отмерло: DOS-овское после перехода на Windows, Forth-овское - ... тоже после перехода на привычные Win-парадигмы редактирования. Редактор, собственно говоря, перестал быть частью Forth-системы, став сторонней утилитой.

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

3. Когда я реализовал подобный редактор для своего FOBOS, то столкнулся с неудобством ввода и переввода длинных строк. К счастью, т.к. FOBOS использовал стандартный DOS-ввод, то ему досталась дармовая поддержка простенького редактирования командной строки (кто помнит, клавиши F1-F7). Я даже ввел специальную команду для редактирования строки с забиванием исходной в DOS-буфер. Без подобного средства быстрого набора, редактирование становится жутко муторным делом.
G.
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Чт Апр 15, 2021 12:45 am

ГЛАВА 4. КОМПЬЮТЕР "ПРИНИМАЕТ РЕШЕНИЯ"

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

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

Запишем на Форте простой оператор принятия решения. Допустим, вы программируете механический упаковщик яиц в картонные коробки. Некоторое механическое устройство уже подсчитало число яиц, находящихся в ленте конвейера, и поместило это число в стек. В соответствии со следующим предложением на Форте:

12 = IF УПАКОВАТЬ THEN

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b06510

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b06810

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

Попытаемся выполнять этот оператор. Определим, например, следующее слово:

: ?ПОЛНА ( число-яиц) 12 - IF . " КОРОБКА ПОЛНА " THEN ; _ok
11 ?ПОЛНА _ok
12 ?ПОЛНА _КОРОБКА_ПОЛНА_ok

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

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

Если вам удобно, сразу представляйте запись в ПОСТФИКСНОЙ ФОРМЕ. Вместо традиционного применения выражения IF:

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

мы имеем

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

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

БОЛЕЕ ПОДРОБНО ОБ ОПЕРАТОРЕ IF

В результате выполнения операции сравнения флаг [ДЛЯ НАЧИНАЮЩИХ. На компьютерном жаргоне значение, которое один фрагмент программы оставляет другому в качестве сигнала, называется флагом] на самом деле вверх не выбрасывается, как это показано на рисунках, а его значение ЗАНОСИТСЯ В СТЕК, подобно любому другому аргументу. Истина представляется -1 (отрицательной единицей), а ложь - 0 (нулем) [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ, СОЗДАННЫХ ДО ВВЕДЕНИЯ СТАНДАРТА-83. В более ранних системах истина представлялась как 1]. Слово IF берет флаг из стека и использует его.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b06910

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

12 12 = . _-1_ok -- Да, 12 равно 12
11 12 = . _0_ok -- Нет, 11 не равно 12

Можно вводить знаки операций сравнения непосредственно с вашего терминала, как в приведенном выше примере, но помните, что оператор IF ... THEN должен целиком находиться в пределах одного определения, поскольку его выполнение сопряжено с ветвлением программы. Слово IF будет воспринимать -1 как значение флага "истина", а 0 - как "ложь". Перед IF может стоять еще одно слово NOT [ДЛЯ СИСТЕМ ФИГ-ФОРТ. Используйте в этих целях 0= ], которое меняет значение флага в стеке на противоположное:

0 NOT . _-1_ok
-1 NOT . _0_ok

Слово NOT позволяет изменить условие IF на обратное. Таким образом, мы можем записать

: ?ДВЕНАДЦАТЬ ( n) 12 = NOT IF . " Не двенадцать. " THEN ;

и если параметр n НЕ БУДЕТ РАВЕН 12, на печать будет выводиться фраза "Не двенадцать".

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

: .ДА? ( ? --) IF ." Да " THEN ;
12 12 = .ДА? _Да_ok

(Знак вопроса в стековом комментарии означает флаг). Можете ли вы назвать еще такой язык, который дает возможность записывать условие в одной процедуре, а оператор IF - в другой?

ОПЕРАЦИИ СРАВНЕНИЯ

Ниже приводится неполный список операций сравнения, которые вы можете применять перед выполнением оператора IF ... THEN

= -- равно
<> -- не равно
< -- меньше
> -- больше
0= -- равно-нулю
0< -- меньше-нуля
0> -- больше-нуля

[Для тех, у кого нет <> . Используйте - (минус). См.разд. "Секреты оператора IF" в данной главе.] Слова < и > требуют такого же расположения аргументов в стеке, как и арифметические операции:

ИНФИКСНАЯ ФОРМА
2 < 10
17 > -39
ПРЕФИКСНАЯ ФОРМА
2 10 <
17 -39 >

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

: ?ЖАРКО ( температура --)
220 > IF ." ОПАСНО! Уменьшите нагрев " THEN ;

Если значение, находящееся в стеке БОЛЬШЕ, ЧЕМ 220, то на терминал будет выведено сообщение об опасности. Вы можете выполнить это слово автономно, набрав на клавиатуре сначала определение, а затем и само слово. Перед словом вы должны набрать значение температуры:

290 ?ЖАРКО _ОПАСНО!_Уменьшите_нагрев_ok
130 ?ЖАРКО _ok

Вы можете проверить, является ли некоторое число нулем, отрицательным или положительным, с помощью трех следующих слов: 0= , 0< , 0> . Они эквивалентны выражениям 0 = , 0 < , 0 > и отличаются лишь эффективностью предлагаемых операций.

АЛЬТЕРНАТИВНАЯ ВЕТВЬ УСЛОВНОГО ОПЕРАТОРА

Форт позволяет вам написать в рамках оператора IF с помощью слова ELSE (ИНАЧЕ) альтернативное выражение. В приведенном ниже примере дается определение, которое проверяет, является ли заданное число правильной календарной датой:

: ?ДЕНЬ ( день) 32 < IF ." Путь открыт " ELSE ." Объезд " THEN ;

Если число в стеке меньше 32, то будет выдано сообщение "ПУТЬ ОТКРЫТ". В противном случае выдается сообщение "ОБЪЕЗД"

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b07010

Представьте себе, что IF переключает стрелку железнодорожной колеи в зависимости от результатов проверки условия, после чего выполнение пойдет по одному из двух маршрутов, но в любом случае рельсы сойдутся у слова THEN. В компьютерной терминологии изменение путей выполнения операторов называется ВЕТВЛЕНИЕМ [ДЛЯ СПЕЦИАЛИСТОВ. В Форте нет оператора GOTO. Если вы не можете обойтись без этого оператора немного подождите. К концу книги вам станут ясны отрицательные последствия его буквального применения. Вместо GOTO мы предложим вам средства, позволяющие сделать ваши программы более корректными].

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

Можно определить некоторое слово, выполняющее деление в том случае, если делитель не равен нулю [ДЛЯ СПЕЦИАЛИСТОВ. Как вы увидите ниже, существуют лучшие способы выполнения этих действий]:

: /ПРОВЕРКА ( числитель знаменатель -- результат)
DUP 0= IF ." Знаменатель нуль " DROP ELSE / THEN ;

Заметим, что сначала вы должны с помощью DUP создать копию знаменателя, так как выражение

0= IF

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

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

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

Рассмотрим следующее слово, которое определяет сорт яиц для продажи в зависимости от их размера (очень крупные, крупные и т.д.), по весу в унциях [1 унция = 28,3г.- Примеч.пер.] на дюжину [ДЛЯ РАБОТАЮЩИХ ЗА ТЕРМИНАЛОМ. Поскольку приведенное определение уже достаточно сложное, рекомендуется вводить его из блока на диске]:

: РАЗМЕР-ЯИЦ ( унций-на-дюжину -- )
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 ;

Загрузив однажды слово РАЗМЕР-ЯИЦ, вы можете получать различные результаты, например [ДЛЯ ЛЮБОЗНАТЕЛЬНЫХ. Официальная таблица, согласно которой производится определение сорта яиц: очень крупные - 27-30, крупные - 24-27, средние - 21-24, мелкие - 18-21]:

23 РАЗМЕР-ЯИЦ _Средние_ok
29 РАЗМЕР-ЯИЦ _Очень_крупные_ok
40 РАЗМЕР-ЯИЦ _Ошибка_ok

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b07110

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

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

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

"СЕКРЕТ" ОПЕРАТОРА IF

Мы уже знаем, что IF принимает -1 за истину. Программирующие на Форте часто пользуются тем, что на самом деле IF воспринимает как истину ЛЮБОЕ НЕНУЛЕВОЕ ЗНАЧЕНИЕ и только нуль как ложь [ДЛЯ СОМНЕВАЮЩИХСЯ. Чтобы проверить это, введите следующий текст:

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

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

0 ТЕСТ _Нуль_ok
1 ТЕСТ _Не_нуль_ok
8000 ТЕСТ _Не_нуль_ok ].

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

Так, если вы проверяете некоторое число только на равенство нулю, то операция сравнения вам не нужна. Определение рассмотренного нами слова /ПРОВЕРКА может поэтому принять более простую форму:

: /ПРОВЕРКА ( числитель знаменатель -- результат)
DUP IF / ELSE ." Знаменатель нуль " DROP THEN ;

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

10 MOD

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

10 MOD 0=

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

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

НЕМНОГО ЛОГИКИ

Форт (как и большинство языков программирования) дает вам возможность комбинировать флаги. Возьмем, к примеру, комбинацию по принципу "или". Даны значения двух флагов. Если хотя бы одно из них истинно, то Форт-система выполнит действие, если оба значения ложны - не выполнит. Поясним изложенное на следующем примере. Допустим, вы хотите вывести имя АРТИШОК в том случае, если исходное число ЛИБО отрицательно, ЛИБО кратно 10. Как это сделать на Форте? Рассмотрим выражение:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b07210

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

ОПЕРАТОРСОДЕРЖАНИЕ СТЕКАОПЕРАЦИЯ
30
DUP30 30Содержимое стека (вершины) удваивается, поэтому мы можем проверить это значение дважды
0<30 0Значение отрицательно? Нет (нуль)
SWAP0 30Флаг и число меняются местами
10 MOD 0=0 -1Делится ли это значение на 10? Да (отрицательная единица)
OR-1Флаги логически складываются

Флаги логически складываются? Что же происходит при таком сложении флагов? Вы получаете в результате истину, если хотя бы один из флагов (или сразу оба) истинны. Ниже приводятся четыре возможных сочетания двух флагов и результаты операции OR (ИЛИ) над ними.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b07310

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

: ОВОЩ ( n) DUP 0< SWAP 10 MOD 0= OR
IF ." Артишок " THEN ;

Ниже приводится улучшенный вариант приведенного выше определения слова ?ДЕНЬ. Предыдущее определение браковало только элементы, значение которых превышало 31, а отрицательные значения не допускались вовсе.

: ?ДЕНЬ ( день) DUP 1 < SWAP 31 > OR
IF ." Ошибка " ELSE ." Спасибо " THEN ;

(Во многих Форт-системах для таких случаев предусмотрено слово WITHING (В ИНТЕРВАЛЕ). См. вопросы в конце главы).

Другой комбинацией флагов является комбинация по принципу И. Здесь для получения истины оба флага должны быть истинны. Например, чтобы сделать сквозняк, нужно открыть обе двери: входную И выходную. Сравните с комбинацией ИЛИ: если входная ИЛИ выходная дверь (или сразу обе) будут открыты, то налетят мухи [ДЛЯ ЛЮБОЗНАТЕЛЬНЫХ И НАЧИНАЮЩИХ. Применение слов, аналогичных "или" и "и", при создании некоторых фрагментов прикладных программ называется ЛОГИКОЙ. Нотация для логических операций была разработана в XIXв. Дж.Булем. Этот раздел математики теперь называют булевой алгеброй. Таким образом, термин "булевский флаг" (или даже "булевская переменная") просто обозначает флаг, который будет использован в логической операции].

Форт включает слово AND (И). Ниже приводится таблица результатов операции AND над двумя флагами:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b06610

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

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

: ОБЪЕМ ( длина ширина высота -- )
6 > ROT 22 > ROT 19 > AND AND
IF ." Подходит " THEN ;

Проверить слово ОБЪЕМ можно следующей фразой:

23 20 7 ОБЪЕМ _Подходит_ok

Третий вид комбинации флагов называется ИСКЛЮЧАЮЩИМ ИЛИ. В Форте эта операция выполняется словом XOR. Результат получается истинным только в тех случаях, когда ХОТЯ БЫ ОДИН из флагов истинен, но НЕ ОБА СРАЗУ.

Ниже приводится пример применения такой операции. Слово ?ЗНАКИ выбирает из стека два числа. Если их знаки совпадают (оба числа положительны или оба отрицательны), то выдается сообщение "Знаки совпадают", в противном случае выдается сообщение "Знаки разные":

: ?ЗНАКИ ( nl n2 -- )
0< SWAP 0< XOR IF ." Знаки разные "
ELSE ." Знаки совпадают " THEN ;

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

Следующий пример: вам предстоит выполнить фотосъемку, но прежде необходимо проверить освещенность И наличие пленки в фотоаппарате:

: ФОТОСЪЕМКА ?СВЕТ ?ПЛEHKA AND IF СНИМОК THEN ;

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

: СЛУЖБА-ЗНАКОМСТВ C-ЮМОРОМ ОТЗЫВЧИВЫЙ AND
ЛЮБИТ-ИСКУССТВО ЛЮБИТ-МУЗЫКУ OR AND
КУРИТ NOT AND
IF ." У нас имеется подходящая для вас кандидатура " THEN ;

Здесь такие слова, как С-ЮМОРОМ и ОТЗЫВЧИВЫЙ, предназначены для выполнения проверки записи из дискового файла, содержащего информацию о возможных партнерах.

Обращаем ваше внимание на то, что до тех пор, пока мы не объясним некоторые тонкости использования слов NOT, OR, AND и XOR, их следует применять ТОЛЬКО с аргументами, являющимися логическими флагами, т.е. с нулем или отрицательной единицей. Все рассмотренные выше операции сравнения (за исключением операции "-" , применяемой вместо "не равно") оставляют в стеке логические флаги Поэкспериментируйте, чтобы посмотреть, как выполняются команды NOT, OR, AND и XOR с числами, отличными от 0 и -1.

ДВА СЛОВА С ВСТРОЕННЫМИ ОПЕРАТОРАМИ IF

?DUP
Слово ?DUP дублирует вершину стека только в том случае, если там находится ненулевое значение. Это помогает избавиться от лишних слов. Например, определение

: ?EMIT ( с --) DUP IF EMIT ELSE DROP THEN ;

выдает на печать символ с любым кодом (кроме 0). Применяя ?DUP, можно сократить наше определение:

: ?EMIT ( с --) ?DUP IF EMIT THEN ;

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

Если вы предполагаете, что подобная ошибка может произойти, можно воспользоваться оператором аварийного прекращения выполнения задачи ABORT". Этот оператор проверяет значение флага в вершине стека и в случае его истинности прерывает вычисления. Оператор очищает стек и возвращает управление на терминал до поступления какого-либо сообщения. Оператор ABORT" также выводит имя последнего слова, обработанного текстовым интерпретатором, и предусмотренное вами для такой ситуации сообщение [ДЛЯ ПРОФЕССИОНАЛОВ. В Форте, кроме того, имеются слова QUIT (ОКОНЧИТЬ), которое вызывает прекращение работы программы, но не очищает стек, и ABORT (ПРЕРВАТЬ), которое выполняет те же действия, что и QUIT, очищает стек, но не выводит сообщение. Мы рассмотрим эти слова в гл.9].

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b06710

Проиллюстрируем изложенное примером:

: /ПРОВЕРКА ( числитель знаменатель -- результат)
DUP 0= ABORT" Знаменатель нуль" / ;

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

8 0 /ПРОВЕРКА _/ПРОВЕРКА_Знаменатель_нуль

Теперь в порядке эксперимента попытайтесь поместить слово /ПРОВЕРКА внутрь другого определения:

: ОБОЛОЧКА /ПРОВЕРКА ." Ответ равен " . ;

и введите

8 4 ОБОЛОЧКА _Ответ_равен_2_ok
8 0 ОБОЛОЧКА _ОБОЛОЧКА_Знаменатель_нуль

Обратите внимание на то, что когда слово /ПРОВЕРКА аварийно прерывает работу с помощью оператора ABORT", оставшаяся часть ОБОЛОЧКА пропускается. Заметьте также, что выводится имя ОБОЛОЧКА, а не /ПРОВЕРКА.

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

IF xxx ELSE yyy THEN zzz ( ? --) Выполнение ххх, вели ? истинно (не нулевое значение) , и yyy,- если ? ложно, zzz выполняется независимо от выбранного варианта. Выражение ELSE ууу является необязательным.
= ( n1 n2 -- ?) Занесение в стек истины если n1 и n2 равны.
<> ( n1 n2 -- ?) Занесение в стек истины, если n1 и n2 не равны.
< ( n1 n2 -- ?) Занесение в стек истины, если n1 меньше n2.
> ( n1 n2 -- ?) Занесение в стек истины, если n1 больше n2.
0= ( n -- ?) Занесение в стек истины, если n является нулем (то есть истина меняется на ложь и наоборот).
0< ( n -- ?) Занесение в стек истины, если n отрицательно.
0>( n -- ?) Занесение в стек истины, если n положительно.
NOT ( ? --?) Изменение значения флага на противоположное.
AND ( n1 n2 -- И) Доставление логического значения, согласно таблице операции AND.
OR ( n1 n2 -- ИЛИ) Занесению в стек логического значения, согласно таблице операции OR.
XOR ( n1 n2 -- ИСКЛЮЧ-ИЛИ) Занесение в стек логического значения, согласно таблице операции XOR.
?DUP( n -- n n) или ( 0 -- 0) Дублирование вершины стека только в том случае, если n является ненулевым значением.
ABORT" ххх" ( ? --) Если значение флага истинно, то вывод последнего проинтерпретированного слова и за ним заданного текста. Кроме этого очищает стеки пользователя и возвращается управление на терминал. Если в стеке ложь, то не предпринимается никаких действий.

Обозначения: n, n1... - 16-разрядные числа со знаком, ? - логическое значение (флаг).

ОСНОВНЫЕ ТЕРМИНЫ

ABORT -- Часто встречающийся в информатике термин, обозначающий оператор, который моментально прекращает вычисления при возникновении аварийной ситуации. Его выполнение позволяет предотвратить получение бессмысленных результатов и, возможно, нарушение функционирования системы.
ВЕТВЛЕНИЕ -- Изменение линейной последовательности вычислений в зависимости от условий, возникающих в процессе выполнения программы.
ВЛОЖЕННЫЕ СТРУКТУРЫ -- Структуры ветвления, "вложенные" одна в другую.
ЛОГИКА -- Система представления условий в форме "логических переменных", которые могут быть либо истинными, либо ложными, и комбинаций этих переменных посредством таких логических операций, как "и", "или" и "не". Полученное в результате выражение может быть либо истинным, либо ложным.
ЛОГИЧЕСКАЯ ОПЕРАЦИЯ "И" -- Два логических значения объединяются таким образом, что если оба они истинны, то и результат получается истинным, в противном случае результат ложный.
ЛОГИЧЕСКАЯ ОПЕРАЦИЯ "ИСКЛЮЧАЮЩЕЕ-ИЛИ" -- Два логических значения объединяются таким образом, что если хотя бы одно из них (но не оба сразу) истинно, то результат является истинным, в противном случае результат ложный.
ЛОГИЧЕСКАЯ ОПЕРАЦИЯ "ИЛИ" -- Два логических значения объединяются таким образом, что если хотя бы одно из них истинно, то результат является истинным, в противном случае ложный.
ОПЕРАЦИЯ СРАВНЕНИЯ -- Одно значение сравнивается с другим (например, определяется, не больше ли одно значение, чем другое) и устанавливается соответствующее значение флага. Последнее, как правило, проверяется условным оператором. В Форте при сравнении значение флага остается в стеке.
УСЛОВНЫЙ ОПЕРАТОР -- Слово, такое, как IF, которое устанавливает последовательность вычислений в зависимости от некоторого условия (истина или ложь).
ФЛАГ -- Число, сигнализирующее о том, что некоторое известное условие является истинным или ложным.

УПРАЖНЕНИЯ

1. Какое значение оставляет в стеке выражение 0= NOT при следующих значениях аргумента: 1, 0, 200?

2. Объясните, как поведет себя артишок при указанных выше значениях.

3. Определите слово с именем РАЗРЕШЕНИЕ, которое берет из стека значение, соответствующее возрасту некоторого лица, и выводит одно из двух сообщений (согласно установленным в вашей местности порядкам):

_Употребление_спиртных_напитков_разрешено
_По_возрасту_не_положено

4. Определите слово с именем ПРОВЕРКА-ЗНАКА, которое проверяло бы число, находящееся в стеке, и выдавало бы одно из трех сообщений: "ПОЛОЖИТЕЛЬНОЕ", "НУЛЬ" или "ОТРИЦАТЕЛЬНОЕ".

5. Напишите определение <> (не равно), которое в отличие от определения - (минус) всегда оставляло бы в стеке значение "истина" или "ложь".

6. Напишите определение XOR, используя другие логические операторы, такие, как AND, OR и NOT.

7. В гл.1 мы определили слово с именем STARS, но оно не выполняется правильно с аргументом, равным нулю (В Форт-системах, соответствующих Стандарту-83, будет выведено 65535 звездочек, в остальных - одна). Предложите на основе старого определения новый вариант STARS, который был бы свободен от указанного недостатка.

8. Напишите определение слова NEGATE (оно, между прочим, входит о состав Форт-систем), которое меняло бы знак числа в стеке на противоположный (4 пpи этом становится -4, и наоборот). После этого, используя NEGATE, создайте слово ABS чтобы поместить в стек абсолютное значение заданного числа n (Слово ABS также есть в вашей системе).

9. Напишите определение слова /UP, которое округляло бы остаток от деления, не равный нулю. (При решении задачи: "Сколько ящиков потребуется для упаковки n предметов?", для размещения округленного остатка потребуется дополнительный ящик).

10. Напишите определение для слова WITHIN (В ИНТЕРВАЛЕ) которое выбирает из стека три аргумента

( n нижняя-граница верхняя-граница )

и помещает в стек истинное значение только в том случае, когда n находится внутри диапазона:

нижняя-граница =< n < верхняя-граница

11. Существует игра с угадыванием числа (программируя ее, вы получите больше удовольствия, чем в нее играя). Вначале вы вводите некоторое число в стек, причем можете сохранить это число в тайне, выполнив слово PAGE - которое очищает экран терминала. Затем вы просите своего партнера попытаться угадать задуманное число. Предполагаемое значение играющий должен ввести вместе со словом УГАДАЙ, например.

100 УГАДАЙ

Компьютер должен выдать ответ "Слишком большое", "Слишком маленькое" или "Правильно!". Напишите определение для слова УГАДАЙ в предположении, что после нескольких попыток ответа введенные числа будут находиться в стеке. После правильного ответа стек необходимо очистить.

12. С помощью операторов IF ... ELSE ... THEN напишите определение с именем ПРОПИСЬ, которое выводит число, находящееся в стеке, в виде слов на естественном языке. Диапазон выводимых чисел - от -4 до 4. Если в стеке находится число вне этого диапазона, то должно выдаваться сообщение "Выходит за диапазон". Например:

2 ПРОПИСЬ _Два_ok
-4 ПРОПИСЬ _Минус_четыре_ok
7 ПРОПИСЬ _Выходит_за_диапазон_ok

Создайте по возможности короткое определение. (Указание: слово ABS выдает абсолютное значение числа, находящегося в стеке).

13. Используя созданное в упр.4.10. определение WITHIN, разработайте еще одну игру с угадыванием чисел и назовите ее ЛОВУШКА. По условию игры вы вводите значение, не известное вашему партнеру, после чего он пытается угадать заданное число, вводя два числа, между которыми, по его мнению, заключено искомое значение, например:

0 1000 ЛОВУШКА _Между_ok
330 660 ЛОВУШКА _Между_ok
440 550 ЛОВУШКА _Вне_ok
330 440 ЛОВУШКА _Между_ok

и т.д. до тех пор, пока ваш партнер не отгадает ответ:

391 391 ЛОВУШКА _Вы_угадали!_ok

Совет: вы можете предусмотреть в своей программе следующее: когда с искомым числом совпадает только одно число из задаваемого интервала, она не будет выдавать "Между".
***

ЗАМЕЧАНИЕ

Странно, что Броуди столь сильно восхищается хранением в стеке результатов сравнения. Любой язык высокого уровня имеет для средство для логических вычислений - логический тип. А язык C, подобно Forth, тоже умеет смешивать логические и целочисленные вычисления.
Надо, кстати, заметить, что подобная практика не очень выгодна с точки зрения компиляции, т.к. процессор хранит результаты сравнений совершенно иным способом.
G.
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Пт Апр 16, 2021 12:34 am

ГЛАВА 5. ОПЕРАЦИИ НАД ЦЕЛЫМИ ЧИСЛАМИ

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

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

Рассмотрим сначала наиболее простые операции. Ниже приводятся слова, смысл которых вам очевиден [ДЛЯ НАЧИНАЮЩИХ. Понятие "арифметический сдвиг влево (вправо)" мы объясним позднее]:

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 + . Наконец, вы экономите какое-то время на компиляции.

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

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

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

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

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

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b07410

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

Ниже дается перечень слов, связанных со стеком возвратов. Помните, что описание состояния стека относится здесь к СТЕКУ ДАННЫХ.

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

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

>R SWAP R>

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

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

>R SWAP R@

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

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

( а b с x -- n)

(Напоминаем, что операция возведения в степень должна выполняться первой).

ОПЕРАЦИЯСТЕК ДАННЫХСТЕК ВОЗВРАТОВ
а b с x
>Ra b сx
SWAP ROTс b аx
R@с b а xx
*с b ахx
+с (ax+b)x
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.50 *1.5
2.232.23
=3.345

Десятичная точка "плавает" по индикатору по мере необходимости. Такой индикатор называется ИНДИКАТОРОМ С ПЛАВАЮЩЕЙ точкой. Представление с плавающей точкой - это способ записи чисел в компьютере в виде мантиссы и порядка. Например, 12млн. можно записать как 12e6, поскольку 1e6 равно 1млн. Во многих компьютерах 12млн. должно быть записано в виде двух чисел 12 и 6, что воспринимается как 1e6, умноженное на 12. Число 3.345 будет записано так: 3345 и -3.

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

Представление с фиксированной точкой - другой способ записи чисел в память, при котором положение десятичной точки числа не запоминается. Например, при вводе суммы в долларах и центах все значения должны запоминаться в центах, а расположение десятичной точки должно "помнить" НЕ САМО ЧИСЛО, А ПРОГРАММА.

Сравним для иллюстрации три представления чисел: общепринятое, с фиксированной точкой и с плавающей точкой:

ОБЩЕПРИНЯТОЕ ПРЕДСТАВЛЕНИЕС ФИКСИРОВАННОЙ ТОЧКОЙС ПЛАВАЮЩЕЙ ТОЧКОЙ
1.23123123e-2
10.9810981098e-2
100.00100001e2
58.605860586e-1)

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

ПОЧЕМУ ПРОГРАММИСТЫ ПРЕДПОЧИТАЮТ МАСШТАБИРОВАНИЕ

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

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

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

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

Неверно, что Форт НЕ ИМЕЕТ ВОЗМОЖНОСТИ поддерживать арифметику с плавающей точкой. Программистами созданы на Форте функции с плавающей точкой [1-7], а в ряде Форт-систем применяются сопроцессоры с плавающей точкой (отдельный чип, единственной функцией которого является реализация высокоскоростных операций над числами с плавающей точкой) [8, 9].

Большинство программистов считают, что операции над числами с плавающей точкой им необходимы, но это не так. При решении многих физических задач значения обрабатываемых и получаемых величин не выходят за пределы диапазона от единицы до нескольких тысяч и, следовательно, умещаются в 16-разрядное целое слово. (Некоторые вычисления могут потребовать 32-разрядного представления, что Форт в состоянии обеспечить). К таким задачам относится моделирование погоды, восстановление изображения, автоматическое измерение электрических цепей и т.п. Масштабирование дает вам возможность работать с данными в 16-разрядном целочисленном диапазоне и поэтому отпадает надобность в расточительных операциях над числами с плавающей точкой. Не подумайте, что мы вынуждаем вас нарушать общепринятые правила и оставляем наедине со своими проблемами. Форт представляет в ваше распоряжение уникальный набор команд, так называемых ОПЕРАЦИЙ МАСШТАБИРОВАНИЯ, которые поддерживают АППРОКСИМАЦИЮ ВЕЩЕСТВЕННЫХ ВЕЛИЧИН и выполнение операций С ДРОБНЫМИ ЗНАЧЕНИЯМИ. В следующем разделе мы познакомим вас с основными возможностями целочисленной арифметики.

ОПЕРАЦИЯ МАСШТАБИРОВАНИЯ */

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

*/ ( n1 n2 n3 -- результат) Умножение, затем деление (n1*n2/n3). Промежуточный результат 32-разрядный.

Например, допустим, что в стеке содержатся три числа*

( 225 32 100 --)

*/ сначала перемножит 225 на 32, а затем разделит полученный результат на 100. Эта операция особенно удобна для решения таких задач, как вычисление процентов. В частности, вы можете определить слово % следующим образом:

: % ( n % -- n') 100 */ ;

Введя число 225, а затем выражение

32 %

вы получите в стеке 32% от 225, т.е. 72 [ДЛЯ ЛЮБОЗНАТЕЛЬНЫХ. Способ, при котором сначала перемножаются два числа, а затем полученное произведение делится на 100, применяется и при выполнении подобных вычислений вручную].

Команда */ это не простое сочетание команд * и / . Она использует для промежуточного результата число удвоенной длины. "Что это значит?",- спросите вы. Предположим, вам нужно вычислить 34% от 2000. Вспомним, что операции над числами одинарной точности, такие, как * и / , дают результаты в диапазоне от -32768 до +32767. Если вы введете выражение

2000 34 * 100 /

(то получите неверный результат, поскольку промежуточное выражение (в нашем случае произведение) превышает 32767:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b07710

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

2800 34 100 */

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

ОКРУГЛЕНИЕ

Из предыдущего примера вытекает другой вопрос: как производить округление? Решим такую задачу: сколько бананов нужно завезти в школьный буфет, если он рассчитан на 225 человек, и 32% из них обычно покупают бананы? Естественно, нас интересуют только целые бананы, так что в случае получения нецелого результата его нужно округлить. Сейчас слово % определено так, что все цифры справа от десятичной точки просто отбрасываются. Иными словами, результат "усекается":

32% ОТРЕЗУЛЬТАТ
225 = 72.0072 - абсолютно правильный
226 = 72.3272 - правильный, округлен в меньшую сторону (усечен)
227 = 72.6472 - усечен, но не округлен

Существует, однако, способ, при котором любое десятичное значение от 0.5 и выше, полученное в остатке, округляется до следующего целого. Для того чтобы найти "округленные проценты", мы должны определить слово R%:

: R% ( n % -- результат ) 10 */ 5 + 10 / ;

так что теперь выражение

227 32 R% .

даст в результате 73, т.е. правильно округленное до следующего целого числа.
Заметьте, что сначала мы делим на 10, а не на 100. При этом у нас появляется возможность добавить цифру 5 в позицию единиц числа, полученного в результате выполнения операции */ .

ОПЕРАЦИЯСОДЕРЖИМОЕ СТЕКА
227 32 10
*/726
5 +731
10 /73

Окончательное деление на 10 приводит число к надлежащему виду, в чем вы можете убедиться самостоятельно [ДЛЯ СПЕЦИАЛИСТОВ. Существует и более быстрое определение

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

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

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

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

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 n3 -- n-остаток n-результат) Умножение, затем деление (n1*n2/n3). Помещение на стек остатка и частного. Для промежуточного результата используется слово двойной длины.

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

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Пт Апр 16, 2021 12:41 am

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

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

31416 10000 */

что дает довольно хорошее приближение.

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

: PI ( n -- n') 31416 10000 */ ;
: ПЛОЩАДЬ ( радиус -- площадь) DUP * PI ;

С помощью определения вычислим площадь круга с радиусом 10 дюймов:

10 ПЛОЩАДЬ . _314_ok

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

355 113 */

обеспечивающая точность, большую, чем шесть знаков после запятой, в то время как 31416 - обеспечивает менее четырех знаков.

Следовательно, наше новое, улучшенное определение будет иметь вид:

: PI ( n -- n') 355 113 */ ;

Оказывается, вы можете хорошо аппроксимировать любую константу посредством множества различных пар целых чисел, значения которых меньше, чем 32768, с погрешностью менее 1e-8.

ДЛЯ ИСТИННЫХ МАТЕМАТИКОВ. Ниже приводится небольшая таблица вещественной аппроксимации различных констант:

ЗНАЧЕНИЯАППРОКСИМАЦИЯПОГРЕШНОСТЬ
пи = 3.141...355/1138.5e-8
корень из 2 = 1.414...19601/138601.5e-9
корень из 3 = 1.732...18817/108641.1e-9
e = 2.718...28667/105465.5e-9
корень из 10 = 3.162...22936/72535.7e-9
корень 12-й степени из 2 = 1.059...26797/252931.0e-9
log10(2)/1.6384 = 0.183...2040 /111031.1e-8
ln(2)/16.384 = 0.042...485/114641.0e-7
.001град./22-bit rev = 0.858...18118/211091.4e-9
arc-sec/22-bit rev = 0.309...9118/295091.0e-9
c = 2.9979248...24559/81921.6e-9

ОПЕРАЦИИ НАД ДРОБНЫМИ ЧИСЛАМИ

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

7/34 + 23/99 =

На Форте вы можете сделать это с помощью команд так называемой ДРОБНОЙ АРИФМЕТИКИ, реализующих операции над числами С ФИКСИРОВАННОЙ ТОЧКОЙ.

При использовании дробной арифметики мы применяем масштабирование с подразумеваемым положением десятичной точки. Но вместо масштабирования посредством умножения на степень 10 (как принято при вычислениях вручную) мы будем масштабировать путем умножения на степень 2 (как принято при вычислении на компьютере). Таким образом, выражение "десятичная точка" уместно заменить выражением "двоичная точка" [ДЛЯ НАЧИНАЮЩИХ. Этот вопрос подробнее рассматривается в гл.7].
Допустим, вы ввели следующее определение:

: +1 ( -- масштабная-единица) 16364 ;

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

0100000000000000

т.е. это единица в соответствующем масштабе с подразумеваемой двоичной точкой.

Теперь добавьте к Форту две новые арифметические операции: дробное умножение и дробное деление, выполняемые с учетом выбранного нами масштаба.

: *. ( n n -- n) +1 */ ;
: /. ( n n -- n) +1 SWAP */ ;

Что же у вас получилось? При делении единицы на единицу должна получиться единица.

1 1 /. . _16384_ok

(Напомним, что в выбранном масштабе 16384 это единица). Теперь разделите 1 на 2:

1 2 /. . _8192_ok

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

7 34 /. 23 99 /. +

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

10000 *. . _4381_ok

Ответ составит 4381/10000, или в более привычном виде 0.4381. С помощью дробной арифметики, используя быстрые операции над целыми числами, вы можете складывать, вычитать, умножать и делить даже вещественные числа. В вычислительных задачах (в которых выполняется множество арифметических действий), скорость вычислений считается самым важным параметром. Перевод же чисел в удобочитаемый вид (в десятичную систему) не столь существен, поскольку выводить нужно лишь конечный результат. Более того, в таких приложениях, как графика и робототехника, ВООБЩЕ НЕ ТРЕБУЕТСЯ перевод результата в десятичную систему - получаемая информация должна быть понятна, графическому устройству, плоттеру, руке робота или чему-то еще.

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

Нам все-таки хотелось бы вводить и выводить дроби с помощью привычных обозначений. Вы были бы не против, если бы Форт-система выводила результат с десятичной точкой в соответствующем месте? А для этого нужно всего лишь одно слово, представляющее число в традиционном формате. Воспользуемся средствами, описанными в гл.7 [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ ФИГ-ФОРТА, СИСТЕМ ПОЛИФОРТ, СОЗДАННЫХ ДО ВВЕДЕНИЯ СТАНДАРТА-83 И ДРУГИХ СТАРЫХ СИСТЕМ. Перед SIGN слово ROT должно быть опущено]:

: #.#### DUP ABS 0 <# # # # # 46 HOLD # ROT SIGH #>
TYPE SPACE ;
: .F ( дробь --) 10000 *. #.#### ;

Теперь наше выражение будет иметь вид

7 34 /. 23 99 /. + .F _0.4381_ok

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

Допустим, вы хотите, чтобы ВВОДИМЫЕ аргументы выглядели как вещественные числа, например:

.1250 + .3750 = ?

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

: D>F ( d -- дробь) DROP 10000 /. ;

(D>F означает перевод из числа двойной длины в дробное. DOUBLE - двойной, FRACTION - дробь). Теперь можно вводить следующие выражения:

.1250 D>F .3750 D>F + .F _0.5000_ok

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

.7500 D>F .5000 D>F *. .F _.3750_ok

Интересно отметить, что если вы выполняете операцию умножения *. дроби на целое, то результат будет целым, например:

28 .5000 D>F *. . _14_ok

Применяя операцию /. , можно разделить, скажем, -0.3 на 0.95:

-.3000 D>F .9500 D>F /. .F _-0.3160_ok

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

22 44 /. .F _.5000_ok

Если же вы делите посредством /. целое на дробь, то в результате получите целое. Обозначив символом f дробь, а символом i целое, мы можем построить следующую таблицу выполнения операций:

ОПЕРАЦИЯРЕЗУЛЬТАТ
f f +f
f f -f
f i *f
i f *f
f i /f
f f *.f
f i *.i
i f *.i
f f /.f
f i /.i
i f /.i

Использование двоично-ориентированного масштаба, например числа 16384, а не десятично-ориентированного, например 10000, позволяет обеспечить в пределах 16 разрядов большую точность (точность повышается в отношении 16 к 10). При этом операции */ и /. кодируются на ассемблере чрезвычайно эффективно. Но и число 16384 мы выбрали в качестве единицы произвольно. Если бы было выбрано число 256, то получилось бы восемь разрядов (включая знак) слева от двоичной точки и восемь разрядов справа. Этот метод при необходимости можно применить и к 32-разрядным числам.

Операции *. и /. можно выполнять над тригонометрическими функциями, поскольку значение угла представляется как функция длины окружности, выраженная дробью (в интервале от 0 до 1). Перевод радиан в градусы и обратное преобразование осуществляются весьма просто.

ЗАКЛЮЧЕНИЕ

Итак, вы познакомились с масштабированием, методами округления и аппроксимации вещественных чисел и операциями над числами с фиксированной точкой. Для того чтобы вы чувствовали себя увереннее при решении сложных математических задач, где необходимо следить за правильностью выбора масштаба в процессе решения посмотрите второй пример в гл.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 n3 -- результат) Умножение, затем деление (n1*n2/n3). Промежуточный результат 32-разрядный.
*/MOD ( n1 n2 n3 -- n-остаток n-результат) Умножение, затем деление (n1*n2/n3). Помещение на стек остатка и частного. Для промежуточного результата используется слово двойной длины.

ОСНОВНЫЕ ТЕРМИНЫ

АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ НАД ЧИСЛАМИ С ФИКСИРОВАННОЙ ТОЧКОЙ -- Операции над числами, в которых не указано положение десятичной точки. Вместо этого для некоторой группы чисел программа подразумевает десятичную точку в какой-то позиции или хранит позицию десятичной точки для этой группы чисел в виде отдельного числа.
АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ НАД ЧИСЛАМИ С ПЛАВАЮЩЕЙ ТОЧКОЙ -- Операции над числами, в которых положение десятичной точки указано. Программа должна интерпретировать истинное значение каждого отдельного числа, прежде чем какая-либо арифметическая операция может быть выполнена.
ДРОБНАЯ АРИФМЕТИКА -- Арифметика, где можно выражать дроби с помощью целых чисел, а за положением точки следит сам программист.
МАСШТАБИРОВАНИЕ -- Процесс умножения (или деления) какого-то числа в определенной пропорции. То же относится к процессу умножения (или деления) числа на степень десяти так, чтобы все значения в некотором наборе данных могли бы быть представлены как целые в предположении, что десятичная точка находится в одной и той же позиции для всех чисел, или на степень 2 в случае дробной арифметики.
ПРОМЕЖУТОЧНЫЙ РЕЗУЛЬТАТ ДВОЙНОЙ ТОЧНОСТИ (ДЛИНЫ) -- Значение двойной длины временно создается двухшаговой операцией, такой, как */ , тогда, когда промежуточное значение (результат первой операции) может превышать число одинарной длины, даже в том случае, если исходные аргументы и конечный результат не выходят за пределы одинарной точности.
СТЕК ДАННЫХ -- В Форте - это участок памяти, который служит общей областью для передачи аргументов между различными операциями (чисел, флагов и т.д.).
СТЕК ВОЗВРАТОВ -- В Форте - это участок памяти, который Форт-система использует для хранения "адресов возврата" в отличие от других объектов (будет рассмотрен в гл.9). Пользователь имеет право хранить значения в стеке возвратов временно, при определенных условиях.

УПРАЖНЕНИЯ

1. Укажите различия между -1 и 1- .

2. Переведите следующее алгебраическое выражение в форму определения Форта: -(ab)/c при состоянии стека ( а b с) .

3. При состоянии стека ( 6 70 123 45) напишите выражение, которое бы инициировало печать наибольшего из этих значений.

4. а) Определите слово 2ПОРЯДОК, которое из имеющихся в стеке двух чисел располагало бы в вершине большее, а оставшееся - под ним.
б) Определите слово 3ПОРЯДОК, которое располагало бы три заданных числа в стеке так, чтобы большее было в его вершине.
в) Вспомните определение ОБЪЕМ из гл.4. Перепишите его, используя определение ЗПОРЯДОК, так, чтобы пользователь мог вводить измерения в любом порядке.

Практикум в масштабировании

5. ГИСТОГРАММА - это графическое представление серии значений, каждая из которых выражена высотой или длиной некоторого отрезка. Определите слово с именем РИСУЙ - компонент вашей программы по созданию гистограмм по заданному значению в диапазоне от 0 до 100 слово РИСУЙ должно вывести на экран горизонтальную линию из звездочек, графически представляющую это заданное значение.

Трудность заключается в том, что на экране только 80 колонок. Таким образом, значение 100 должно соответствовать 80 звездочкам, значение 50 - 40 звездочкам, значение 0 - 0 звездочкам и т.д. (Начинайте ваше определение с команды CR и используйте вариант слова STARS из упр.4.7.).

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

oC = (oF - 32)/1.8;
oF = (oC * 1.8 ) + 32;
oK = oC + 273.

Выразите все аргументы и результаты целыми числами (в градусах):
а) 0oF в oС;
б) 212oF в oС;
в) -32oF в oС;
г) 16oС в oF;
д) 233oК в oС.

7. Определите слова для выполнения преобразований из упр.3. Используйте следующие имена:

F>C Р>K C>F C>K K>F K>C

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

ЛИТЕРАТУРА

1. Bowhill, Sidney A., "A Variable-Precision Floatmg-Point System for Forth," 1983 FORML Conference Proceedings, Asilomar, California.
2. Bumgarner, John O., and Jonathan R. Sand, "Dysan IEEE P-754 Binary Floating Point Architecture," 1983 Rochester Forth Conference Proceedings, pp.185-94.
3. Jesch, Michael, "Floating Point in FORTH?" 1981 FORML Conference Proceedings, pp.61-78; reprinted as "Floating Point FORTH?", Forth Dimensions, 4/1, May-June 1982, pp.23-25.
4. Harwood, James V., "FORTH Floating Point," 1981 Rochester Forth Standards Conference Proceedings, p.189.
5. Monroe, Alfred J., "Forth Floating-Point Package," Dr.Dobb's Journal, 7/9, September 1982, pp.16-29.
6. Petersen, Joel V., and Michael Lennon, "NIC-FORTH and Floating Point Arithmetic," 1981 Rochester Forth Standards Conference Proceedings, pp.213-17.
7. Duncan. Ray, and Martin Tracy, 'The FVG Standard Floating-Point Extension," Dr.Dobb's Journal, 9/9, September 1984, pp.110-15.
8. Redington, Dana, "Forth and Numeric Co-processors: An Extensible Way to Floatingpoint Computation," Conference Proceedings of the Eighth WCCF, 3, pp.368-73, March 18-20, 1983.
9. Redington, Dana, "Stack-Oriented Co-Processors and Forth," Forth Dimensions, 5/3 September-October 1983, pp.20-22.

ЗАМЕЧАНИЯ

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

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

И, да, все это пошло от Мура - Броуди. Начальный курс программирования на языке ФОРТ. 1990 Leaf10ТЕМА #3, АБЗАЦ #1272, #3.4.1Броуди. Начальный курс программирования на языке ФОРТ. 1990 Leaf10.
G.
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Сб Апр 17, 2021 12:20 am

ГЛАВА 6. ЦИКЛИЧЕСКИЕ СТРУКТУРЫ

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

Теперь вы должны научиться писать определения, позволяющие повторять тот или иной фрагмент несколько раз в зависимости от заданных условий. Такой вид структуры управления называется ЦИКЛОМ. Способность выполнять циклы - это второе и, вероятно, наиболее важное достоинство компьютеров. Если вы можете составить программу проверки одной платежной ведомости, то вы можете составить программу проверки и тысячи подобных ведомостей.

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

ЦИКЛЫ СО СЧЕТЧИКОМ

Одна из циклических структур называется ЦИКЛОМ СО СЧЕТЧИКОМ. Здесь вы сами определяете число повторений цикла. На Форте для этого нужно задать начальное и конечное числа перед словом DO (в обратном порядке), а затем поместить слова, выполнение которых вы хотите повторять, между словами DO (ВЫПОЛНИТЬ) и LOOP (ЦИКЛ). Например:

: ТЕСТ 10 0 DO CR ." Привет " LOOP ;

будет осуществлять возврат каретки и выводить слово ПРИВЕТ десять раз, потому что, если вычесть нуль из 10, вы получите 10.

ТЕСТ
_Привет
_Привет
_Привет
_Привет
_Привет
_Привет
_Привет
_Привет
_Привет
_Привет_ok

Как и оператор IF ... THEN, относящийся к операторам управления, оператор DO ... LOOP должен размещаться в пределах (одного) определения. Число 10 в приведенном примере называется ГРАНИЦЕЙ ЦИКЛА (limit), а нуль - ИНДЕКСОМ (index). Общий вид оператора цикла со счетчиком:

граница индекс DO ... LOOP

Теперь посмотрим, что происходит внутри цикла DO ... LOOP:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b07810

Слово DO [Единоутробный братец птички ДОДО] берет из стека данных границу и индекс и подготавливает их для слова LOOP, которое по этим данным будет управлять циклическим процессом.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b08310

Далее выполняются слова, находящиеся внутри цикла.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b08410

Получив управление, LOOP продвигается на единицу, после чего управление возвращается к DO. Когда LOOP "пересекает" финишную черту, электронный глаз переключает стрелку, разрешая тем самым выполняться операторам, следующим за словом LOOP.

Слово I помещает в стек данных текущее значение ИНДЕКСА (значение, на котором в этот момент стоит LOOP) на каждом шаге циклического процесса. Рассмотрим определение

: ДЕКАДА 10 0 DO I . LOOP ;

которое выполняется так, как показано ниже:

ДЕКАДА _0_1_2_3_4_8_6_7_8_9_ok

Заметьте, что цикл выполняется всего 10 раз - от 0 до 9. Мы не добираемся до 10, потому что LOOP пересекает границу в тот момент, когда 9 сменяется числом 10.
Вы можете выбрать любые числа, но так, чтобы значение границы превышало значение индекса:

: ПРИМЕР -243 -250 DO I . LOOP ;
ПРИМЕР _-250_-249_-248_-247_-246_-245_-244_ok

И в этом примере значение границы (-243) больше, чем начальный индекс (-250).

ОГРАНИЧЕНИЯ НА ВЫПОЛНЕНИЕ ЦИКЛА

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

: ОШИБКА 10 10 DO I . LOOP ;

то не получите ожидаемого результата. Результат будет таков:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b08510

К сожалению, LOOP уже переступил финишную черту и не попадет под электронный глаз в течение длительного времени (до тех пор, пока не вернется назад через 65535 шагов) [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ, СОЗДАННЫХ ДО ПРИНЯТИЯ СТАНДАРТА-83. В более старых системах цикл выполнился бы один раз (вместо 64К раз), но и это не то, что вам нужно].

Если вы не уверены в том, что граница и индекс в каком-то вашем определении не совпадут, используйте вместо DO слово ?DO, которое, как и первое, берет из стека ( границу индекс --) , но сразу передает управление LOOP в том случае, когда граница и индекс совпадают.
Возвращаясь к слову STARS из гл.1, напишем его правильное определение:

: STARS ( число-звездочек) 0 ?DO 42 EMIT LOOP ;

При отсутствии в вашей системе слова ?DO используйте следующее определение:

: STARS ( число-звездочек) ?DUP IF 0 DO 42 EMIT LOOP THEN ;

С какими неприятностями вы еще можете столкнуться? В большинстве Форт-систем при выполнении цикла DO текущие значения счетчика хранятся в стеке возвратов (введенном в гл.5). Это приводит к некоторым ограничениям. В частности, слово I можно употреблять только в том определении, в котором используются слова DO и LOOP. Вы не имеете права ввести такой текст:

: ТЕСТ I . ;
: ДЕКАДА 10 0 DО ТЕСТ LOOP ;

Если вспомогательное определение (ТЕСТ) требует значения индекса, вы должны передать его через стек, например:

: ТЕСТ ( индекс --) . ;
: ДЕКАДА 10 0 DO I ТЕСТ LOOP ;

Отметим еще несколько ограничений. Вы не должны перед словом DO помещать в стек возвратов с помощью слова >R промежуточные значения с тем, чтобы использовать их внутри цикла. Если же вы запрограммировали внесение некоторого значения в стек возвратов внутри цикла, то обязаны перед выходом из цикла, до начала выполнения LOOP (или слова LEAVE, которое мы рассмотрим ниже), запрограммировать его удаление словом R>.

ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ ОПЕРАТОРА ЦИКЛА DO

Вы можете оставить в стеке какое-нибудь число, которое будет служить аргументом для некоторого объекта внутри оператора цикла. Например,

: ПРОИЗВЕДЕНИЯ ( n) CR 11 1 DO DUP I * . LOOP DROP ;

даст вам следующий результат:

7 ПРОИЗВЕДЕНИЯ
_7_14_21_28_35_42_49_56_63_70_ok

или

8 ПРОИЗВЕДЕНИЯ
_8_16_24_32_40_48_56_64_72_80_ok

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

Существует несколько "хитрых" примеров работы со стеком внутри цикла DO. Рассмотрим их на примере начисления сложных процентов. При заданном начальном остатке, скажем 1000дол. и норме процента, допустим 6%, требуется написать определение для создания и вывода на печать таблицы, приведенной ниже:

1000 6 СЛОЖНЫЕ-ПРОЦЕНТЫ
_Год_1____Сумма_1060
_Год_2____Сумма_1124
_Год_3____Сумма_1191
и т.д.

и т.д. на двадцать лет. Сначала мы загружаем слово R%, специфицированное в гл.5, а затем создаем определение

: СЛОЖНЫЕ-ПРОЦЕНТЫ ( вклад процент --)
SWAP 21 1 DO
CR ." Год " I . 3 SPACES 2DUP R% + DUP ." Сумма " .
LOOP 2DROP ;

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b08610

На каждом шаге выполнения цикла мы применяем операцию 2DUP и таким образом обеспечиваем значение текущего остатка и неизменной нормы процента для следующего шага. После выхода из цикла по окончании вычислений мы убираем эти значения с помощью операции 2DROP.

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

: ПРЯМОУГОЛЬНИК 256 0 DО I 16 MOD 0= IF
CR THEN ." *" LOOP ;

Слово ПРЯМОУГОЛЬНИК выведет на печать 256 звездочек, причем после каждой 16-й звездочки он будет осуществлять возврат каретки на вашем терминале. В результате вы получите

****************
****************
****************
****************
****************
****************
****************
****************
****************
****************
****************
****************
****************
****************
****************
****************

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

Ранее мы определили слово с именем ПРОИЗВЕДЕНИЯ, в котором содержался цикл 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_30_35_40_45_ok

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

Отрицательное приращение заставляет цикл выполняться в обратном направлении, например:

: СНИЖЕНИЕ -10 0 DO I . -1 +LOOP ;

дает такой результат:

СНИЖЕНИЕ _0_-1_-2_-3_-4_-5_-6_-7_-8_-9_-10_ok

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b08710

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

: ПРИРАЩЕНИЕ ( приращение граница индекс -- ) DO I . DUP +LOOP DROP ;

Внутри этого определения непосредственно нет приращения. Оно будет взято из стека при выполнении слова ПРИРАЩЕНИЕ наряду с границей и индексом. Посмотрите, что происходит в данном случае:

1 5 0 ПРИРАЩЕНИЕ _0_1_2_3_4_ok
2 5 0 ПРИРАЩЕНИЕ _0_2_4_ok
-3 -10 10 ПРИРАЩЕНИЕ _10_7_4_1_-2_-5_-8_ok

Наш следующий пример демонстрирует изменение приращения на каждом шаге выполнения цикла:

: УДВАИВАНИЕ CR 32767 1 DO I . I +LODP ;

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

УДВАИВАНИЕ
_1_2_4_8_16_32_64_128_256_512_1024_2048_4096_8192_16384_ok

Если бы в этом примере аргумент для +LOOP принял хотя бы один раз значение 0, то мы никогда не вышли бы из цикла - получился бы так называемый БЕСКОНЕЧНЫЙ ЦИКЛ [ДЛЯ СПЕЦИАЛИСТОВ. Некоторые Форт-системы, созданные до принятия Стандарта-83, включают /LOOP, которое, как и +LOOP, выбирает из стека приращение, но оно должно быть положительным. Это может привести к тому, что индекс превысит значение 32767, скажем, при индексации по адресам или номерам блоков, так как указанные числа принимают только положительные значения в отличие от тех ситуаций, когда выполняются операции над числами со знаком. Слово +LOOP, удовлетворяющее Стандарту-83, снимает данную проблему].

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b08810

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

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

Рассмотрим, каким образом "оперившийся" программист может заняться отладкой определения СЛОЖНЫЕ-ПРОЦЕНТЫ (из первого раздела настоящей главы). Прежде чем добавить сообщения ." , программист может кратко записать такой вариант на листе бумаги:

: СЛОЖНЫЕ-ПРОЦЕНТЫ ( вклад процент -- )
SWAP 21 1 DO CR I . 2DUP R% + DUP . LOOP 2DROP ;

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

Шаг 1

1000 6 SWAP .S<return>
_6_1000_ok
2DUP .S<return>
_6_1000_6_1000_ok
R% .S<return>
_6_1000_60_ok
+ .S<return>
_6_1060_ok

Шаг 2

2DUP R% + .S<return>
_6_1124_ok
2DROP .S<return>
_Стек_пуст_ok

При моделировании мы не используем фразы "граница счетчик DO", "I" и "DUP". Цикл отлажен. Считая, что выполнен последний шаг, проверяем, не пуст ли стек.

ПОЛЕЗНЫЙ ПРИЕМ. Как очистить стек и при этом эмоционально разрядиться. Иногда начинающий программист может случайно написать цикл, который после себя оставляет в стеке множество значений, например:

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

вместо

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

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

ASDFKJ

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

ЦИКЛЫ С УСЛОВИЕМ

Наряду с циклами DO, которые называются циклами со счетчиком, Форт поддерживает ЦИКЛЫ С УСЛОВИЕМ. Цикл такого рода выполняется либо неопределенно долго, либо до тех пор, пока не произойдет некоторое событие. Один из видов цикла с условием -

BEGIN ... UNTIL

Этот цикл выполняется до тех пор, пока некоторое условие истинно. Форма его применения:

BESIN xxx ? UNTIL

Здесь xxx обозначает слова, которые должны повторяться в рамках цикла, а ? - некоторый флаг. Пока флаг принимает значение "ложь", цикл будет повторяться, но как только флаг примет значение "истина", цикл завершится.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b08910

Оператор цикла BEGIN ... UNTIL используется в одном из определений в примере со стиральной машиной, который рассматривался ранее:

: ДО-НАПОЛНЕНИЯ BEGIN ?ПОЛОН UNTIL ;

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

: НАЛИТЬ-ВОДУ КРАНЫ ОТКРЫТЬ ДО-НАПОЛНЕНИЯ КРАНЫ ЗАКРЫТЬ ;

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

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

BEGIN xxx FALSE UNTIL

(Слово FALSE выполняет те же функции, что и число 0, поэтому, если в вашей системе нет этого слова, используйте вместо него 0). Так как флаг, относящийся к UNTIL, всегда имеет значение "ложь", цикл будет повторяться бесконечно. В некоторых системах слово AGAIN заменяет выражение FALSE UNTIL.

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

Известна еще одна форма организации цикла с условием [REPEAT - ПОВТОРЯТЬ, WHILE - ПОКА.- Прим.пер.]:

BEGIN xxx ? WHILE yyy REPEAT

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b07910

Заметьте, что результат проверки условия в цикле описываемой конструкции противоположен результату проверки в цикле BEGIN ... UNTIL. В первом случае цикл повторяется до тех пор, ПОКА НЕКОТОРОЕ УСЛОВИЕ ИСТИННО (а не до тех пор, ПОКА ОНО НЕ СТАНЕТ ИСТИННЫМ).

Предположим, у нас имеется некоторая база данных, заполненная именами. Мы уже определили слово НОМЕР-ИМЕНИ, помещающее в стек либо номер следующего имени из списка, либо значение "ложь", если список исчерпан. Кроме того, у нас есть слово .ИМЯ, которое выводит само имя по его номеру в стеке.

: НОМЕР-ИМЕНИ ( -- номер-имени-или-ложь) ... ;
: .ИМЯ ( номер-имени --) ... ;

Допустим, нам нужно вывести список всех имен. До тех пор, пока не распечатан список, мы не знаем, сколько там имен, и поэтому не можем воспользоваться конструкцией DO LOOP, но можем написать определение:

: .ВСЕ-ИМЕНА
BEGIN НОМЕР-ИМЕНИ DUP WHILE CR .ИМЯ REPEAT PROP ;

А что произойдет, если список окажется пустым? На первом шаге выполнения цикла НОМЕР-ИМЕНИ поместит в стек нуль и вторая часть цикла никогда не будет исполнена. Слова WHILE исключает необходимость в специальной проверке.

ВЫХОД ИЗ ЦИКЛА (LEAVE)

Существует способ написания цикла со счетчиком, при котором выполнение цикла может закончиться раньше, чем будет достигнуто заданное значение границы. Для этого нужно с помощью слова LEAVE запрограммировать внутри цикла DO изменение условия с "истины" на "ложь". LEAVE заставляет цикл немедленно завершиться [Для пользователей систем, разработанных до принятия Стандарта-83. Ранее слово LEAVE вызывало завершение цикла при выполнении очередного слова LOOP или +LOOP.].

Перепишем наше прежнее определение слова СЛОЖНЫЕ-ПРОЦЕНТЫ. Теперь мы не будем запускать цикл ровно 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 ;

В результате получим:

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 один раз в начало определения:

: ПРЯМОУГОЛЬНИК PAGE 256 0 DO
I 16 MOD 0= IF CR THEN ." *" LOOP ;

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

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

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

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

DO ( граница индекс --) ... LOOP ( --) Организация цикла со счетчиком по заданному диапазону индексов.
DO ... +LOOP ( n --) Аналогично DO ... LOOP . Только к индексу на каждом шаге добавляется значение n (а не как всегда единица).
LEAVE ( --) Немедленное завершение выполнения цикла LOOP или +LOOP. (Используется только внутри цикла).
BEGIN ( --) ... UNTIL: ( ? --) Организация цикла с условием, который завершается, когда ? принимает значение истина.
BEGIN xxx WHILE ( ? --) yyy REPEAT ( --) Организация цикла с условием, причем xxx выполняется всегда, а yyy - только если ? истинно.
.R ( u ширина-поля --) Вывод числа одинарной точности без знакa. Число выровнено справа по границе поля.
PAGE ( --) Чистка экрана дисплея и установка курсора в верхний левый угол.
QUIT ( --) Завершение выполнения текущей задачи и возврат управления на терминал.

ОСНОВНЫЕ ТЕРМИНЫ

БЕСКОНЕЧНЫЙ ЦИКЛ -- Циклическая структура, позволяющая,повторять слова, содержащиеся внутри цикла, без возможности внешнего прерывания. Остановить такие вычисления можно только отключением или перезагрузкой компьютера.
ЦИКЛ СО СЧЕТЧИКОМ -- Циклическая структура, позволяющая повторять слова, содержащиеся внутри цикла, определенное число раз. В Форте это число зависит от начального и конечного счетчиков (индекса и границы), которые помещаются в стек до выполнения слова DO.
ЦИКЛ С УСЛОВИЕМ -- Циклическая структура, при которой слова, содержащиеся внутри цикла, повторяются до тех пор, пока не изменится некоторое условие истинности (с истины на ложь или со лжи на истину). В Форте бесконечный цикл начинается со слова BEGIN.

УПРАЖНЕНИЯ

В задачах с 1 и по 6 вы должны создать определения нескольких слов, которые будут выводить шаблоны из звездочек. Это потребует применения циклов DO и BEGIN ... UNTIL.

1. Создайте слово с именем STARS, которое выводит n звездочек в одной строке, n задается в стеке.

2. Определите слово БРУСОК, которое выводит прямоугольник из звездочек. Ширина и высота (число строк) задаются в стеке в следующем порядке: ( ширина высота).

10 3 БРУСОК
-**********
-**********
_**********_ok

3. Создайте слово \STARS, которое выводит наклонные (ромбовидные) массивы из звездочек. Высота задается в стеке. Используйте цикл DO и для простоты примите ширину постоянной и равной 10 звездочкам.

3 \STARS
_**********
__**********
___**********_ok

4. Определите слово, которое выводит массивы из звездочек с наклоном в другую сторону, и назовите его /STARS. Высота должна задаваться в стеке, а ширину возьмите постоянной и равной 10 звездочкам. Используйте оператор DO.

5. Переопределите последнее слово с использованием оператора цикла BEGIN ... UNTIL.

6. Напишите определение с именем РОМБЫ, которое выводит заданное число ромбов, как показано в следующем примере:

2 РОМБЫ
__________*
_________***
________*****
_______*******
______*********
_____***********
____*************
___***************
__*****************
_*******************
_*******************
__*****************
___***************
____*************
_____***********
______*********
_______*******
________*****
_________***
__________*
__________*
_________***
________*****
_______*******
______*********
_____***********
____*************
___***************
__*****************
_*******************
_*******************
__*****************
___***************
____*************
_____***********
______*********
_______*******
________*****
_________***
__________*

7. В гл.3 было введено слово THRU. Сможете ли вы написать определение этого слова с помощью вызова LOAD ? Если хотите, расширьте функции слова так, чтобы при использовании его во время загрузки блока выводился бы номер последнего.

8. При рассмотрении слова LEAVE мы приводили пример, в котором вычислялись сложные проценты при норме процента 6% и начальном остатке 1000дол. за 20 лет или же до тех пор, пока начальный остаток не удвоится, в зависимости от того, какое из событий наступит раньше. Перепишите это определение таким образом, чтобы значения начальной суммы и нормы процента находились в стеке и процесс вычисления завершился с помощью LEAVE как только сумма начального остатка удвоится.

9. Определите слово с именем ** , которое вычисляло бы степень, например:

7 2 ** . _49_ok

(семь в квадрате)

2 4 ** . _16_ok

(два в четвертой степени)

Для простоты показатель степени примите положительным, но убедитесь в том, что ** выполняется правильно, если показатель равен нулю (в результате должна получиться единица) или единице (в результате должно получиться само число).
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Вс Апр 18, 2021 12:38 am

ГЛАВА 7. ЧИСЛО ТИПОВ ЧИСЕЛ

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

Излагаемый здесь материал разделен на две части. В первой части (ДЛЯ НАЧИНАЮЩИХ) рассматриваются числа применительно к компьютеру, а также дается точное определение терминов СО ЗНАКОМ, БЕЗ ЗНАКА, ОДИНАРНАЯ ДЛИНА, ДВОЙНАЯ ДЛИНА, во второй (для всех) продолжается обсуждение Форта и объясняется, как трактуются числа со знаком и без знака, одинарной и двойной длины (точности).

ЧАСТЬ 1. ДЛЯ НАЧИНАЮЩИХ. ЧЕМ ОТЛИЧАЮТСЯ ЧИСЛА СО ЗНАКОМ И БЕЗ ЗНАКА

Все цифровые компьютеры хранят числа в двоичной форме [ДЛЯ ТЕХ, КТО НЕ ЗНАКОМ С ДВОИЧНОЙ СИСТЕМОЙ СЧИСЛЕНИЯ. Попросите кого-нибудь из ваших друзей (увлекающегося математикой) рассказать вам об этой системе или поищите учебное пособие по ЭВМ для начинающих]. Элемент стека Форт-системы состоит из 16 разрядов [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ 32-РАЗРЯДНЫХ КОМПЬЮТЕРОВ. В таких процессорах, как 68000, стек Форта обычно 32-разрядный. Поэтому термин, ЧИСЛО ОДИНАРНОЙ ДЛИНЫ в данном случае означает 32-разрядное число], или битов (бит - двоичная цифра). Ниже показана структура из 16 битов, где приводятся значения всех битов:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b09010

Если в каждом бите хранится единица, то это число составит 65535. Таким образом, 16 битами можно представить любое значение от 0 до 65535. Поскольку такой способ представления не позволяет нам изображать отрицательные числа, мы называем их ЧИСЛАМИ БЕЗ ЗНАКА. В наших таблицах и стековой нотации числа без знака обозначаются буквой u.

Но как же быть с отрицательными числами? Чтобы иметь возможность изображать как положительные, так и отрицательные числа, необходимо пожертвовать одним битом, задействовав его под знак. Для этой цели мы отведем самый левый "старший по порядку" бит. 15 оставшихся бит позволят нам представить любое число вплоть до 32767. Если знаковый бит содержит единицу, то нужно отложить изображаемое значение по оси влево от нуля, т.е. в области отрицательных чисел. Таким образом, с помощью 16 бит можно представить число в диапазоне от -32768 до +32767. Вы уже знаете, что этот диапазон составляют числа одинарной длины, обозначаемые буквой n.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b09510

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

Чтобы понять, как представляются отрицательные числа, вернемся снова к десятичной системе счисления и понаблюдаем за показаниями счетчика, устанавливаемого на магнитофонах многих типов. Допустим, что счетчик высвечивает три цифры (трехзначное число). По мере перемотки ленты это число увеличивается. Установим счетчик на нуль и начнем перематывать ленту в обратном направлении. Первое число, которое вы увидите на индикаторе, будет 999. Его мы воспринимаем как -1. Следующим числом окажется 998, что соответствует -2, и т.д.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b09710

Аналогично представляются числа со знаком и в компьютере. Если мы начнем с нуля:

0000000000000000

и вернемся назад на одно число, то будем иметь

1111111111111111 (16 единиц)

что означает 65535 при изображении чисел без знака и -1 при изображении чисел со знаком. Число

1111111111111110

соответствует 65534 при изображении чисел без знака и -2 при изображении чисел со знаком.

(Традиционно числа -1 и 0 применяются в качестве значений "истина" и "ложь" потому, что в представлении -1 все биты установлены, а в представлении 0 - сброшены).

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b09810

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

Продемонстрируем изложенное на примере простой задачи: 2-1. Эта задача эквивалентна задаче сложения 2+(-1). При двоичном представлении чисел одинарной длины двойка выглядит следующим образом:

0000000000000010

а отрицательная единица - так:

1111111111111111

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

0000000000000010
+
1111111111111111
-----------------
10000000000000001

Как видите, компьютеру потребовалось выполнить перенос единицы в старший разряд во всех колонках, а последняя единица была вынесена в 17-й разряд. Но так как элемент стека состоит только из 16 битов, будет выведено число

0000000000000001

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

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

АРИФМЕТИЧЕСКИЙ СДВИГ

Когда мы рассматривали в гл.5 выполнение компьютером некоторых арифметических операций, нам встретились две "таинственные" фразы: "арифметический сдвиг влево" и "арифметический сдвиг вправо". Теперь настала пора объяснить их.

ОТ ЧЕГО ЗАВИСИТ БЫСТРОТА ОТВЕТА ФОРТ-СИСТЕМЫ?

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

Для иллюстрации представим какое-нибудь число, скажем шесть, в двоичном виде:

0000000000000110

(4 + 2). Сдвинем каждую цифру этого числа на один разряд влево, а освободившийся бит заполним нулем:

0000000000001100

Мы получили двоичное представление числа 12 (8 + 4), что ровно в два раза больше первоначального числа. Такой способ применим во всех случаях. Если вы переместите каждую цифру числа на одну позицию ВПРАВО и заполните освободившийся бит нулем, то в результате всегда получите ПОЛОВИНУ первоначального числа.

При арифметическом сдвиге знаковый разряд не смещается. Иными словами, положительное число остается положительным, а отрицательное - отрицательным при выполнении операций деления и умножения на два. (Сдвиг, при котором вместе с остальными битами сдвигается и старший по порядку бит, называется ЛОГИЧЕСКИМ СДВИГОМ). Вы должны знать, что компьютер выполняет сдвиг цифр намного быстрее, чем операции обычного деления или умножения. Когда скорость критична, то лучше написать 2* , чем 2 * , и даже может быть лучше написать 2* 2* 2* , чем 8 * - все зависит от конкретной модели вашего компьютера.

ЧИСЛА ДВОЙНОЙ ДЛИНЫ

Вы, вероятно, уже знаете, что такое число двойной длины. Это число, представление которого занимает 32 бита вместо 16. Двойная длина допускает представление чисел в диапазоне +/-2147483647 (т.е. свыше четырех биллионов).

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b09910

В Форте число двойной длины занимает в стеке место двух чисел одинарной длины. Такие операции, как 2SWAP и 2DUP, применимы и для чисел двойной длины, и для пары чисел одинарной длины.

Обращаем ваше внимание на то, что в программировании термином "слово" принято обозначать 16-разрядное число, или два байта. В Форте же под словом понимают некоторым образом определенную команду. Поэтому, чтобы избежать коллизий, программирующие на Форте 16-разрядное значение называют ЯЧЕЙКОЙ. Число двойной длины требует для своего представления двух ячеек.

ПРЕИМУЩЕСТВА ШЕСТНАДЦАТЕРИЧНОЙ СИСТЕМЫ СЧИСЛЕНИЯ (И ДРУГИЕ СИСТЕМЫ)

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

Программисты начали использовать шестнадцатиричные и восьмиричные числа потому, что компьютеры "думают" в двоичной системе, а человеку очень трудно воспринимать числа, состоящие из длинного ряда двоичных цифр. Намного проще перевести двоичное число в шестнадцатиричное, чем двоичное в десятичное, поскольку 16 является степенью числа 2, в то время как 10 - нет. То же самое справедливо и для восьмиричной системы. Поэтому программисты обычно применяют для записи двоичных чисел, которыми компьютер обозначает адреса и машинные коды, шестнадцатиричную или восьмиричную системы. Шестнадцатиричная система выглядит на первый взгляд необычно, так как в ней задействованы буквы от А до F.

ДЕСЯТИЧНАЯДВОИЧНАЯШЕСТНАДЦАТИРИЧНАЯ
000000
100011
200102
300113
401004
501015
601106
701117
810008
910019
101010А
111011В
121100С
131101D
141110Е
151111F

Возьмем двоичное число одинарной длины: 0111101110100001. Для того чтобы перевести это число в шестнадцатиричную систему, мы сначала должны разбить его на тетрады: 0111 1011 1010 0001, затем заменить каждую тетраду на ее шестнадцатиричный эквивалент: 7 В А 1 или просто 7ВА1.

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

КОД ДЛЯ ПРЕДСТАВЛЕНИЯ СИМВОЛЬНОЙ ИНФОРМАЦИИ (ASCII)

Если числа в компьютере хранятся в двоичной форме, то как в нем хранятся буквы или другие символы? Тоже в двоичной форме, только в специальном коде, который был принят в качестве промышленного стандарта много лет назад, ASCII [Код ASCII совпадает с применяемым в СССР семиразрядным кодом КОИ-7. В реализациях Форта на отечественных ЭВМ используются восьмиразрядные коды (например, КОИ-8 ) с добавлением кодов русских букв, что и позволяет использовать русские слова.- Примеч ред.] (стандартный американский код для обмена информацией). В табл.7.1 приводятся все символы системы и их цифровые представления как в шестнадцатиричной, так и в десятичной форме.

Таблица 7.1. Символы в коде ASCII и их числовые эквиваленты [В первом столбце перечислены символы в коде ASCII или, если это управляющие символы, в общепринятых обозначениях; в двух последующих столбцах даются их шестнадцатиричные и десятичные эквиваленты]

СимволШестн.Дес.СимволШестн.Дес.СимволШестн.Дес.СимволШестн.Дес.
NUL000SP2032@4064`6096
SOH011!2133A4165а6197
STX022"2234В4266b6298
ETX033#2335С4367с6399
EOT044$2436D4468d64100
ENQ055%2537E4569е65101
ACK066&2638F4670f66102
BEL077'2739G4771967103
BS088(2840H4872h68104
HT099)2941I4973i69105
LF0A10*2A42J4A74j6A106
VT0B11+2B43К75k6B107
FF12,2C44L4C76l6C108
CR0D13-2D45M4D77m6D109
SM0E14.2E46N4E78n6E110
SI0F15/2P47O4F79o6F111
DLE101603048P5080p70112
DC1111713149Q5181q71113
DC2121823250R5282r72114
DC3131933351S5383s73115
DC4142043452Т5484t74116
NAK152153553U5585u75117
SYN162263654V5686v76118
ETB172373755W5787w77119
CAN182483856X5888x78120
EM192593957Y5989y79121
SUB1A26:ЗА58Z5A90z7A122
ESC1B27;3B59[91{7B123
FS1C28<3C60\5C92|7C124
GS1D29=3D61]5D93}7D125
RS1E30>3E62^5E94~7E126
US1F31?3F63_5F95DEL (RB)7F127

Символы, помещенные в первом столбце (имеющие шестнадцатиричные коды 0-1F), называются УПРАВЛЯЮЩИМИ, так как они указывают, что терминал или компьютер должен выполнить, например подать звуковой сигнал, вернуть каретку на одну позицию, начать новую строку и т.д. Остальные символы называются ПЕЧАТНЫМИ, поскольку они выводят на печать видимые символы, включая буквы, цифры от 0 до 9, все доступные символы и даже пробел (шестнадцатиричный код 20). Единственным исключением является символ DEL (шестнадцатиричный код 7F), который предписывает компьютеру игнорировать последний переданный символ.

В первой главе мы ввели слово EMIT, которое берет из стека значение в коде ASCII и посылает его на терминал для распечатки этого значения в виде символа, например:

65 EMIT _Aok
66 EMIT _Вok

(Мы используем десятичное, а не шестнадцатиричное представние, потому что ваш современный компьютер больше всего приспособлен ко вводу именно в таком представлении [ДЛЯ СПЕЦИАЛИСТОВ. Не тратьте время на раздел для начинающих]). Почему бы нам не проверить действие EMIT на каждом печатном символе "автоматически"?

: ОТОБРАЖАЕМЫЕ 127 32 DO I EMIТ SPACE LOOP ;

Слово ОТОБРАЖАЕМЫЕ выведет на печать каждый требуемый символ из кода ASCII, т.е. символы с кодами от десятичного 32 до десятичного 126. (Мы используем коды ASCII в качестве индекса цикла DO).

ОТОБРАЖАЕМЫЕ _!_"_#_$_&_'_(_)_*_+ ... _ok

Начинающие могут поинтересоваться, как поведет себя EMIT с управляющими символами; наберите на клавиатуре такой текст:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b10010

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

НАЗВАНИЕОПЕРАЦИЯДЕСЯТИЧНЫЙ ЭКВИВАЛЕНТ
BSВозврат назад на одну позицию ("забой")8
LFПеревод строки10
CRВозврат каретки13

Поэкспериментируйте с этими управляющими символами и посмотрите, как они выполняются.

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

В некоторых Форт-системах имеется слово ASCII, которое используется для улучшения читабельности определений, поскольку переводит отдельные символы в шестнадцатиричные значения. Вспомним, к примеру, следующее определение:

: STAR 42 EMIT ;

Если в вашей системе есть это слово, то вы можете определить его так:

: STAR ASCII * EMIT ;

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

ASCII ( -- c) перевод следующего символа из входного потока в его ASCII-эквивалент.
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Вс Апр 18, 2021 12:52 am

ЧАСТЬ 2. ДЛЯ ВСЕХ. ДВОИЧНАЯ ЛОГИКА

Слова AND и OR (введенные в гл.4) используют "двоичную логику", т.е. каждый бит проверяется независимо, и перенос единицы в старший разряд не производится. Выполним, например, операцию AND над следующими двумя двоичными числами:

AND
0000000011111111
0110010110100010
----------------
0000000010100010

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

Слово OR также применяет двоичную логику. В примере

OR
1000100100001001
0000001111001000
----------------
1000101111001001

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

1011101010011100

нулю или единице, путем маскирования остальных флагов:

AND
10111010100_1_1100
00000000000_1_0000
------------------
00000000000_1_0000

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

AND
10111010100_1_1100
11111111111_0_1111
------------------
10111010100_0_1100

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

OR
10111010100_0_1100
00000000000_1_0000
------------------
10111010100_1_1100

Ниже приводятся несколько приемов использования операции AND.

Вы могли заметить, что значение строчной буквы в коде ASCII отличается от значения прописной в точности на 32 (в десятичной системе счисления). Мы можем создать определение, которое осуществляло бы перевод строчного символа в прописной:

: ПРОПИСНОЙ ( строчный-символ -- прописной-символ ) 32 - ;

Итак,

97 EMIT _aok
97 ПРОПИСНОЙ EMIT _Aok

К сожалению, данный вариант слова ПРОПИСНОЙ не будет действовать в том случае, если переводимый символ уже является прописным, поскольку весь процесс перевода сводится к простому вычитанию.

Но код ASCII разработан очень мудро. Число 32 выбрано не случайно, а с учетом его представления в двоичной системе счисления. Посмотрите, как выглядят представления прописной и строчной букв А:

A 1000001
a 1100001

Они отличаются только одним битом, который и представляет число 32. Если мы сбросим этот бит в 0, то независимо от того, была ли буква прописной или строчной, она станет прописной:

: ПРОПИСНОЙ ( строчный-символ -- прописной-символ) 95 AND ;

Число 95 является десятичным эквивалентом двоичного числа 1011111 и совпадает с маской для двоичного представления числа 32. Следовательно,

97 ПРОПИСНОЙ EMIT _Aok
65 ПРОПИСНОЙ EMIT _Aok

(Однако поведение рассматриваемого варианта слова ПРОПИСНОЙ оказывается несколько странным по отношению к небуквенным символам. Попытайтесь, к примеру, перевести цифры).

Слово XOR также предназначено для работы с битами. Как отмечалось в гл.4, при выполнении этой операции истина получается только тогда, когда один из аргументов (но не оба сразу) истинен. Сравним результат выполнения операций XOR и OR:

OR
1000100100001001
0000001111001000
----------------
1000101111001001

XOR
1000100100001001
0000001111001000
----------------
1000101011000001

Если вы применяете операцию XOR с аргументом, все биты которого равны единице, то тем самым инвертируете биты второго аргумента.

XOR
1111111111111111
1000100100001001
----------------
0111011011110110

Таким образом, выражение

-1 XOR

является двоичной маской или шаблоном инвертирования. (Существует математический термин ДОПОЛНЕНИЕ ЧИСЛА ДО ЕДИНИЦЫ).

[СТАНДАРТ-83 И ОПЕРАЦИЯ NOT. Стандарт 83 изменил первоначальный смысл операции NOT. В системах, разработанных до принятия этого Стандарта, слово NOT заменяло значение логического аргумента оператора IF противоположным, т.е. не нуль (истина) становился нулем (ложью). Оно было синонимом слова 0= , созданным для улучшения читабельности программы. В Стандарте-83 слово NOT эквивалентно выражению "-1 XOR" и не сработает в том случае, если значение исходного флага "истина" не представлено как -1.

Обязательно убедитесь в том, что инвертируемое значение является логическим, а не арифметическим. Выражение "0= NOT" вырабатывает из ненулевого значения правильное логическое значение "истина"].

ЧИСЛА СО ЗНАКОМ И БЕЗ ЗНАКА

В первой главе мы ввели слово NUMBER (ЧИСЛО).

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b09110

Если слово INTERPRET (ИНТЕРПРЕТАТОР) не может найти введенную строку символов в словаре, то оно передает ее слову NUMBER, после чего NUMBER пытается прочесть всю совокупность символов как двоичное число. Когда NUMBER это удается, прочитанное число помещается в двоичной форме в стек NUMBER не проверяет числа на принадлежность их какому-либо диапазону [ДЛЯ НАЧИНАЮЩИХ. NUMBER не проверяет, выходит ли введенное вами в качестве числа одинарной длины значение за рамки соответствующего диапазона. Если вы ввели слишком большое число, то NUMBER преобразует его, но сохранит только 16 последних значащих цифр], поэтому может представлять вводимые числа либо как числа со знаком, либо как числа без знака. Например, при вводе любого числа в диапазоне от 32768 до 65535 NUMBER представит его в виде числа без знака, а любого значения в диапазоне от -32768 до -1 - как целое в двоичном дополнительном коде. Это важный момент: стек может быть использован для хранения целых чисел со знаком или целых чисел без знака. Будет ли некоторое двоичное значение интерпретироваться как целое со знаком или как целое без знака, зависит от выполняемых над ним операций. Вы выбираете то, что вам больше подходит в данной ситуации, а затем твердо придерживаетесь выбранного варианта.

Ранее мы ввели слово . , которое выводит на печать из стека значение в виде целого СО ЗНАКОМ:

65535 . _-1_ok

Слово U. печатает то же самое двоичное представление как число БЕЗ ЗНАКА:

6S535 U. _65535_ok

U. ( u --) Вывод числа одинарной длины без знака с одним пробелом после него.

Напоминаем, что буквой n обозначаются числа одинарной длины СО ЗНАКОМ, а буквой u - числа одинарной длины БЕЗ ЗНАКА. Ниже приводятся еще два слова, использующие числа без знака:

U.R ( u ширина --) Вывод числа без знака. Число выровнено по правой границе поля заданной ширины.
U< ( u1 u2 -- ?) Помещение на стек истины в том случае, если u1<u2. Оба аргумента рассматриваются как числа одинарной длины без знака.

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b09210

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

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 ! ;

ЧИСЛА ДВОЙНОЙ ДЛИНЫ

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

200000.<return>

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b09310

NUMBER воспринимает запятую как признак того, что данное значение должно быть представлено числом двойной длины и помещает это значение в стек в виде двух последовательных ячеек (ячейкой в Форте называется структура из 16 битов), причем старшая по порядку ячейка помещается над младшей [ДЛЯ СПЕЦИАЛИСТОВ. В большинстве Форт-систем положение десятичной точки запоминается в некоторой переменной, и вы можете это использовать в своих целях. Более подробную информацию вы найдете в документации по вашей системе. Мы продолжим обсуждение данного вопроса в гл.10 (см. "Преобразование чисел при вводе"). Кроме того, в некоторых системах, где применяются арифметические сопроцессоры, на расширенное представление целого числа указывает не десятичная точка, а символ X, например: 123456789X].

Слово D. выводит число двойной длины без всяких знаков пунктуации:

D. ( d --) Печать числа двойной длины со знаком.

Здесь d - целое число двойной длины со знаком. Например, если вы введете число двойной длины, а затем выполните операцию D., то компьютер вам ответит:

D. _200000_ok

В некоторых Форт-системах для выделения дробной части применяются еще четыре знака пунктуации:

, / - :

В таких системах все перечисленные ниже числа переводятся в одно и то же представление:

12345. D. _12345_ok
123.45 D. _12345_ok
1-2345 D. _12345_ok
1/23/45 D. _12345_ok
1:23:45 D. _12345_ok

а число -12345 - нет, потому что оно будет представлено как отрицательное число одинарной длины. (Это единственный случай, когда дефис интерпретируется как минус, а не как знак пунктуации).

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

ФОРМИРОВАНИЕ ЧИСЕЛ ДВОЙНОЙ ДЛИНЫ БЕЗ ЗНАКА

Приведенные ниже слова:

8200.00 12/31/86 372-8493 6:32:59 98.6

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

Самое простое определение форматного вывода вы можете написать следующим образом:

: UD. ( ud --) <# #S #> TYPE ;

UD. предназначено для вывода числа двойной длины без знака. Слова <# и #> (СКОБКА-ЧИСЛО и ЧИСЛО-СКОБКА) означают начало и конец процесса преобразования числа. В данном определении весь перевод осуществляется единственным словом #S (ЧИСЛА). #S преобразует значение из стека в символы кода ASCII. По этой команде формируется столько цифр, сколько их необходимо для представления числа: незначащие нули она не выводит. Однако всегда выводится по крайней мере одна цифра: если значение равно нулю, то выводится нуль, например:

12,345 UD. _12345ok
12. UD. _12ok
0. UD. _0ok

Слово TYPE (ПЕЧАТЬ) выводит символы, которые составляют число. Заметьте, что между числом и приглашением ok нет пробела. Для того чтобы вывести пробел, вы должны просто добавить слово SPASE, как это сделано в приведенном ниже примере:

: UD. ( ud -- ) <# #S #> TYPE SPACE ;

Предположим, что у вас в стеке имеется номер телефона, выраженный 32-разрядным целым числом, скажем 372-8493 (помните, что дефис указывает NUMBER на то, что число нужно воспринимать как значение двойной длины. В вашей системе это может быть точка). Вы хотите определить некоторое слово, которое будет представлять такое число снова в виде телефонного номера. Назовем его .ТЕЛЕФОН (для вывода номера телефона) и запишем следующее определение:

: .ТЕЛЕФОН ( ud -- )
<# # # # # 45 HOLD #S #> TYPE SPACE ;

Ваше определение .ТЕЛЕФОН содержит все компоненты слова UD. и некоторые другие. Слово Форта # (ЧИСЛО) выводит только одну цифру. Определение форматного вывода числа берет цифры выводимого числа в обратном порядке, поэтому выражение "# # # #" выводит четыре крайние правые цифры номера телефона. Теперь самое время вставить дефис. В таблице значений кода ASCII (см. часть 1 - для начинающих) дефис закодирован десятичным числом 45. Слово Форта HOLD (ЗАНЕСТИ) возьмет этот код ASCII и вставит его в строку символов форматируемого числа.

Если в вашей системе имеется слово ASCII, то вы можете заменить в приведенном выше определении непонятное выражение "45 HOLD" на более читабельное:

ASCII - HOLD

Слово ASCII {которое мы ввели в первой части данной главы) помещает в стек числовое значение следующего символа из входного потока, в нашем случае - дефис.

Итак, у вас остались три левые цифры. Вы можете воспользоваться выражением "# # #", но проще применить слово #S, которое автоматически приведет остаток вашего числа к требуемому виду.

Последовательность <#...#> называется выражением ФОРМАТНОГО ВЫВОДА ЧИСЛА, поскольку формирует шаблон (справа налево), по которому число должно быть сформатировано.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b09410

Теперь представим число двойной длины без знака как календарную дату: 7/15/86. Определение будет выглядеть так:

: .ДАТА ( ud -- )
<# # # ASCII / HOLD # # ASCII / HOLD # # #>
TYPE SPACE ;

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

# # ASCII / HOLD

(вы можете вместо "ASCII /" использовать 47) выводит две крайние правые цифры (год) и крайний слэш. При следующем вхождении этого выражения выводятся две средние цифры (день) и левый слэш. Наконец, "# #" выводит две крайние цифры (месяц). Вы можете определить:

: /nn ( ud -- ud) # # ASCII / HOLD ;
: .ДАТА ( ud --) <# /nn /nn # # #> ТУРЕ SPACE;

Поскольку вы сами управляете процессом преобразования чисел, можно на самом деле переводить различные числа из одной системы счисления в другую. Это может пригодиться вам при форматном выводе времени (часов и минут). Например, у вас в стеке время записано в секундах, а вы хотите определить слово, которое выражало бы это время в часах, минутах и секундах. Определение в данном случае может выглядеть следующим образом [ДЛЯ НАЧИНАЮЩИХ. См. полезный прием "Изменение системы счисления"]:

: SEXTAL 6 BASE ! ;
: :00 ( ud -- ud) # SEXTAL # DECIMAL ASCII : HOLD ;
: .СЕКУНДЫ <# :00 :00 #S #> TYPE SPACE ;

Для форматного вывода секунд и минут вы используйте слово :00 . Как секунды, так и минуты вычисляются по модулю 60, значит, правой цифрой может быть любая цифра до девяти, а левой - цифра от нуля до пяти включительно. Поэтому в своем определении :00 вы преобразуете первую цифру (она является правой) как десятичное число, затем переходите по слову SEXTAL в шестиричную систему (с основанием 6) и преобразуете левую цифру, после чего возвращаетесь в десятичную систему и вставляете символ двоеточия. После того как слово :00 преобразует секунды и минуты, #S переведет оставшиеся часы. Так, если у вас в стеке время задано как 4500с, то в результате вы получите:

4500. .СЕКУНДЫ 1:15:00 ok

(Если продолжительность дня измерять в секундах, то 86400с - это слишком много для 16-разрядного числа).
В табл. 7.2 сведены слова Форта, использующиеся при форматизации чисел. (Обратите внимание на условные обозначения в конце таблицы, которые напоминают вам о смысле символов "n", "d" и т.д.)

Таблица 7.2 Форматирование чисел
<# -- Начало процесса преобразования числа. В стеке должно находиться ЧИСЛО ДВОЙНОЙ ДЛИНЫ без знака
# -- Преобразование одной цифры и помещение ее в выходную символьную строку. # доставляет цифру в ЛЮБОМ СЛУЧАЕ - если вы подали этому слову на вход неверное цифровое значение, то и в этом случае вы получите нуль для каждого #
#S -- Преобразование числа (цифры за цифрой) до тех пор, пока в результате не получится нуль. Всегда доставляется ПО КРАЙНЕЙ МЕРЕ ОДНА ЦИФРА (нуль, если число равно нулю)
c HOLD -- Вставка в форматируемую символьную строку на текущую позицию символа, значение которого в коде ASCII находится в стеке
n SIGN -- Вставка знака "-" в выходную строку в том случае, если третье число в стеке отрицательное (это число из стека выбирается - см. сноску в следующем разделе)
#> -- Завершение преобразования числа и помещение в вершине стека счетчика символов и адреса (именно эти аргументы требуются для TYPE)


ВЫРАЖЕНИЕСОСТОЯНИЕ СТЕКАТИП АРГУМЕНТОВ
<# ... #>( d -- а u)32-разрядный без знака
( u 0 - а u)16-разрядный без знака
<# ... n SIGN #>( |d| -- а u)32-разрядный со знаком, где |d| является абсолютмым значением d, a n - верхней ячейкой d
( |n| 0 -- а u)16-разрядный со знаком, где |n| - абсолютное значение n

Условные обозначения:
n, n1 ... - 16-разрядные числа со знаком;
а - адрес;
d, d1 ... - 32-разрядные числа со знаком;
u, u1 ... - 16-разрядные числа без знака;
с - значение символа в коде ASCII.

ФОРМАТИРОВАНИЕ ЧИСЕЛ ОДИНАРНОЙ ДЛИНЫ СО ЗНАКОМ

До сих пор мы форматировали только числа двойной длины без знака. Xoтя структура <#...#> применима именно к таким числам, попробуем воспользоваться ею и для других типов чисел, выполнив определенные манипуляции со стеком. Например, рассмотрим простейший вариант системного определения D. (оно выводит число двойной длины СО ЗНАКОМ):

: D. ( d -- )
DUP >R DABS <# #S R> SIGN #> TYPE SPACE ;

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

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

Теперь расположение аргументов в стеке соответствует выражению форматного вывода. Затем #S осуществляет перевод цифр справа налево, после чего мы заносим в стек знак. Если этот знак отрицательный, то SIGN добавляет к форматированной строке минус [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ БОЛЕЕ РАННИХ СИСТЕМ. Слово SIGN имеет свою историю. Первоначально в качестве аргумента этого слова выступал третий элемент стека. Таким образом, для того чтобы определить слово D. , нужно было писать

: D. ( d --) SWAP OVER DABS <# #S SIGN #> TYPE SPACE ;

Выражение "SWAP OVER" помещает копию верхней ячейки (той, что со знаком) на дно стека. Чтобы упростить выражение форматного вывода, операцию ROT решено было перенести в определение слова SIGN.

Данное соглашение действовало в системах фиг-Форт и системах полиФорт, разработанных до введения Стандарта-83. В системах Форт-79 и Форт-83, а также Форт-системах МУР и MMS используется соглашение о передаче знака через вершину стека, что в большей степени соответствует механизму передачи данных через стек. Если вы располагаете старой версией, определите SIGN следующим образом:

: SIGN ( n --) 0< IF ASCII - HOLD THEN ;

]. Так как нам нужно, чтобы знак минус располагался слева, включаем SIGN справа в структуру <# ... #>. В некоторых случаях, например в бухгалтерских расчетах, может потребоваться вывод отрицательных чисел в виде 12345-. В подобной ситуации мы должны поместить слово SIGN СЛЕВА в выражение <# ... #>, как показано ниже:

<# SIGN #S #>

Определим слово, которое выводило бы число двойной длины со знаком с десятичной точкой и двумя десятичными значениями после точки. Поскольку такая форма часто используется для представления какой-либо суммы в долларах и центах, назовем это cлово .$ и определим его следующим образом:

: .$ ( d --)
DUP >R DABS <# # # ASCII . HOLD #S
R> SIGN ASCII S HOLD #> TYPE SPACE ;

Проверим его:

2000.00 .$ _$2000.00_ok

или даже так:

2,000.00 .$ _$2000.00_ok

Рекомендуем вам сохранить определение .$, которое пригодится нам в дальнейшем в некоторых примерах.

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

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

( n --) DUP >R ABS 0 <# #S R> SIGN #>

Ниже приводятся "стандартные" выражения, которые применяются для вывода различных видов чисел:

ВЫВОДИМОЕ ЧИСЛОСЛОВУ <# ПРЕДШЕСТВУЕТ ВЫРАЖЕНИЕ
32-разрядное без знака(ничего)
31-разрядное плюс знакDUP >R DABS (чтобы сохранить знак в третьем элементе стека для SIGN)
16-разрядное без знака0 (чтобы получить фиктивную, старшую по порядку, часть)
15-разрядное плюс знакDUP >R ABS 0 (чтобы сохранить знак)

ОПЕРАЦИИ НАД ЧИСЛАМИ ДВОЙНОЙ ДЛИНЫ

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

Ниже приводится перечень слов двойной длины для выполнении математических операций.

D+ ( d1 d2 -- d-сумма) Сложение двух 32-разрядных чисел
D- ( d1 d2 -- d-разность) Вычитание одного 32-разрядного числа ив другого (d1 - d2).
DNEGATE ( d -- -d) Изменение знака 32-разрядного числа на противоположный.
DABS ( d -- |d|) Занесение в стек абсолютного значения 32-разрядного числа.
DMAX ( d1 d2 -- d-max) Занесение в стек максимального из двух 32-разрядных чисел.
DMIN ( d1 d2 -- d-min) Занесение в стек минимального из двух 32-разрядных чисел.
D= ( d1 d2 -- ?) Занесение в стек истины в случае равенства d1 и d2.
D0= ( d -- ?) Занесение в стек истины, если d равно нулю.
D< ( d1 d2 -- ?) Занесение в стек истины, если d1 меньше d2.
DU< ( ud1 ud2 -- ?) Занесение в стек истины, если ud1 меньше ud2. Оба числа без знака.
D.R ( d ширина -- ) Вывод 32-разрядного числа со знаком. Число выравнивается справа внутри поля заданной ширины.

Буква D в начале каждого выражения означает, что указанная операция может выполняться только над числами двойной длины, а цифра 2 в начале слова, в частности 2SWAP и 2DUP ,- что данная операция может выполняться как над числами двойной длины, так и над парами чисел одинарной длины. Пример выполнения операции D+ :

200000. 300000. D+ D. _500000_ok

ОПЕРАЦИИ НАД ЧИСЛАМИ РАЗЛИЧНОЙ ДЛИНЫ

В следующей таблице перечислены часто используемые слова Форта, которые выполняются над комбинацией чисел одинарной и двойной длины [См. сноску в разд. "Операции деления" (гл.2).]:

UM* ( ul u2 -- ud) Перемножение двух 16-разрядных чисел. Все значения без знака. (В предыдущих версиях данная операция называлась U* )
UM/MOD ( ud u1 -- u2 u3) Деление 32-разрядного числа на 16-разрядное. В стек заносятся 16-разрядные остаток и частное (частное в вершину). Частное округлено до ближайшего меньшего целого! Все значения без знака. (В предыдущих версиях данная операция называлась U/MOD).
M* ( n1 n2 -- d-произведение) Перемножение двух 16-разрядных чисел. Все значения со знаком.
М+ ( d n -- d-сумма) Сложение 32-разрядного и 16-разрядного чисел. Результат 32-разрядный.
М/ ( d n -- n-частное) Деление 32-разрядного числа на 16-разрядное. Результат 16-разрядный. Все значения со знаком.
М*/ ( d n u -- d) Умножение 32-разрядного на 16-разрядное и деление промежуточного результата тройной длины на 16-разрядное число (d * n / u). Результат 32-разрядный.

Слово UM* является командой быстрого умножения, где под результат отводится 32 разряда. Эта команда может быть использована как базовая при определении других операций умножения. Аналогичным образом все команды деления могут быть определены через базовую операцию UM/MOD.

Ниже приводится пример выполнения операции М+:

200,000 7 М+ D. _200007_ok

С помощью операции М*/ мы можем переопределить наш прежний вариант слова % так, что оно будет выполняться с аргументом двойной длины:

: % ( d n% -- d) 100 M*/ ;

Например:

200.50 15 % D. _3007_ok

Если вы загрузили определение .$ (которое мы приводили ранее), то можете ввести следующий текст:

200.50 15 % .$ _$30.07_ok

Можно переопределить данное выше определение R% таким образом, чтобы получать округленный результат двойной длины

: R% ( d n% -- d) 10 M*/ 5 M+ 1 10 M*/ ;

и тогда

200.50 15 R% .$ _$30.08_ok

Заметим, что М*/ является единственным "штатным" словом Форта, которое осуществляет умножение аргумента двойной длины. Для того чтобы умножить, скажем, 200000 на 3, мы должны подставить единицу в качестве фиктивного делителя:

200,000 3 1 M*/ D. _600000_ok

так как 3/1 эквивалентно 3.

M*/ также является единственным словом Форта, которое осуществляет деление с результатом двойной длины. Так, при делении 200000 на 4 мы должны подставить единицу в качестве фиктивного числителя:

200,000 1 4 M*/ D. _50000_ok

ИСПОЛЬЗОВАНИЕ ЧИСЕЛ В ОПРЕДЕЛЕНИЯХ

Если в определении содержится число, например,

: БОЛЬШЕ-НА-ДВАДЦАТЬ ( n -- n+20) 20 + ;

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b09610

Двоичное значение числа зависит от системы счисления, которая существует в системе на момент КОМПИЛЯЦИИ данного определения. Например, при вводе

HEX : БОЛЬШЕ-НА-ДВАДЦАТЬ ( n -- n+20) 14 + ; DECIMAL

словарное определение должно содержать шестнадцатиричное значение 14, что соответствует десятичному значению 20 (16 + 4). Впредь слово БОЛЬШЕ-НА-ДВАДЦАТЬ будет всегда добавлять к содержимому стека эквивалент десятичного числа 20, независимо от текущей системы счисления. В том случае, когда вы поместите слово HEX ВНУТРЬ определения, основание системы счисления будет изменяться при ВЫПОЛНЕНИИ данного определения. Например, если вы определяете:

DECIMAL
: ПРИМЕР HEX 20 . DECIMAL ;

то число компилируется как двоичный эквивалент десятичного числа 20, поскольку во время компиляции текущей была DECIMAL (десятичная) система счисления. ВО ВРЕМЯ ВЫПОЛНЕНИЯ произойдет следующее:

ПРИМЕР _14_ok

Наше число выдается в шестнадцатиричной системе. Заметьте, что число, помещенное внутри некоторого определения, называется ЛИТЕРАЛОМ. (В отличие от слов, присутствующих в этом определении, которые ссылаются на другие определения, значение числа заключено в самом числе).
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Вс Апр 18, 2021 1:01 am

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

U. ( u --) Вывод числа одинарной длины без знака с одним пробелом после него.
U.R ( u ширина --) Вывод числа без знака. Число выровнено по правой границе поля заданной ширины.
U< ( u1 u2 -- ?) Помещение на стек истины в том случае, если u1<u2. Оба аргумента рассматриваются как числа одинарной длины без знака. Стековая нотация для форматирования чисел:

ВЫРАЖЕНИЕСОСТОЯНИЕ СТЕКАТИП АРГУМЕНТОВ
<# ... #>( d -- а u)32-разрядный без знака
( u 0 - а и)16-разрядный без знака
<# ... n SIGN #>( |d| -- а u)32-разрядный со знаком, где |d| является абсолютным значением d, a n - верхней ячейкой d
( |n| 0 -- а u)16-разрядный со знаком, где |n| - абсолютное значение n

Операции над числами двойной длины:

D+ ( d1 d2 -- d-сумма) Сложение двух 32-разрядных чисел.
D- ( d1 d2 -- d-разность) Вычитание одного 32-разрядного числа ив другого (d1 - d2).
DNEGATE ( d -- -d) Изменение знака 32-разрядного числа на противоположный.
DABS ( d1 -- |d|) Занесение в стек абсолютного значения 32-разрядного числа.
DMAX ( d1 d2 -- d-max) Занесение в стек максимального из двух 32-разрядных чисел.
DMIN ( d1 d2 -- d-min) Занесение в стек минимального из двух 32-разрядных чисел.
D= ( d1 d2 -- ?) Занесение в стек истины в случае равенства d1 и d2.
D0= ( d -- ?) Занесение в стек истины, если d равно нулю.
D< ( d1 d2 -- ?) Занесение в стек истины, если d1 меньше d2.
DU< ( ud1 ud2 -- ?) Занесение в стек истины, если ud1 меньше ud2. Оба числа без знака.
D.R ( d ширина --) Вывод 32-разрядного числа со знаком. Число выравнивается справа внутри поля заданной ширины.

Смешанные операции:

UM* ( ul u2 -- ud) Перемножение двух 16-разрядных чисел. Все значения без знака. (В предыдущих версиях данная операция называлась U* ).
UM/MOD ( ud u1 -- u2 u3) Деление 32-разрядного числа на 16-разрядное. В стек заносятся 16-разрядные остаток и частное ( честное в вершину). Частное округлено до ближайшего меньшего целого! Все значения без знака. (В предыдущих версиях данная операция называлась U/MOD).
M* ( n1 n2 -- d-произведение) Перемножение двух 16-разрядных чисел. Все значения со знаком.
M+ ( d n -- d-сумма) Сложение 32-разрядного и 16-раэрядного чисел. Результат 32-разрядный.
M/ ( d n -- n-частное) Деление 32-разрядного числа на 16-разрядное. Результат 16-разрядный. Все значения со знаком.
M*/ ( d n u -- d) Умножение 32-разрядного на 16-разрядное и деление промежуточного результата тройной длины на 16-разрядное число (d * n / u). Результат 32-разрядный.

Условные обозначения:
n, n1 ... - 16-разрядные числа со знаком;
а - адрес;
d, d1 ... - 32-разрядные числа со знаком;
u, u1 ... - 16-разрядные числа без знака;
с - значение символа в коде ASCII.

ОСНОВНЫЕ ТЕРМИНЫ

АРИФМЕТИЧЕСКИЙ СДВИГ ВЛЕВО И ВПРАВО -- Процесс сдвига всех разрядов числа, за исключением знакового, влево или вправо для эффективного выполнения операций деления и умножения на степень числа 2 с сохранением знака.
ASCII -- Стандартизованная система представления вводимых или выводимых символов в виде побайтных значений. Полное название - стандартный американский код для обмена информацией.
БАЙТ -- Стандартный термин для обозначения 8-разрядного значения.
ВОСЬМИРИЧНАЯ СИСТЕМА -- Система счисления по основанию 8.
ДВОИЧНАЯ СИСТЕМА -- Система счисления по основанию 2.
ДЕСЯТИЧНАЯ СИСТЕМА -- Система счисления по основанию 10.
ДОПОЛНЕНИЕ -- Число, равное исходному по абсолютной величине, но с противоположным знаком. Для того чтобы вычислить разность 10 - 4, компьютер сначала определит дополнение 4 (т.е. -4), а затем произведет сложение 10 + (-4).
ЗНАКОВЫЙ РАЗРЯД -- Разряд, который для чисел со знаком указывает, является ли данное число положительным или отрицательным, а для чисел без знака является старшим значащим разрядом.
ЛИТЕРАЛ -- Число или символ, значение которого заключено в нем самом; в Форте - это число, появляющееся внутри определения.
МАСКА -- Значение, которое может быть "наложено" на другое значение, в результате чего определенные разряды маскируются, а высвечиваются только интересующие нас разряды.
СЛОВО -- В Форте - это определенный элемент словаря, во всех других случаях - это термин для обозначения 16-разрядного значения.
ФОРМАТИЗАЦИЯ ЧИСЕЛ -- Процесс представления двоичного числа выводимыми на дисплей символами, как правило, в специальной форме, например: 3/13/81 или $47.93.
ЧИСЛО БЕЗ ЗНАКА -- Число, которое по определению положительное.
ЧИСЛО ОДИНАРНОЙ ДЛИНЫ БЕЗ ЗНАКА -- Целое, значение которого может лежать в диапазоне от 0 до 65535.
ШЕСТНАДЦАТИРИЧНАЯ СИСТЕМА -- Система счисления по основанию 16.
ЯЧЕЙКА -- Термин Форта для 16-разрядного значения.

УПРАЖНЕНИЯ ДЛЯ НАЧИНАЮЩИХ

1. Вероника Вэйнрайт не могла вспомнить верхнее значение для чисел одинарной длины со знаком. У нее не было книги, по которой она могла бы справиться, а была только Форт-система, поэтому она написала определение N-MAX с использованием цикла BEGIN ... UNTIL. После выполнения этого определения у нее получилось следующее:

_32767_ok

Восстановите написанное ею определение.

2. (Данное упражнение позволит вам приобрести навыки работы с битами). Прежде всего, если вы не сделали этого ранее, определите слово BINARY.

а) Разряды внутри 16-разрядной ячейки нумеруются с 0 до 15 справа налево, так что нулевым разрядом является самый младший бит, а 15-м - самый старший.
Определите слово с именем БИТ, которое переводило бы номер разряда (от 0 до 15) в маску, соответствующую этому разряду, например 0 бит соответствует маске 1, бит 1 - маске 2, бит 3 - маске 4 и т.д. (Совет: проще всего использовать цикл DO).

б) Пусть в стеке находится значение, представляющее некоторый массив из 16 разрядов (назовем его "битовый1"). Определите слово с именем УСТАНОВИТЬ-БИТ, которое устанавливало бы в единицу заданный бит в массиве "битовый1" при следующей стековой нотации: (битовый1 номер-бита -- битовый2). Например, если в стеке находится битовый шаблон, равный двоичному числу 1000, и вы выполняете последовательность команд 1 УСТАНОВИТЬ-БИТ (в десятичной системе), то получите в результате 1010.

в) Определите слово ОЧИСТИТЬ-БИТ, которое сбрасывает заданный бит. Стековая нотация та же, что и для слова УСТАНОВИТЬ-БИТ. Например:

BINARY 11111111
DECIMAL 5 ОЧИСТИТЬ-БИТ 7 ОЧИСТИТЬ-БИТ
BINARY U. _101011111_ok

г) Определите слово ДАЙ-БИТ, вносящее в стек указанный бит (выбранный посредством маски из массива "битовый"), который мог бы служить аргументом оператору IF (т.е. если бит установлен в единицу, то в стек помещается значение "истина", если нет,- "ложь"). Стековая нотация выглядит следующим образом: (битовый номер-бита -- бит). Например:

BINARY 1001 DECIMAL
DUP 0 ДАЙ-БИТ . _1_ok
DUP 1 ДАЙ-БИТ . _0_ok
3 ДАЙ-БИТ . _8_ok

д) Определите слово с именем ПЕРЕКЛЮЧИТЬ-БИТ с той же стековой нотацией, что и для слов УСТАНОВИТЬ-БИТ и ОЧИСТИТЬ-БИТ, которое переключало бы заданный бит (устанавливало бы его в единицу, если бы он был сброшен, и наоборот).

е) Пусть заданы две битовые последовательности, причем вторая получена путем изменения значений нескольких битов первой. Определите слово с именем ИЗМЕНЕНИЕ, которое воспроизводило бы битовую маску с измененными битами.

3. Напишите определение, которое заставило бы зазвонить колокольчик на вашем терминале три раза. Убедитесь в том, что перерыв в звучании достаточен для того, чтобы отдельные звонки не слились в один длинный звонок. Всякий раз при звучании колокольчика на экране терминала должно появляться слово БИП.
Упражнения 7.4 и 7.5 посвящены выполнению операций над числами двойной длины.

4. а) Перепишите созданные в гл.5 определения перевода значений температур из одной шкалы в другую в предположении, что вводимые в результирующие значения температур должны быть представлены целыми числами двойной длины со знаком с коэффициентом масштабирования 10 (т.е. должны быть умножены на 10). Например, если вы вводите фактическое значение 10.5град., то оно будет представлено 32-разрядным целым числом со значением 105.

б) Напишите слово для форматного вывода с именем .ГРАДУСЫ, которое будет выдавать 32-разрядное целое с коэффициентом масштабирования 10 как строку цифр с десятичной точкой и одной дробной цифрой. Например:

12.3 .ГРАДУСЫ<return>_12.3_ok

в) Преобразуйте следующие значения температур:

0.0 град.F в град.С
212.0 град.F в град.С
20.5 град.F в град.С
16.0 град.С в град.F
-40.0 град.С в град.F
100.0 град.К в град.С
100.0 град.К в град.F
233.0 град.К в град.С
233.0 град.К в град.F

5. а) Напишите программу, которая вычисляет значение полинома 7х2 + 20х + 5 при заданном х и выводит результат в виде числа двойной длины.

б) Какое максимальное значение может принимать х, чтобы при вычислении результата как 32-разрядного числа со знаком не произошло переполнения?

УПРАЖНЕНИЯ ДЛЯ ВСЕХ

6. Неопытный пользователь экспериментирует с шестнадцатиричными числами и пытается вернуться снова к десятичной форме, вводя команду DEC. Несмотря на то что Форт-система выводит _ok, пользователю кажется, что перевод из одной системы счисления в другую не происходит. Что случилось?

7. Напишите слово, которое бы выводило числа от 0 до 16 (десятичных) в десятичной, шестнадцатиричной и двоичной системах счисления в три столбца, т.е.

DECIMAL 0 HEX 0 BINARY 0
DECIMAL 1 HEX 1 ВINARY 1
DECIMAL 2 HEX 2 BINARY 10
...
DECIMAL 16 HEX 10 BINARY 10000

8. Введите число 37 и дважды выполните команду . (точка). Объясните, что вы получили и почему. Введите число 65536. и дважды выполните команду . . Объясните результат. Попытайтесь ввести число 65538. (с десятичной точкой).

9. Если вы вводите

..<return>

(две точки, НЕ разделенные пробелом) и система отвечает вам _ok, что это означает?

10. Напишите определение для форматного вывода номера телефона, которое одновременно выводит через слэш номер района ТОГДА И ТОЛЬКО ТОГДА, КОГДА в этот номер включен номер района, т.е.

555-1234 .ТЕЛЕФОН _555-1234_ok
213/372-8493 .ТЕЛЕФОН _#213/372-8493_ok

(В некоторых системах вместо слэша и дефиса может быть выведена десятичная точка).

ЗАМЕЧАНИЕ

Еще одна развлекательно-завлекательная глава. Нужно не забыть, что все эти выкрутасы не имеют к собственно Forth почти никакого отношения... и что, при изменении технических характеристик компьютера, все эти расчеты "плывут".
Да и решение автора "не вникать в детали" привело к паре-другой неувязок, которые вылезут сразу, как только вы начнете программировать сами.
G.
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Пн Апр 19, 2021 12:25 am

ГЛАВА 8. ПЕРЕМЕННЫЕ, КОНСТАНТЫ И МАССИВЫ

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

ПЕРЕМЕННЫЕ (ОБЩИЕ СВЕДЕНИЯ)

Для начала приведем пример ситуации, когда вам понадобилось бы воспользоваться переменной, например для хранения даты [ДЛЯ НАЧИНАЮЩИХ. Предположим, ваш компьютер выдает банковские счета на протяжении дня, и каждый такой счет должен включать дату. Вам не хотелось бы хранить дату все время в стеке и, кроме того, дата не должна быть частью определения, иначе его пришлось бы переопределять каждый день. Вам нужна переменная]. В первую очередь создадим переменную (VARIABLE) с именем ДАТА [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ ФИГ-ФОРТ. Чтобы вариант VARIABLE вашей системы совпадал с описанным в книге, введите определение : VARIABLE 0 VARIABLE ; ]:

VARIABLE ДАТА

Если сегодня 12-е число, то мы вводим: 12 ДАТА ! , т.е. помещаем 12 в стек, затем указываем имя переменной и, наконец, выполняем слово ! (ЗАПОМНИ). Это выражение записывает число 12 в переменную ДАТА. Для обратного действия нужно ввести ДАТА @, иными словами, назвать переменную, а затем выполнить слово @ (ВЫБРАТЬ). Указанное выражение выбирает число 12 и помещает его в стек. Таким образом, выражение

ДАТА @ . _12_ok

выведет на печать дату.

На Форте это делается еще проще с помощью слова, определение которого приводится ниже:

: ? @ . ;

Поэтому вместо "ДАТА-выборка-точка" мы можем просто набрать

ДАТА ? _12_ok

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

13 ДАТА ! _ok
ДАТА ? _13_ok

Понятно, что можно определить и дополнительные переменные для месяца и года:

VARIABLE ДАТА VARIABLE МЕСЯЦ VARIABLE ГОД

а затем слово с именем !ДАТА (для "запоминания-даты"), например:

: !ДАТА ( месяц день год --) ГОД ! ДАТА ! МЕСЯЦ ! ;

и использовать его следующим образом:

7 31 88 !ДATA ok

После этого нужно определить слово с именем .ДАТА (для "вывода-даты"):

: .ДАТА МЕСЯЦ ? ДАТА ? ГОД ? ;

Ваша Форт-система уже имеет ряд определенных переменных, одна из которых - BASE. Она содержит основание текущей системы счисления. На самом деле определения систем счисления HEX, DECIMAL (и OCTAL, если в вашей системе таковая имеется) весьма просты:

: DECIMAL 10 BASE ! ;
: HEX 16 BASE ! ;
: OCTAL 8 BASE ! ;

Вы можете работать с любой системой счисления, просто загрузив ее основание в BASE [ДЛЯ СПЕЦИАЛИСТОВ. Трехбуквенный код, например, имя терминала некоего аэропорта, можно запомнить как число одинарной длины без знака в системе счисления с основанием 36. Например:

: АЛЬФА 36 BASE ! ;
АЛЬФА
ZAP U. _ZAP_ok

].

Если где-нибудь в определении системных слов, осуществляющих преобразования входных и выходных чисел, вы найдете выражение BASE @ значит, текущее значение BASE используется в процессе такого преобразования. Следовательно, одна и та же программа может работать с числами в любой системе счисления. Отсюда мы можем сделать следующее формальное заключение об использовании переменных: в Форте уместно создавать переменные для тех значений, используемых внутри определения, которые могут быть изменены в любой момент после того, как это определение уже скомпилировано.

БОЛЕЕ ПОДРОБНО О ПЕРЕМЕННЫХ

Создавая некоторую переменную, например ДАТА, с помощью выражения VARIABLE ДАТА, вы фактически компилируете новое слово с именем ДАТА в словарь. Упрощенно это выглядит так [ДЛЯ СПЕЦИАЛИСТОВ. Как на самом деле выглядит в памяти элемент словаря, мы покажем в следующей главе]:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b10110

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

Когда вы вводите

12 ДАТА !

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b10910

число 12 поступает в стек, после чего интерпретатор текста ищет слово ДАТА в словаре и, найдя его, передает на исполнение EXECUTE.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b11010

EXECUTE выполняет переменную путем копирования адреса "пустой" ячейки этой переменной (куда будет послано значение) в стек [ДЛЯ НАЧИНАЮЩИХ. В программировании адресом называется число, которое определяет участок машинной памяти. Например, по адресу 2076 (адреса, как правило, выражаются шестнадцатиричными числами без знака) может содержаться 16-разрядное представление значения 12. Здесь 2076 - адрес, 12 - содержимое]:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b11110

Слово ! выбирает адрес (из вершины) и значение (под ним) и запоминает это значение по выбранному адресу. Новое число замещает любое другое число, находившееся прежде по данному адресу.

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

Для слова @ требуется только один аргумент: адрес, который в данном случае обеспечивается именем переменной, например ДАТА @ .

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b11210

Используя выбранное из стека значение в качестве адреса, слово @ помещает содержимое, находящееся по данному адресу, в стек, предварительно сняв с последнего адрес. (Содержимое участка памяти остается прежним).

ПЕРЕМЕННЫЕ В КАЧЕСТВЕ СЧЕТЧИКА

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

Сначала мы определяем

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b11310

в которой будем вести подсчет. Каждое утро мы будем начинать подсчет с нуля, поэтому нам придется загружать в переменную ЯЙЦА нуль, используя слово, определение которого выглядит так:

: УСТАНОВИТЬ 0 ЯЙЦА ! ;

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

1 ЯЙЦА +!

Слово +! [ДЛЯ ЛЮБОЗНАТЕЛЬНЫХ. Это слово обычно определяют на уровне языка Ассемблера, определение же на языке высокого уровня имеет вид:

: +! ( приращение а --) DUP @ ROT + SWAP ! ;

] добавляет заданное значение к содержимому (любому) по данному адресу. Таким образом, выражение

1 ЯЙЦА + !

увеличивает счетчик яиц на единицу. Для иллюстрации изложенного поместим это выражение внутрь некоторого определения:

: ЯЙЦО 1 ЯЙЦА +! ;

А в конце дня выясним, сколько яиц прошло через конвейер, набрав на клавиатуре ЯЙЦА ? . Теперь проверим:

УСТАНОВИТЬ _ok
ЯЙЦО _ok
ЯЙЦО _ok
ЯЙЦО _ok
ЯЙЦА ? _3_ok

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

VARIABLE xxx ( --) Создание переменной с именем xxx.
xxx ( -- а) Слово xxx при выполнении помещает в стек свой адрес.
! ( n а --) Запоминание числа одинарной длины по заданному адресу.
@( a -- n) Замещение адреса его содержимым.
? ( а --) Вывод значения по заданному адресу с последующим пробелом.
+! ( n а --) Сложение числа одинарной длины с содержимым заданного адреса.

КОНСТАНТЫ

Если в переменных обычно хранятся значения, которые могут изменяться, то константы используются для хранения значений, которые изменению НЕ ПОДЛЕЖАТ. На Форте мы создаем константу и тут же устанавливаем ее значение, например:

220 CONSTANT ПРЕДЕЛ

Здесь определена константа с именем ПРЕДЕЛ, которой присвоено значение 220. Теперь мы имеем право подставлять слово ПРЕДЕЛ вместо значения

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b11410

: ?ЖАРКО ( температура --) ПРЕДЕЛ > IF ." Опасно - Уменьшите нагрев ! " THEN ;

В том случае, когда число в стеке больше 220, выдается предупреждающее сообщение. Заметьте, что, говоря ПРЕДЕЛ, мы получаем ЗНАЧЕНИЕ, а не адрес. Нам здесь не требуется "выборка". В этом и состоит основное отличие переменных от констант. Дело в том, что при работе с переменной нам нужен адрес, чтобы иметь возможность как выборки значения, так и его запоминания. При использовании же константы всегда требуется значение (мы никогда в нее ничего не запоминаем).

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

: СНИМОК ЗАТВОР ОТКРЫТЬ ВРЕМЯ ВЫДЕРЖАТЬ ЗАТВОР ЗАКРЫТЬ ;

Здесь слово ЗАТВОР определено как константа при условии, что его выполнение обеспечивает аппаратный адрес затвора фотокамеры. Оно может быть определено так:

HEX
3E27 CONSTANT ЗАТВОР
DECIMALE

Слова ОТКРЫТЬ и ЗАКРЫТЬ могут быть просто определены:

: ОТКРЫТЬ ( а --) 1 SWAP ! ;
: ЗАКРЫТЬ ( а --) 0 SWAP ! ;

так что выражение ЗАТВОР ОТКРЫТЬ запишет единицу по адресу затвора, и он откроется.

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

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

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

0 CONSTANT 0
1 CONSTANT 1
и т.д.

В дальнейшем будем считать, что в вашей системе имеются следующие определения констант FALSE (ЛОЖЬ) и TRUE (ИСТИНА):

0 CONSTANT FALSE
-1 CONSTANT TRUE

CONSTANT xxx ( n --) Создание константы с именем xxx и значением n.
xxx ( -- n) Слово xxx при своем выполнении заносит в стек n.
FALSE ( -- f) Занесение в стек логического значения ложь (0).
TRUE ( -- t) Занесение в стек логического значения истина (-1).

ПЕРЕМЕННЫЕ И КОНСТАНТЫ ДВОЙНОЙ ДЛИНЫ
Вы можете определить переменную двойной длины с помощью слова 2VARIABLE, например:

2VARIABLE ДАТА

После этого вы можете использовать слова ФОРТА 2! (ЗАПОМНИ ДВА) и 2@ (ВЫБЕРИ ДВА) для доступа к переменной двойной длины. Можно записать число двойной длины в такую переменную, просто записав

800000. ДАТА 2!

и выбрать значение из нее, введя

ДАТА 2@ D. _800000_ok

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

7/16/86 ДАТА 2!

и выбрать их назад:

ДАТА 2@ .ДАТА _7/16/81_ok

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

200000. 2CONSTANT ЯБЛОКИ

после чего слово ЯБЛОКИ будет помещать в стек число двойной длины:

ЯБЛОКИ D. _200000_ok

Судя по префиксу 2, мы можем также использовать слово 2CONSTANT при определении ПАРЫ чисел одинарной длины. Можно помещать два числа под одним именем просто из соображений удобства, а также для резервирования памяти в словаре.

Вспомните (см. гл.5), что вы можете с помощью выражения 355 113 */ умножить некоторое число на аппроксимацию PI. Слово 2CONSTANT позволяет запомнить эти два целых:

355 113 2CONSTANT PI

а впоследствии применить выражение PI */ , например:

10000 PI */ . _31415_ok

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

2VARIABLE xxx ( --) Создание переменной двойной длины с именем xxx.
xxx ( -- a) Слово xxx при выполнении помещает на стек свой адрес.
2CONSTANT xxx ( d --) Создает константу двойной длины с именем xxx и значением d.
xxx ( -- d) Слово xxx при выполнении помещает в стек значение d.
2! ( d а --) Запоминание числа двойной длины по заданному адресу.
2@ ( а -- d) Занесение в стек числа двойной длины, расположенного по заданному адресу.

МАССИВЫ

Как вам уже известно, выражение VARIABLE ДАТА создает определение и выделяет память для значения одинарной длины:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b11510

А если необходимо выделить память для 10 или 20 значений одинарной длины под одним и тем же именем? Такая структура называется МАССИВОМ. На форте массив строится следующим образом.

В первую очередь мы создаем массив с помощью непривычного определяющего слова CREATE (СОЗДАТЬ) [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ ФИГ-ФОРТА. В вашей системе имеется слово CREATE, но оно отличается от упоминаемого здесь и редко используется. Чтобы вы могли и далее следить за ходом событий, переопределите его следующим образом: : CREATE <BUILD DOES> ; ]:

CREATE МОЙ-МАССИВ

Как и VARIABLE, CREATE компилирует в словарь новое имя (вашего массива) вместе с кодами, которые специфицируют его действия. Но при этом память под данные НЕ ВЫДЕЛЯЕТСЯ.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b11610

Каким образом мы впоследствии отведем память под созданный массив? Это делается посредством слова ALLOT (ВЫДЕЛИТЬ), которое выбирает из стека в качестве аргумента число БАЙТОВ, резервируемое для массива.

Если требуется выделить память под 10 значений одинарной длины, то нужно ввести

20 ALLOT

(Значение одинарной длины занимает два байта).

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b10210

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b10310

Мы можем с помощью слова ?ЖАРКО проверять, не превышает ли температура нагрева каждой из пяти горелок установленного для нее максимального значения, если определим ПРЕДЕЛ не как константу, а как массив. Присвоим этому массиву имя ПРЕДЕЛЫ:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b10410

Допустим, что мы устанавливаем предельное значение температуры для горелки 0 220o. Запомним это значение посредством следующей фразы:

220 ПРЕДЕЛЫ !

так как ПРЕДЕЛЫ доставляет адрес первой ячейки нашего массива. Установим предельное значение температуры для горелки 1 равным 340o и запомним его, добавив два байта к адресу исходной ячейки:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b10510

Мы можем запомнить предельные значения для горелок 2, 3 и 4, добавляя к исходному адресу "смещения" 4, 6 и 8. Так как смещение всегда равно удвоенному номеру горелки, определим полезное слово:

: ПРЕДЕЛ ( номер-горелки -- адрес-предельного-значения) 2* ПРЕДЕЛЫ + ;

чтобы по номеру соответствующей горелки, находящемуся в стеке, вычислять адрес, который отстоит от начала на величину соответствующего смещения [1. ДЛЯ НАЧИНАЮЩИХ. В этом случае номер горелки называется ИНДЕКСОМ массива. Индекс - это относительный указатель элемента памяти. Умножая индекс на 2, мы получаем СМЕЩЕНИЕ относительно начала массива. Смещение равно фактическому числу байтов между началом массива и искомым элементом.

2. Мы нумеруем горелки с 0 до 4, вместо того чтобы нумеровать их с 1 до 5 по той причине, что хотим использовать сами номера горелок в качестве индексов. То, что большинство людей называют "первым" в какой-то последовательности, программисты называют "нулевым". При желании вы можете пронумеровать горелки с 1 по 5, но тогда вам придется добавлять в определении слов самого высокого уровня корректирующий фрагмент (простое выражение "1-"), как это всегда делается].

После всех преобразований полезность слова ПРЕДЕЛ возросла в такой степени, что мы можем переопределить слово ?ЖАРКО:

: ?ЖАРКО ( температура номер-горелки --)
ПРЕДЕЛ @ > IF , " Опасно - Уменьшите нагрев! " THEN ;

Примеры выполнения нового варианта этого слова приведены ниже:

210 0 ?ЖАРКО _ok
230 0 ?ЖАРКО _Опасно_-_Уменьшите_нагрев!_ok
300 1 ?ЖАРКО _ok
350 1 ?ЖАРКО _Опасно_-_Уменьшите_нагрев!_ok
и т.д.

CREATE xxx ( --) Создание заголовка в словаре с именем xxx.
xxx ( -- a) Слово xxx при выполнении заносит в стек свой адрес.
ALLOT ( n -- ) Резервирование в поле параметров слова, определенного последним, n дополнительных байт.

ИСПОЛЬЗОВАНИЕ МАССИВА СЧЕТЧИКОВ

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

Вспомните, что в приведенном выше определении РАЗМЕР-ЯИЦ (см. гл.4) у нас было четыре категории стандартных яиц и две категории нестандартных.

0 БРАК
1 МЕЛКИЕ
2 СРЕДНИЕ
3 КРУПНЫЕ
4 ОЧЕНЬ КРУПНЫЕ
5 ОШИБКА

Давайте создадим массив из шести ячеек:

CREATE СЧЕТЧИКИ 12 ALLOT

Счетчики будут увеличиваться с помощью слова +! , так что мы должны иметь средства "обнуления" всех элементов массива перед началом процесса подсчета. Выражение

СЧЕТЧИКИ 12 0 FILL

заполняет нулями 12 байтов, начиная с адреса СЧЕТЧИКИ. Если в вашей Форт-системе имеется слово ERASE, то в данной ситуации лучше воспользоваться им. Это слово заполняет заданное число байтов нулями. Ниже показан пример его использования.

СЧЕТЧИКИ 12 ERASE

FILL (ЗАПОЛНИТЬ) ( a u b --) Заполнение n байтов памяти, начиная с заданного адреса, значением b.
ERASE (ОЧИСТИТЬ) ( а n --) Заполнение n байтов памяти, начиная с заданного адреса, нулями.

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

: УСТАНОВИТЬ СЧЕТЧИКИ 12 ERASE ;

Далее определим слово, которое по заданному номеру категории яиц (от 0 до 5) даст нам адрес одного из счетчиков, например:

: СЧЕТЧИК ( номер-категории -- а) 2* СЧЕТЧИКИ + ;

и еще одно слово для добавления единицы к счетчику с заданным номером:

: УЧЕТ ( номер-категории --) СЧЕТЧИК 1 SWAP +! ;

Здесь 1 служит приращением для слова +! , a SWAP располагает его аргументы в требуемом порядке, т.е. ( n адрес).

Теперь, например, выражение 3 УЧЕТ увеличит значение счетчика, соответствующего категории крупных яиц.

Определим слово, которое переводит вес на дюжину в номер категории [ДЛЯ СПЕЦИАЛИСТОВ. В конце главы будет приведено более простое определение]:

: КАТЕГОРИЯ ( вес-на-дюжину -- номер-категории)
DUP 18 < IF в ELSE
DUP 21 < IF I ELSE
DUP 24 < IF 2 ELSE
DUP 27 < IF 3 ELSE
DUP 30 < IF 4 ELSE
5
THEN THEN THEN THEN THEN SWAP DROP ;

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

Например, выражение 25 КАТЕГОРИЯ оставит в стеке число 3. Приведенное выше определение слова КАТЕГОРИЯ напоминает наше прежнее определение РАЗМЕР-ЯИЦ, но, следуя стилю Форта (слова должны создаваться по возможности более короткими), мы убрали из этого определения выдаваемые сообщения и определили еще одно слово, которое по заданному номеру сорта яиц выдает сообщение [ДЛЯ СПЕЦИАЛИСТОВ. Более элегантный вариант этого определения приводится в следующей главе]:

: МАРКИРОВКА ( номер-категории --)
DUP 0= IF ." Брак " ELSE
DUP 1 = IF ." Мелкие " ELSE
DUP 2 = IF ." Средние " ELSE
DUP 3 = IF ." Крупные " ELSE
DUP 4 = IF ." Очень крупные " ELSE
." Ошибка "
THEN THEN THEN THEN THEN BROP ;

Например:

1 МАРКИРОВКА _Мелкие_ok

Теперь мы можем определить слово РАЗМЕР-ЯИЦ, используя три наших собственных слова:

: РАЗМЕР-ЯИЦ ( вес-на-дюжину --) КАТЕГОРИЯ DUP МАРКИРОВКА УЧЕТ ;

Таким образом, выражение 23 РАЗМЕР-ЯИЦ выведет на вашем дисплее сообщение

_Средние_ok

и обновит счетчик яиц среднего размера.

Каким образом мы узнаем содержимое счетчиков в конце дня? Придется проверить по отдельности каждую ячейку массива, например, с помощью выражения 3 СЧЕТЧИК? (которое выведет число упакованных коробок с "крупными" яйцами). Однако можно попытаться для печати результирующей таблицы за день в приведенном ниже формате определить свое собственное слово:

_КОЛИЧЕСТВО________РАЗМЕР
______1____________Брак
____112____________Мелкие
____132____________Средние
____143____________Крупные
____159____________Очень_крупные
______0____________Ошибка

Так как вы уже научились получать номера категорий, можно просто использовать цикл DO с номером категории в качестве индекса:

: СВОДКА PAGE ." КОЛИЧЕСТВО РАЗМЕР" CR CR
6 0 DO I СЧЕТЧИК @ 5 U.R
7 SPACES I МАРКИРОВКА CR LOOP ;

(Выражение

I СЧЕТЧИК @ 5 U.R

выбирает номер категории, подготовленный словом I, как индекс массива и выводит содержимое соответствующего элемента последнего в виде поля из пяти значений).
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Пн Апр 19, 2021 12:47 am

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

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

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

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

Например, в следующем рецепте

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

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

ТОМАТ ДОБАВИТЬ
ГРИБЫ ДОБАВИТЬ

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

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

: РАЗМЕР-ЯИЦ ( вес-на-дюжину --) КАТЕГОРИЯ DUP МАРКИРОВКА УЧЕТ;

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

: МАРКИРОВКА ( номер-категории -- номер-категории) и т.д.
: РАЗМЕР-ЯИЦ ( в ее-на-дюжину --)
КАТЕГОРИЯ МАРКИРОВКА УЧЕТ ;

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

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

ОРГАНИЗАЦИЯ ЦИКЛА ПО МАССИВУ

Предлагаем вашему вниманию один из приемов работы с массивами. Лучше всего проиллюстрировать этот прием, написав на Форте наше собственное определение слова с именем DUMP. Оно применяется для вывода содержимого ряда адресов памяти. Форма использования этого слова следующая:

адрес длина DUMP

Например, мы можем ввести

СЧЕТЧИКИ 12 DUMP

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

Очевидно, что в рассматриваемом здесь примере слово DUMP содержит цикл DO. Вопрос заключается в том, что должно использоваться в качестве индекса. Хотя мы можем задействовать как индекс цикла непосредственно сам счетчик (0-6), все же для скорости лучше взять за индекс АДРЕС. Адрес массива СЧЕТЧИКИ будет начальным индексом для нашего цикла, а адрес плюс счетчик - верхней границей, например:

: DUMP ( а длина --)
OVER + SWAP DO CR I @ 5 U.R 2 +LOOP ;

Ключевым выражением здесь является фраза

OVER + SWAP

которая непосредственно предшествует DO.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b10610

Теперь начальный и конечный адреса находятся в стеке, готовые к использованию в качестве верхней границы и индекса цикла DO.

Выражение типа

OVER + SWAP

называется КЛИШЕ, поскольку оно выполняет некоторое единое действие с точки зрения более высокого уровня. Данное конкретное клише преобразует адрес и счетчик в форму аргументов оператора DO. Так как индексирование осуществляется по адресам, для того чтобы напечатать содержимое каждого элемента массива, поскольку мы находимся внутри цикла, достаточно просто ввести

I @ 5 U.R

Проверка байтов выполняется попарно (из-за того, что @ выбирает 16-разрядное значение), поэтому мы всякий раз увеличиваем индекс на ДВА с помощью выражения

2 +LOOP

МАССИВЫ БАЙТОВ

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b10710

Диапазон значений 8-разрядного числа без знака - от 0 до 255. Байтовые массивы могут также служить для хранения строк символов в коде ASCII. Преимущество массива байтов перед массивом ячеек заключается в том, что при его применении вы можете иметь тот же объем данных при половинном объеме памяти.

Механизм использования байтового массива тот же, что и массива ячеек, за исключением двух положений:
1) вы не должны удваивать смещение, так как каждый элемент соответствует одному адресу;
2) слова ! и @ нужно заменить словами С! и С@. Этим, словам, которые функционируют только с байтовыми значениями, дан префикс С , потому что обычно они обеспечивают доступ к символам в коде ASCII.

С! ( b a --) Занесение 8-разрядного числа по заданному адресу.
С@ ( а -- b) Выборка 8-разрядного числа по заданному адресу.

ИНИЦИАЛИЗАЦИЯ МАССИВА

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

Предположим, вы хотите, чтобы значения массива ПРЕДЕЛЫ были постоянными. Тогда вместо выражения

VARIABLE ПРЕДЕЛЫ 8 ALLOT

необходимо ввести

CREATE ПРЕДЕЛЫ 220 , 340 , 170 , 100 , 190 ,

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

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

Слово , берет число из стека и помещает его в массив. Поэтому всякий раз, когда вы записываете число и ставите за ним запятую, вы добавляете к массиву одну ячейку [ДЛЯ НАЧИНАЮЩИХ. Укоренившиеся привычки грамотно писать фразы на естественном языке приводят к тому, что некоторые новички забывают написать последнюю запятую в строке. Помните, что "," не разделяет числа, а компилирует их].

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b10810

Доступ к элементам массива, созданного с помощью CREATE, обеспечивается точно так же, как и к элементам массива, созданного посредством VARIABLE, например:

ПРЕДЕЛЫ 2+ ? _340_ok

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

Для того чтобы сформировать массив байтов, можно воспользоваться С, . Например, мы могли бы загрузить каждое из значений в нашем определении слова КАТЕГОРИЯ при сортировке яиц, следующим образом:

CREATE РАЗМЕРЫ 18 С, 21 С, 24 С, 27 С, 30 С, 255 С,

Это позволило бы переопределить слово КАТЕГОРИЯ посредством оператора цикла DO, а не рядом вложенных операторов IF ... THEN, в частности [ДЛЯ ТЕХ, КТО НЕ ЛЮБИТ ДОКАПЫВАТЬСЯ ДО СУТИ САМОСТОЯТЕЛЬНО. Суть дела состоит в следующем. Так как у нас пять возможных категорий, можно использовать номер категории как индекс для цикла. При выполнении каждого шага цикла мы сравниваем число из стека с элементом массива РАЗМЕРЫ, отстоящим от начала массива на смещение, равное текущему индексу цикла. Как только вес в стеке превысит значение очередного элемента массива, мы завершим цикл и с помощью I определим, сколько раз цикл выполнялся до того, как мы вышли из него. Поскольку это число представляет собой смещение в нашем массиве, то оно также является и номером категории]:

: КАТЕГОРИЯ ( вес-на-дюжину -- номер-категории)
6 0 DO DUP РАЗМЕРЫ I + С@
< IF DROP I LEAVE THEN LOOP ;

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

Этот новый вариант более изящен и более компактен по сравнению с прежним.

СЛОВА
Ниже приводится перечень слов Форта, с которыми вы познакомились в данной главе.

VARIABLE xxx ( --) Создание переменной с именем xxx.
xxx ( --а) Слово xxx при выполнении помещает в стек свой адрес.
! ( n а --) Запоминание числа одинарной длины по заданному адресу.
@ ( a -- n) Замещение адреса его содержимым.
? ( а --) Вывод значения по заданному адресу с последующим пробелом.
+! ( n а --) Сложение числа одинарной длины с содержимым заданного адреса.
CREATE xxx ( --) Создание заголовка в словаре с именем xxx.
xxx ( -- a) Слово xxx при выполнении заносит в стек свой адрес.
ALLOT ( n --) Резервирование в поле параметров слова, определенного последним, n дополнительных байт.
С! ( b a --) Занесение 8-разрядного числа по заданному адресу.
С@ ( а -- b) Выборка 8-разрядного числа по заданному адресу.
FILL (ЗАПОЛНИТЬ) ( a u b --) Заполнение n байтов памяти, начиная с заданного адреса, значением b.
ERASE (ОЧИСТИТЬ) ( а n --) Заполнение n байтов памяти, начиная с заданного адреса, нулями.
CONSTANT xxx ( n --) Создание константы с именем xxx и значением n.
xxx ( -- n) Слово xxx при своем выполнении заносит в стек n.
FALSE ( -- f) Занесение в стек логического значения ложь (0).
TRUE ( -- t) Занесение в стек логического значения истина (-1).

Операции двойной длины:

2VARIABLE xxx ( --) Создание переменной двойной длины с именем xxx.
xxx ( -- a) Слово xxx при выполнении помещает на стек свой адрес.
2CONSTANT xxx ( d --) Создает константу двойной длины с именем xxx и значением d.
xxx ( --d) Слово xxx при выполнении помещает в стек значение d.
2! ( d а --) Запоминание числа двойной длины по заданному адресу.
2@ ( а -- d) Занесение в стек числа двойной длины, расположенного по заданному адресу.

Условные обозначения:
n, n1 ... - 16-разрядные числа со знаком;
а - адрес;
d, d1 ... - 32-разрядные числа со знаком;
u, u1 ... - 16-разрядные числа без знака;

ОСНОВНЫЕ ТЕРМИНЫ

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

УПРАЖНЕНИЯ

1. а) Создайте два слова с именами ИСПЕКИ-ПИРОЖОК и СЪЕШЬ-ПИРОЖОК. Первое слово увеличивает число имеющихся пирожков на единицу, второе уменьшает эго число на единицу и благодарит вас за пирожок. Но если пирожков в наличии нет, оно выводит: "Какой пирожок?". (Начните в предположении, что число пирожков равно нулю).

СЪЕШЬ-ПИРОЖОК _Какой_пирожок?
ИСПЕКИ-ПИРОЖОК _ok
СЪЕШЬ-ПИРОЖОК _Спасибо!_ok

б) Напишите слово с именем ЗАМОРОЗЬ-ПИРОЖКИ, которое берет все имеющиеся в наличии пирожки и добавляет их к числу пирожков, хранящихся в морозильной камере. Помните, что замороженные пирожки есть нельзя.

ИСПЕКИ-ПИРОЖОК ИСПЕКИ-ПИРОЖОК ЗАМОРОЗЬ-ПИРОЖКИ _ok
ПИРОЖКИ ? _0_ok
ЗАМОРОЖЕННЫЕ-ПИРОЖКИ ? _2_ok

2. Определите слово с именем .BASE, которое выводит текущее значение переменной BASE в десятичной системе счисления. Проверьте это слово при первом же изменении переменной BASE на любом значении, отличном от 10. (Это не так просто, как может показаться).

DECIMAL .BASE _10_ok
HEX .BASE _16_ok

3. Определите слово для форматизации чисел с именем М. , которое выводит на печать число двойной длины с десятичной точкой. Положение десятичной точки внутри числа подвижно и зависит от значения некоторой переменной.
Если в вашей системе имеется для указания положения десятичной точки только что введенного числа переменная DPL или какая-либо другая, воспользуйтесь ею, например так:

2000.00 M. _2000.00_ok

В противном случае определите переменную с именем ДРОБНЫЕ и загружайте туда значения вручную:

2 ДРОБНЫЕ !
2000.00 M. _2000.00_ok

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

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

КРАСНЫЕ КАРАНДАШИ

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

23 КРАСНЫЕ КАРАНДАШИ
15 ГОЛУБЫЕ КАРАНДАШИ
12 ЗЕЛЕНЫЕ КАРАНДАШИ
0 ЖЕЛТЫЕ КАРАНДАШИ

5. Создайте массив значений и напечатайте гистограмму (см. упражнения к гл.5), где для каждого значения выводилась бы строка символов * . Для начала сформируйте массив из 10 элементов. Присвойте всем элементам массива значения в диапазоне от нуля до 10, после чего определите слово РИСУЙ, которое будет выводить строку, соответствующую каждому значению. На каждой строке должен выводиться номер элемента, а после него - число звездочек, равное содержимому данного элемента.

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

ЖЕНЩИНА СЕМЕЙНЫЙ РАБОТАЕТ ГОРОДСКОЙ

Теперь определите следующие константы, каждая из которых несет нули во всех битах

МУЖЧИНА ОДИНОКИЙ HE-РАБОТАЕТ НЕ-ГОРОДСКОЙ

Далее определите в виде переменных два имени, например:

VARIABLE ВАСЯ
VARIABLE МАША

Впоследствии в этих переменных будут содержаться атрибуты наших персонажей.

Затем определите слово с именем ОПИСАНИЯ, которое выбирает из стека значения четырех констант из восьми нами заведенных плюс адрес поля, отведенного для одного из наших персонажей:

ЖЕНЩИНА ОДИНОКИЙ РАБОТАЕТ ГОРОДСКОЙ МАША ОПИСАНИЯ

Слово ОПИСАНИЯ заносит в область, отведенную персонажу, битовый шаблон, соответствующий атрибутам персонажа.
Наконец, определите слово СВЕДЕНИЯ, которое выбирало бы из стека имя персонажа и по битовому шаблону последнего выводило бы сведения о нем, например:

МАША СВЕДЕНИЯ _ЖЕНЩИНА_ОДИНОКИЙ_РАБОТАЕТ_ГОРОДСКОЙ_ok

7. Создайте программу, которая высвечивала бы поле для игры в "крестики-нолики" и давала возможность двум игрокам задавать свои ходы с помощью клавиатуры. Так, выражение 4 X! помещает X в клетку 4 (счет клеток ведется с единицы) и выводит на экран картинку:

___:___:___
_X_:___:___
___:___:___

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

___:___:_0_
_X_:___:___
___:___:___

Для того чтобы помнить содержимое игрового поля, примените массив байтов, где значение 1 соответствовало бы X, значение -1 - 0, а 0 -- пустой клетке.

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

ЗАМЕЧАНИЕ

Очень важная глава! Суть не в том, что Forth, подобно "нормальным" языкам ИМЕЕТ переменные и массивы, а в том, что ПО МЕРЕ НАДОБНОСТИ такие и подобные, и совсем другие механизмы МОГУТ быть легко созданы.
По тексту тут и там разбросаны мысли, которые служат маяками фортеров уже долгие десятилетия.
G.
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Вт Апр 20, 2021 12:25 am

ГЛАВА 9. ФУНКЦИОНИРОВАНИЕ ФОРТ-СИСТЕМЫ

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

ПОИСК ПО СЛОВАРЮ

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b11710

Посмотрим, из каких компонентов состоит текстовый интерпретатор, начнем с изучения слов поиска по словарю. Слово ' (апостроф) находит определение в словаре и помещает в стек АДРЕС этого определения. Воспользуемся словом ВСТРЕЧА, которое мы определили в гл.1, и запишем:

' ВСТРЕЧА U. _25520_ok

В результате получим адрес слова ВСТРЕЧА (вот и все, что должно произойти). (На самом деле и INTERPRET, и апостроф используют для поиска по словарю примитив с именем FIND (ПОИСК)).

Слово ' имеет несколько применений. Так, с помощью выражения: ' ВСТРЕЧА , вы можете узнать, было ли слово ВСТРЕЧА определено, фактически не выполняя его (при выполнении этого выражения будет либо напечатан адрес, либо выдан ответ: "?").

Вам также может понадобиться адрес для того, чтобы вывести с помощью DUMP содержимое определения, например:

' ВСТРЕЧА 12 DUMP

Можно сочетать апостроф со словом EXECUTE. Вспомните, что текстовый интерпретатор, найдя слово, передает его адрес слову EXECUTE. To же самое можете делать и вы. Слово EXECUTE выполняет определение, адрес которого задается в стеке. Таким образом, вы можете ввести (следуя Стандарту-83):

' ВСТРЕЧА EXECUTE _Привет_я_говорю_на_Форте_ok

и получить тот же эффект, что и при выполнении одного слова ВСТРЕЧА, только более изощренным способом. Слово EXECUTE не проверяет, является ли заданный адрес правильным. Вся ответственность ложится на вас. Неверный адрес почти всегда приводит к разрушению системы. Стандартом-83 определено, что апостроф оставляет в вершине стека правильный адрес для EXECUTE. К сожалению, интерфейс между апострофом и EXECUTE менялся от диалекта к диалекту на протяжении многих лет.

В приведенной ниже таблице излагаются правила вычисления адреса слова для EXECUTE. В первом столбце даются примеры использования апострофа в режиме интерпретации (что было продемонстрировано выше). Например, на фиг-Форте вы можете ввести:

' ВСТРЕЧА CFA EXECUTE

Пояснения к остальным столбцам - более краткие.

Получение адреса для EXECUTE в режиме интерпретацииПолучение адреса для EXECUTE из входного потока (внутри определения)Получение адреса следующего слова внутри определения для передачи EXECUTEЧто помещает апостроф в стек
Фиг-Форт' имя CFA[COMPILE] ' CFA' имя CFApfa
MMS-Форт' имя 2 -[COMPILE] ' 2-' имя 2-pfa
Стандарт-79' имя CFA или FIND имя[COMPILE] ' CFA' имя CFApfa
полиФорт' имя'['] имяpfa
Стандарт-83' имя'['] имяcfa

ВЕКТОРНЫЕ ВЫЧИСЛЕНИЯ
[Как я уже отмечал при чтении книги Баранова и Ноздрунова, слово "векторные" здесь безграмотно. Никаких векторов, т.е. одномерных массивов тут не упоминается. "Косвенные вычисления", "через указатель", но никак не "векторные"!- G.]

Векторное вычисление, хотя и воспринимается как нечто трудоемкое, на самом деле осуществляется довольно просто. Вместо того чтобы выполнять какое-либо определение НЕПОСРЕДСТВЕННО (см. выражение ' ВСТРЕЧА EXECUTE), мы выполняем его КОСВЕННО, запоминая адрес определения в некоторой переменной, а затем выполняя содержимое этой переменной:

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

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

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

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

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

ФРАЗА _Привет_ok

Если вы альтернативно выполните выражение

' ДО-СВИДАНИЯ 'ФРАЗА !

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

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

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

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

Более общее применение векторных вычислений - изменение выполняемых действий некоторого слова уже ПОСЛЕ ТОГО, КАК ОНО СКОМПИЛИРОВАНО. Определения слов, осуществляющих интерфейс, таких, как слова управления видеодисплеем, принтером, дисководом, часто делают векторными. Векторизация позволяет хранить имена для перечисленных функций в предварительно скомпилированной части Форт-системы и в то же время организовать свои собственные варианты их исполнения. Поскольку Форт-система сама использует вектора, ваши изменения будут влиять и на нее. Вы можете заставить систему после вывода "ok" сделать возврат каретки на любом терминале или принтере, к которому есть доступ.

АПОСТРОФ В ОПРЕДЕЛЕНИИ

Согласно Стандарту-83 апостроф всегда пытается найти следующее слово во входном потоке. Что произойдет, если мы поместим апостроф внутрь какого-либо определения? При исполнении такого определения апостроф будет искать следующее слово из входного потока. Таким образом, мы можем определить:

: СКАЖИ ( имя ( --) ' 'ФРАЗА ! ;

(Если у вас иная система, обратитесь к столбцу 2 вышеприведенной таблицы). Необычный стековый комментарий означает, что слово СКАЖИ будет "заглядывать" вперед по входному потоку в поисках очередного слова.
Теперь можно ввести:

СКАЖИ ПРИВЕТ _ok
ФРАЗА _Привет_ok

или

СКАЖИ ДО-СВИДАНИЯ _ok
ФРАЗА _До_свидания_ok

Апостроф в слове СКАЖИ осуществляет поиск имени определенных слов ПРИВЕТ и ДО-СВИДАНИЯ во входном потоке во время ВЫПОЛНЕНИЯ слова СКАЖИ. Во время ОПРЕДЕЛЕНИЯ этого слова апостроф ничего не делает (разве что позволяет себя компилировать).

А как быть, если нужно специфицировать посредством апострофа адрес следующего слова в определении? Для этого имеется слово ['] , которое применяется вместо слова ' , например [Для пользователей небольших систем. Если на вашей клавиатуре нет клавиши "[" или "]", то в документации по Форт-системе должна быть указана замена ] :

: ПРИХОДЯ ['] ПРИВЕТ 'ФРАЗА ! ;
: УХОДЯ ['] ДО-СВИДАНИЯ 'ФРАЗА ! ;

Введите следующий текст:

ПРИХОДЯ _ok
ФРАЗА _Привет_ok
УХОДЯ _ok
ФРАЗА _До_свидания_ok

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

' xxx ( -- а) Осуществляется поиск в словаре адреса слова xxx (следующего слова во входном потоке).
['] период-компиляции: ( --) период-выполнения: ( -- а) Используется только в определении через двоеточие. Компиляция адреса следующего слова в определении как литерала.
EXECUTE ( а --) Исполнение элемента словаря, адрес поля параметров которого находится на стеке.
@EXECUTE ( а --) Исполнение элемента словаря, адрес которого является содержимым а. Если по адресу a находится нуль, @ЕХЕCUТЕ ничего не выполняет.

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

Все определения, независимо от того, были ли они специфицированы посредством : , VARIABLE, CREATE или любого другого ОПРЕДЕЛЯЮЩЕГО СЛОВА, состоят из следующих полей [ДЛЯ СИСТЕМ, ФУНКЦИОНИРУЮЩИХ НА ФОРТ-ПРОЦЕССОРАХ. Приводимая структура элементов словаря и техника вызова процедур относится к виртуальным Форт-системам, реализованным на обычных процессорах с применением косвенного шитого кода, а не к системам, где Форт-процессор реализован непосредственно в кремнии. (Но, изучив материал настоящей главы, вы скорее поймете, как работает Форт-машина)]:
- поля имени;
- поля связи;
- поля кода;
- поля параметров.

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b11810

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

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b12310

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b12410

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

Код, на который происходит ссылка, называется КОДОМ ПЕРИОДА ВЫПОЛНЕНИЯ, потому что он используется при выполнении слова данного вида (а не тогда, когда оно определяется или компилируется).

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

ПОЛЕ ПАРАМЕТРОВ. За полем указателя кода следует поле параметров. В случае переменных и констант поле параметров представляет собой только одну ячейку, в случае 2CONSTANT или 2VARIABLE - две, в случае массива - столько, сколько вы пожелаете, а в случае определения через двоеточие длина поля параметров зависит от длины данного определения, о чем и пойдет речь в следующем разделе.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b12510

АДРЕСАЦИЯ ПОЛЕЙ

При изучении полей, составляющих структуру словаря, важно понять различие между адресами этих полей и их СОДЕРЖИМЫМ. По соглашению адрес, по которому содержится указатель кода, называется АДРЕСОМ ПОЛЯ КОДА (cfa). Следовательно, cfa слов содержат указатель кода периода их выполнения.

Адрес первой ячейки, в которой хранится параметр, называется АДРЕСОМ ПОЛЯ ПАРАМЕТРОВ (pfa).

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b12610

Адрес, помещаемый в стек апострофом, в одних реализациях является cfa, в других - pfa. Стандарт предусматривает слово >BODY, осуществляющее переход от cfa к pfa.

Таким образом, имеется возможность (хотя это далеко не "стандарт" и не может быть рекомендовано к применению) изменять значение существующей константы, например:

n ' ПРЕДЕЛ >BODY !

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

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

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

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

Поле параметров определения через двоеточие содержит АДРЕСА уже определенных слов, составляющих данное определение [ДЛЯ СПЕЦИАЛИСТОВ. Адреса, составляющие тело определения через двоеточие, как правило, указывают поле кода, а не поле параметров (т.е. cfa, а не pfa)].

Ниже приводится элемент словаря для определения слова СНИМОК, которое выглядит следующим образом:

: СНИМОК ЗАТВОР ОТКРЫТЬ ВРЕМЯ ВЫДЕРЖАТЬ ЗАТВОР ЗАКРЫТЬ ;

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b12710

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

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

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

: ОБЕД ПЕРВОЕ ВТОРОЕ ДЕСЕРТ ;

причем сегодня на ВТОРОЕ подается только

: ВТОРОЕ ЦЫПЛЕНОК РИС ;

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b12810

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b12910

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b13010

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b13110

ЕЩЕ ОДИН ВАРИАНТ ИСПОЛЬЗОВАНИЯ СТЕКА ВОЗВРАТОВ

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

: ТЕСТ 3 >R CR CR CR ;

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

В большинстве Форт-систем во время выполнения цикла DO информация об индексе и границе хранится в стеке возвратов (и не всегда в том виде, в котором вы ожидаете).

Именно поэтому в пределах цикла DO LOOP использование операций >R и R> должно быть симметричным. Другими словами, вы имеете право писать так:

... >R ... DO ... LOOP ... R> ... ;

и не должны писать иначе, например:

... >R ... DO ... R> ... LOOP ... ;

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

... DO ... >R ... I ... R> ... LOOP ... ;

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

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b13210

В этом случае при выполнении 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 --) ГЛУБИНА-СТЕКА SWAP < ABORT" Значения?" ;

что может быть использовано так:

: LIST ( n --) 1 АРГУМЕНТЫ LIST ;
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Вт Апр 20, 2021 12:40 am

ПРОИЗВОЛЬНОЕ ИЗМЕНЕНИЕ ПОСЛЕДОВАТЕЛЬНОСТИ ВЫПОЛНЕНИЯ СЛОВ

Существует возможность опустить один уровень исполнения, просто удалив один адрес из стека возвратов. В качестве примера рассмотрим три уровня исполнения, связанных со словом ОБЕД:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b13310

Предположим, что мы изменили определение ВТОРОЕ:

: ВТОРОЕ ЦЫПЛЕНОК РИС R> DROP ;

Выражение "R> DROP" удалит из стека возвратов адрес возврата в слово ДЕСЕРТ, который был туда помещен перед выполнением слова ВТОРОЕ. Если перезагрузить эти определения и выполнить слово ОБЕД, то EXIT третьего уровня обеспечит возврат непосредственно на первый уровень. Мы "съедим" ПЕРВОЕ, ЦЫПЛЕНКА и РИС, но останемся без ДЕСЕРТА:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b13410

Применять в прикладной программе выражения "R> DROP" нежелательно, поскольку это противоречит принципам структурного программирования. Тем не менее данное выражение иногда позволяет упростить решение задачи. Мы не будем здесь приводить аргументы "за" и "против", однако изложенное должно послужить вам предостережением.

Как недавно упоминалось, слово EXIT удаляет адрес возврата из вершины стека возвратов и помещает его в указатель интерпретатора. Интерпретатор адреса, который ориентируется по содержимому указателя интерпретатора, начинает поиск на следующем верхнем уровне. Можно включить EXIT в середину определения. Например, если бы мы переопределили слово ВТОРОЕ:

: ВТОРОЕ РИС ВЕГЕТАРИАНЕЦ IF EXIT THEN ЦЫПЛЕНОК ;

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b13510

Приведенное выше определение эквивалентно следующему:

: ВТОРОЕ РИС ВЕГЕТАРИАНЕЦ NOT IF ЦЫПЛЕНОК THEN ;

Вы не имеете права использовать внутри оператора цикла DO слово EXIT, так как это слово удаляет из стека один из аргументов, занесенных в него оператором DO (вместо того, чтобы удалить из стека возвратов адрес возврата)!

Вы только что видели результат удаления адреса возврата из стека возвратов. Приведем еще один пример - занесение в стек возвратов лишнего адреса (вам, может быть, придется привести адреса cfa и pfa в соответствии с вашей системой):

: ПРИВЕТ ." Привет " ;
: ДО-СВИДАНИЯ ." До свидания " ;
' ДО-СВИДАНИЯ >BODY >R ПРИВЕТ

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

>BODY ( cfa -- pfa) Вычисление адреса поля параметров определения, "адрес компиляции" которого находится на стеке.
EXIT ( --) Удаление адреса возврата из вершины стека возвратов и занесение его в указатель адресного интерпретатора. Если слово EXIT скомпилировано в определении через двоеточие, то оно завершает выполнение этого определения в данной точке"
QUIT ( --) Очистка стека возвратов и передача управления терминалу, ожидающему ввода. Сообщения при этом не выдаются.
ABORT ( --) Очистка стека данных и выполнение функций слова QUIT. Сообщения не выдаются.

РЕКУРСИЯ

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

: LOAD ( n -- ) DUP . LOAD ;

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b13610

Тем не менее на Форте реализовать рекурсию довольно просто (нехитрые средства защиты Форта легко обмануть). Слово RECURSE осуществляет компиляцию адреса текущего определяемого слова. (В некоторых системах это слово известно как MYSELF) [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ ФИГ-ФОРТА. Определение следующее:

: RECURSE LATEST PFA CFA , ; IMMEDIATE

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

: RECURSE LAST @ @ 2+ , ; IMMEDIATE

]. Приведем пример рекурсии:

VARIABLE СЧЕТЧИК
: ПОЧЕМУ CR ." Почему вы спрашиваете? " -1 СЧЕТЧИК +!
СЧЕТЧИК @ IF RECURSE THEN ;
5 СЧЕТЧИК !

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

ПОЧЕМУ
_Почему_вы_спрашиваете?
_Почему_вы_спрашиваете?
_Почему_вы_спрашиваете?
_Почему_вы_спрашиваете?
_Почему_вы_спрашиваете?_ok

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

ГЕОГРАФИЯ ФОРТА

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b13710

Вы видите на рисунке КАРТУ ПАМЯТИ [ДЛЯ НАЧИНАЮЩИХ. Здесь показано, каким образом распределяется намять компьютера в конкретной системе в зависимости от ее назначения. Память разбита на участки по 1024 байта. Это число называется "К" (от слова "кило", означающего тысячу)] типичной Форт-системы для одного пользователя. Мультипрограммные системы, такие, как полиФорт, устроены намного сложнее, о чем пойдет речь позднее. А пока рассмотрим простой случай и последовательно изучим каждый район нашей карты.

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

В предварительно скомпилированном участке обычно хранится большая часть математических операций и слов форматизации чисел одинарной длины, операции преобразования стека одинарной длины, команды редактирования, структуры управления, ассемблер, все определяющие слова, которые вам уже известны, и, конечно, интерпретаторы текста и адреса [ДЛЯ СПЕЦИАЛИСТОВ. Чтобы получить представление о том, как компактен Форт, вам достаточно знать, что весь предварительно скомпилированный участок полифорта занимает меньше 8К байтов памяти].

СИСТЕМНЫЕ ПЕРЕМЕННЫЕ. Следующий раздел памяти содержит системные переменные, которые созданы предварительно скомпилированным ядром Форта и используются всей системой. Они, как правило, не применяются пользователем.

ВЫБОРОЧНЫЕ ОПРЕДЕЛЕНИЯ. Тот раздел Форт-системы, который не был предварительно скомпилирован, хранится на диске в виде исходного текста. Какую часть определений из этого раздела загружать, а какую - нет, вы можете решить сами, что улучшит управление использованием памяти вашего компьютера. Блок загрузки для всех выборочных определений называется БЛОКОМ ВЫБОРА.

СЛОВАРЬ ПОЛЬЗОВАТЕЛЯ. Словарь расширяется в сторону увеличения адресов памяти по мере того, как вы добавляете ваши собственные определения в область памяти, называемую СЛОВАРЕМ ПОЛЬЗОВАТЕЛЯ. Его следующая доступная ячейка в любой момент времени определяется содержимым переменной с именем H (или DP). Во время компиляции указатель H, по мере того как очередной элемент добавляется к словарю, переходит с ячейки на ячейку (или с байта на байт). Таким образом, для компилятора указатель H выступает в качестве закладки; он указывает то место в словаре, куда компилятор может компилировать следующий объект. Этот указатель также используется словом ALLOT, которое передвигает его на заданное число байтов. Например, выражение 10 ALLOT добавляет к нему 10 и, следовательно, компилятор зарезервирует память в словаре для массива, состоящего из 10 байтов (или пяти ячеек).

Родственным словом является и HERE, которое определяется весьма просто:

: HERE ( -- текущий-адрес ) H @ ;

Оно помещает значение H в стек. Слово , (запятая) помещает значение одинарной длины в следующую доступную ячейку словаря:

: , ( n -- ) HERE ! 2 ALLOT ;

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

HERE 220 LOAD HERE SWAP - . _196_ok

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

РАБОЧАЯ ОБЛАСТЬ (PAD). На некотором удалении от HERE вы обнаружите в своем словаре небольшую область памяти, называемую рабочей областью. Наша рабочая область обычно служит для хранения строк символов в коде ASCII, которые подвергаются обработке перед выводом на терминал. Так, слова, осуществляющие форматирование чисел, используют рабочую область для обработки чисел в коде ASCII во время их перевода, прежде чем вывести эти числа с помощью TYPE.

Размер рабочей области не определен.

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

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

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

: PAD ( -- a) HERE 34 + ;

т.е. помещает в стек адрес, который отстоит на фиксированное число байтов от HERE (в действительности это число может меняться).

СТЕК ДАННЫХ. Намного выше рабочей области расположен участок, зарезервированный под стек данных. У вас может создаться впечатление, что значения где-то передвигаются вверх и вниз, как будто их кто-то "вталкивает" и "выталкивает", однако на самом деле ничего подобного не происходит. Единственное, что изменяется,- это указатель вершины стека.

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b13810

Но мере добавления в стек новых значений он "растет" в направлении младших адресов памяти.

Указатель стека выбирается посредством слова SP@ [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ ПОЛИФОРТА. Это слово имеет имя 'S ]. Так как это слово доставляет адрес самого верхнего участка стека, выражение SP@ @ выбирает содержимое вершины стека. Такая операция, конечно, идентична операции DUP. Если у вас в стеке находится пять значений, то пятое значение можно скопировать с помощью выражения:

SP@ 8 + @

(но в большинстве случаев это не может считаться хорошим стилем программирования).

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b13910

Заметим, что при размещении чисел двойной длины как в стеке, так и в словаре старшая по порядку ячейка размещается по младшему адресу. При выполнении операций 2! и 2@ (см. рисунок) этот порядок размещения ячеек сохраняется.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b11910

БУФЕР ВХОДНОГО ТЕКСТА. Буфер входного текста представляет собой область памяти длиной, как правило, 80 байт, куда поступают вводимые с клавиатуры символы после нажатия клавиши "возврат каретки". Именно здесь они будут просмотрены текстовым интерпретатором.

Не путайте этот термин с термином ВХОДНОЙ ПОТОК (см. гл.3), который означает последовательность слов, подлежащих интерпретации. Данная последовательность может быть расположена либо в буфере входного текста (в режиме интерпретации), либо в блоке, содержащем исходный текст (в режиме загрузки).

Буфер входного текста увеличивается в направлении старших адресов памяти (в том же направлении, что и рабочая область (PAD)). Слово TIB выбирает начальный адрес буфера. [На фиг-Форте вы можете ввести "TIB @", на полиФорте - "S0 @"].

СТЕК ВОЗВРАТОВ. Выше буфера входного текста расположен стек возвратов, функционирование которого идентично функционированию стека данных.

ПОЛЬЗОВАТЕЛЬСКИЕ ПЕРЕМЕННЫЕ. Следующий раздел памяти содержит пользовательские переменные. Эти переменные включают в себя слова BASE, S0 и многие другие, которые мы рассмотрели ниже.

БЛОЧНЫЕ БУФЕРЫ. В самой верхней области памяти расположены буферы блоков. Каждый буфер имеет объем 1024 байта для размещения содержимого дискового блока. Всякий раз, когда вы осуществляете доступ к какому-либо блоку (например, распечатывая или загружая его), система копирует данный блок с диска в такой буфер, где он может изменяться с помощью редактора или интерпретироваться посредством слова LOAD. Более подробно мы обсудим блочные буферы в гл.10.

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

H или DP ( -- а) Занесение в стек адреса указателя словаря.
HERE ( -- а) Занесение в стек адреса очередного доступного участка словаря.
PAD ( -- а) Занесение в стек адреса начала рабочей области, в которой хранятся строки символов в процессе промежуточной обработки.
SP@ или 'S ( -- а) Занесение в стек адреса вершины стека данных до того, как исполнено само слово SP@.
S0 ( -- а) Содержит адрес дна стека данных.
TIB ( -- a) Занесение в стек адреса начала буфера входного текста.

МУЛЬТИЗАДАЧНЫЕ ФОРТ-СИСТЕМЫ

Наряду с однозадачными существуют и мультизадачные Форт-системы [Для начинающих. Термин "мультизадачная" описывает систему, в которой многочисленные задачи выполняются одновременно на одном и том же компьютере, не оказывая влияния друг на друга.]. Они могут работать с произвольным числом задач. Задача может быть либо ТЕРМИНАЛЬНОЙ, при выполнении которой вся интерактивная мощь Форта передается оператору, сидящему за терминалом, либо управляющей, которая обеспечивает управление аппаратным средством, не имеющим терминала.

Любой из задач нужна своя ПОЛЬЗОВАТЕЛЬСКАЯ ОБЛАСТЬ. Размер и содержимое пользовательской области зависят от вида задачи. Типовые структуры для двух видов задач показаны на рисунке.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b12010

Каждой терминальной задаче требуется собственный словарь, рабочая область (PAD), стек данных, буфер входного текста, стек возвратов и пользовательские переменные. Это означает, что все определяемые вами слова, как правило, НЕДОСТУПНЫ другим задачам. Кроме того, все задачи имеют свои собственные копии пользовательских переменных, таких, как BASE.

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

ПОЛЬЗОВАТЕЛЬСКИЕ ПЕРЕМЕННЫЕ

Пользовательская переменная в отличие от обычной (определяемой с помощью слова VARIABLE), значение которой хранится в поле параметров элемента словаря, состоит из двух частей. Сами данные хранятся в массиве, называемом ПОЛЬЗОВАТЕЛЬСКОЙ ТАБЛИЦЕЙ. Элемент словаря для каждой пользовательской переменной расположен в другом месте; он содержит смещение в пользовательской таблице. Когда вы выполняете имя пользовательской переменной, например H , смещение добавляется к начальному адресу пользовательской таблицы, что дает вам адрес H в этом массиве и позволяет применять @ или ! .

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b12110

Основное достоинство пользовательских переменных состоит в том, что любое число задач может использовать одно и то же ОПРЕДЕЛЕНИЕ некоторой переменной, и каждая задача может получать свое собственное ЗНАЧЕНИЕ этой переменной. Всякая задача, которая выполняет выражение

BASE @

получает значение BASE из своей пользовательской таблицы, благодаря чему экономится большой объем памяти, и тем не менее она может выполняться независимо от остальных задач.
Порядок размещения пользовательских переменных в таблице и значения и смещений изменяются от системы к системе. Итак, существуют переменные трех видов:
- системные, применяемые всей Форт-системой;
- пользовательские, являющиеся уникальными для каждой задачи, несмотря на то что их определения могут быть использованы всеми задачами системы;
- обычные, которые могут быть доступными либо во всей системе, либо в пределах единственной задачи (в зависимости от того как они определены: внутри слова OPERATOR или внутри частной задачи).

КОНТЕКСТНЫЕ СЛОВАРИ (СПИСКИ СЛОВ)

В простой Форт-системе имеются три штатных контекстных словаря: словарь Форта, словарь редактора и словарь ассемблера.

Все рассмотренные выше слова принадлежат словарю Форта, за исключением команд редактора, которые относятся к словарю редактора. В словарь ассемблера включены команды, предназначенные для программирования на языке Ассемблера конкретного компьютера.

Определения добавляются в один и тот же словарь в порядке их компиляции, независимо от того, к какому контекстному словарю они принадлежат. Таким образом, контекстные словари являются не подразделами словаря как участка памяти, а связанными списками, переплетенными между собой внутри этого общего словаря [Необходимо отличать общий словарь (dictionary) как прерывный участок памяти для размещения слов от словаря (vocabulary) - связанного списка слов. Примерами последнего могут служить словари Форта, редактора и ассемблера].

В качестве примера рассмотрим три контекстных словаря: "футбол", "бейсбол" и "баскетбол" (см. рисунок).

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b12210

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

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

Вы можете переключить контекст поиска по словарю, выполнив любую из трех команд: FORTH, EDITOR или ASSEMBLER. Например, вводя слово FORTH, вы уверены, что поиск будет осуществляться в контексте словаря Форта. Но, как правило, Форт-система изменяет для вас контекст автоматически. Рассмотрим типичную схему. Система начинает работу с контекста словаря Форта. Допустим, вы заносите некоторую программу в блок. Конкретные команды редактора переключают контекст на словарь редактора. Вы работаете в контексте словаря редактора до тех пор, пока не осуществите загрузку вашего блока и не приступите к компиляции определений. Слово : автоматически восстанавливает контекст, который был ранее, а именно: Форт.

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

Словарь, где должен осуществляться поиск, определяется пользовательской переменной с именем CONTEXT (КОНТЕКСТ). Как уже упоминалось выше, команды FORTH, EDITOR и ASSEMBLER изменяют контекст поиска.

Известен еще один вид контекста: словарь, к которому должно быть присоединено новое определение. Такой словарь задается другой переменной с именем CURRENT (ТЕКУЩИЙ). Поскольку CURRENT обычно определяет словарь Форта, то новые определения, как правило, присоединяются к данному словарю.

А каким образом система компилирует слова в словари редактора и ассемблера? Это делается с помощью слова DEFINITIONS (ОПРЕДЕЛЕНИЯ), например:

EDITOR DEFINITIONS

Вы знаете, что слово EDITOR устанавливает CONTEXT для редактора. Слово DEFINITIONS копирует содержимое слова CONTEXT, каким бы оно ни было, в слово CURRENT. Слово DEFINITIONS имеет простое определение:

: DEFINITIONS CONTEXT @ CURRENT ! ;

После вывода выражения EDITOR DEFINITIONS все компилируемые с этого момента слова заносятся в словарь редактора до тех пор, пока вы не введете выражение FORTH DEFINITIONS, чтобы поместить в CURRENT Форт.

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

СЛОВА

' xxx ( -- а) Осуществляется поиск в словаре адреса слова xxx ( следующего слова из входного потока ).
['] период-компиляции: ( --) период-выполнения: ( -- a) Используется только в определении через двоеточие. Компиляция адреса следующего слова из определения как литерала.
EXECUTE ( a --) Выполнение элемента словаря, адрес поля параметров которого находится на стеке.
@EXECUTE ( a --) Выполнение элемента словаря, на pfa которого ссылается содержимое по адресу а. Если адрес содержит нуль, то @EXECUTE ничего не выполняет.
>BODY ( cfa -- pfa) Вычисление адреса поля параметров определения, адрес компиляции которого находится на стеке.
EXIT ( --) Удаление адреса возврата, из вершины стена возвратов и занесение его в указатель адресного интерпретатора. Если слово EXIT скомпилировано в определении через двоеточие, то оно завершает выполнение этого определения в данной точке.
QUIT ( --) Очистка стека возвратов и передача управления терминалу, ожидающему ввода. Сообщение при этом ни выдается.
ABORT ( --) Очистке стека данных и выполнение функций слова QUIT. Сообщения не выдаются.
H или DP ( -- a) Занесение в стек адреса указателя словаря.
HERE ( -- a) Занесение в стек адреса очередного доступного участка словаря.
PAD ( -- a) Занесение в стен адреса начала рабочей области, в которой хранятся строки символов в процессе промежуточной обработки.
SP@ или 'S ( -- a) Занесение в стек адреса вершины стека данных до того, как исполнено само слово SP@.
S0 ( -- a) Содержится адрес дна стека данных.
TIB ( -- a) Занесение в стек адреса начала буфера входного текста.

ОСНОВНЫЕ ТЕРМИНЫ

АДРЕСНЫЙ ИНТЕРПРЕТАТОР -- Второй из двух интерпретаторов Форта выполняет список адресов из элемента словаря, являющегося определением через двоеточие.
БУФЕР ВХОДНОГО ТЕКСТА -- Участок памяти внутри терминальной задачи, используемый для хранения текста по мере его поступления с терминала. Именно здесь интерпретируется поступивший исходный текст.
ВЫБОРОЧНЫЕ ОПРЕДЕЛЕНИЯ -- Набор определений Форта, которые входят в систему, но хранятся отдельно от предварительно сгенерированных определений. Так называемый "блок выбора" загружает блоки, содержащие выборочные определения. Этот блок по желанию пользователя может быть изменен.
ЗАГРУЗКА -- Загрузка предварительно скомпилированного участка Форта в компьютер, после которой вы можете работать на Форте. Она происходит автоматически, когда вы включаете компьютер или нажимаете клавишу "Восстановление".
ЗАГОЛОВОК -- Поля имени и связи в элементе словаря Форта.
CFA -- Адрес поля кода; адрес поля кода в элементе словаря.
ЗАДАЧА -- Применительно к Форту это участок памяти, содержащий как минимум параметр и стек возвратов, а также множество пользовательских переменных.
КОД ПЕРИОДА ВЫПОЛНЕНИЯ -- Программа, которая хранится в памяти в скомпилированном виде и определяет, что происходит во время выполнения слов заданного класса. Код периода выполнений для определения через двоеточие осуществляет переход на следующий уровень и передает управление адресному интерпретатору. В случае переменной код периода выполнения помещает содержимое pfa данной переменной в вершину стека.
КОНТЕКСТНЫЙ СЛОВАРЬ -- Независимо связанное подмножество словаря Форта.
ВЕКТОРНЫЕ ВЫЧИСЛЕНИЯ -- Способ вычислений, при котором выполняемый код задается не своим адресом, а адресом некоторого участка, где содержится адрес этого кода. Такой участок обычно называют "вектором". При изменении состояния системы вектор может быть переключен на некоторый другой участок кода.
ОПРЕДЕЛЯЮЩЕЕ СЛОВО -- Слово Форта, создающее элемент словаря, например: CONSTANT, VARIABLE и т.д.
ПОЛЕ КОДА -- Ячейка в элементе словаря, содержащая адрес кода периода исполнения для данного конкретного типа определения.
ПОЛЕ ИМЕНИ -- Участок элемента словаря, содержащий имя (или сокращение этого имени) определенного слова, а также число символов в данном имени.
ПОЛЕ СВЯЗИ -- Ячейка элемента словаря, содержащей адрес предыдущего определения; применяется при поиске по словарю (в системах, использующих несколько цепочек, поле связи содержит адрес предыдущего определения в этой же цепочке).
ПОЛЕ ПАРАМЕТРОВ -- Участок элемента словаря, где находится "содержимое" данного определения: в случае CONSTANT - значение константы, в случае VARIABLE - значение переменной, в случае определения через двоеточие - список адресов слов, которые должны выполняться поочередно во время выполнения этого определения. Длина поля параметров меняется в зависимости от назначения.
ПОЛЬЗОВАТЕЛЬСКАЯ ПЕРЕМЕННАЯ -- Переменная Форта, к которой в отличие от пользовательской переменной возможен доступ из любой задачи системы.
PFA -- Адрес поля параметров; адрес первой ячейки поля параметров элемента словаря (или, если поле параметров представляет собой одну ячейку, адрес последней).
РАБОЧАЯ ОБЛАСТЬ (PAD) -- Участок памяти внутри терминальной задачи, который служит рабочей областью для хранения строк символов в процессе промежуточной обработки.
СИСТЕМНАЯ ПЕРЕМЕННАЯ -- Переменная Форта, которая в отличие от пользовательской переменной доступна по всей системе (в любой задаче).
ССЫЛКА ВПЕРЕД -- Ссылка на слово, которое еще не определено. В Форте - это способ реализации векторных вычислений.
ТЕЛО ЭЛЕМЕНТА -- Поля кода и параметров элемента словаря Форта.
ТЕРМИНАЛЬНАЯ ЗАДАЧА -- Задача в мультизадачной системе, обеспечивающая взаимодействие с оператором через терминал, т.е. задача, имеющая текстовый интерпретатор, словарь и т.д.
УПРАВЛЯЮЩАЯ ЗАДАЧА -- В мультизадачной системе - задача, которая не допускает взаимодействия с терминалом. Управляющие задачи, как правило, обслуживают аппаратные средства.
УЧАСТОК ПРЕДВАРИТЕЛЬНО СКОМПИЛИРОВАННЫХ ОПРЕДЕЛЕНИЙ -- Часть Форт-системы, которая постоянно присутствует в памяти в объектном коде сразу после выполнения операции начальной загрузки или восстановления. Эта часть обычно включает в себя текстовый и адресный интерпретаторы, определяющие слова, структуры управления, операции одинарной длины и операции над стеком, команды преобразования чисел одинарной длины и их форматизирования, редактор, ассемблер.

УПРАЖНЕНИЯ

1. Напишите определение слова ПОЛУЧАЕМ так, чтобы его функции могли корректироваться словами СКЛАДЫВАЯ и УМНОЖАЯ, как в приведенном ниже примере:

СКЛАДЫВАЯ 2 3 ПОЛУЧАЕМ _5_ok
УМНОЖАЯ 2 3 ПОЛУЧАЕМ _6_ok

2. Каков начальный адрес вашего личного словаря?

3. Как далеко расположена рабочая область (PAD) в чашей системе от конца вашего личного словаря?

4. Если слово ДАТА определено через VARIABLE, то в чем состоит различие между следующими фразами ДАТА . и ' ДАТА >BODY . ? А чем отличаются друг от друга эти две фразы: BASE . и ' BASE >BODY . ?

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

0 ВЫПОЛНИ _Привет,_я_говорю_на_Форте._ok
1 ВЫПОЛНИ _1_2_3_4_5_6_7_8_9_10_ok
2 ВЫПОЛНИ
_**********
_**********
_**********
_**********
_**********
3 ВЫПОЛНИ _ok
4 ВЫПОЛНИ _ok
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Ср Апр 21, 2021 12:38 am

ГЛАВА 10. ВВОД-ВЫВОД

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

БЛОЧНЫЕ БУФЕРЫ

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b01410

Здесь мы имеем дело с так называемой ВИРТУАЛЬНОЙ ПАМЯТЬЮ, т.е. работаем с памятью большой емкости, как с памятью компьютера.

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b01510

Допустим, в вашей системе имеются два буфера и вы выполняете следующие действия. Сначала вы распечатываете блок 200. Система считывает его с диска и пересылает в буфер 0, откуда слово LIST выводит этот блок на печать. Далее вы распечатываете блок 201. Система копирует блок 201 с диска в другой буфер. И наконец, вы распечатываете блок 202. Система копирует данный блок с диска в буфер, который применялся в первую очередь, а именно в буфер 0.

Что случилось с бывшим содержимым буфера 0? Оно просто было замещено (затерто) новым содержимым. Старая информация для нас не потеряна, так как блок 200 все еще остается на диске. Но если бы вы ОТРЕДАКТИРОВАЛИ блок 200, были бы утеряны ваши исправления? Нет, конечно. При распечатке блока 202 СНАЧАЛА измененное содержимое блока 200 посылается на диск с тем, чтобы обновить прежнее содержимое этого блока, а ЗАТЕМ в буфер помещается содержимое блока 202.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b01610

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

Основным словом, которое считывает содержимое какого-либо блока с диска, является BLOCK. Например, если вы введете

205 BLOCK

то система скопирует блок 205 в один из буферов. Кроме того, BLOCK оставляет в вершине стека адрес начала этого буфера. Используя адрес начала буфера как базу, вы можете получить доступ к любому байту блока [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ НЕКОТОРЫХ СИСТЕМ ФИГ-ФОРТА. Блоки, занимающие 1024 байта, могут быть считаны в нескольких несмежных буферов, что затрудняет индексирование блоков (см. листинги системы фиг-Форта)]. Слово BLOCK получает управление всякий раз, когда вы распечатываете содержимое блока посредством LIST или загружаете его с помощью LOAD.

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

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

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

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

Слово FLUSH (ВЫБРОС) инициирует немедленную запись всех обновленных буферов на диск. (Так как вы теперь имеете представление о буферах, можно объяснить, для чего нужно слово FLUSH: обновление буфера не означает записи его содержимого на диск). Помимо прочего после употребления слова FLUSH система "забывает", что в буферах хранилось содержимое блоков (она освобождает все буферы). Если вам понадобится распечатать или загрузить один из таких блоков, то придется считать его содержимое посредством BLOCK с диска снова.

Определенное Стандартом-83 слово SAVE-BUFFERS (СОХРАНИТЬ-БУФЕРЫ) по своим возможностям беднее слова FLUSH: оно сохраняет на диске содержимое обновленных буферов, но не освобождает их. Если вам снова потребовался некоторый блок, то его содержимое уже находится в одном из буферов, и Форт-система в этом случае не обращается к диску.

Как правило, вам нет необходимости пользоваться приведенными выше словами, поскольку слово BLOCK гарантирует, что перед повторным применением буферов их содержимое будет сохранено на диске. Однако во время отладки новой программы вам эти слова могут пригодиться (прежде чем система выйдет из строя, исправления все же должны попасть на диск). SAVE-BUFFERS переписывает хранящиеся в буферах блоки на диск, но с содержимым буферов можно работать и в дальнейшем. Это сокращает число обращений к диску (что экономит время и позволяет избежать многих неприятностей). Слово FLUSH необходимо при смене дисков, так как оно эффективно освобождает буферы от их прежнего содержимого, или в тех случаях, когда вы хотите убедиться в том, что информация ДЕЙСТВИТЕЛЬНО была записана на диск.

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

FLUSHSAVE-BUFFERS
Фиг-ФортКопирование всех обновленных буферов во внешнюю память и их освобождение(Не определено)
Стандарт-79(Не определено: переименованное слово SAVE-BUFFERS)Копирование всех обновленных буферов во внешнюю память и их освобождение
Стандарт-83Копирование всех обновленных буферов и их освобождениеКопирование всех обновленных буферов во внешнюю память, сброс флагов обновления этих буферов без их освобождения

На всех диалектах слово EMPTY-BUFFERS заставляет систему "забыть" о том, что у нее какие-то блоки размещены в буферах и сбрасывает все флаги обновления без записи содержимого блоков на диск. EMPTY-BUFFERS полезно применять в тех случаях, если вы случайно испортили [ДЛЯ НАЧИНАЮЩИХ. Испорченные, бессмысленные или не имеющие отношения к обработке, ради которой были введены, данные программисты называют "мусором"] содержимое некоторого буфера (например, удалили несколько нужных строк, а их текст забыт или просто что-нибудь напутали) и не хотите, чтобы оно попало на диск. Когда вы после выполнения этого слова снова читаете свой блок, система не выясняет, есть ли содержимое вашего блока в памяти, а просто считывает его с диска [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ МУЛЬТИПРОГРАММНОЙ СИСТЕМЫ. Будьте осторожны! Слово EMPTY-BUFFERS освобождает все буферы. Программа для работы с базой данных, широко применяющая средства восстановления после ошибок, не должна делать это с помощью EMPTY-BUFFERS. Но Форт-программы, использующие блоки, легко расширить с тем, чтобы они удовлетворяли запросы любой пользовательской программы].

Согласно Стандарту-83 слово FLUSH можно определить следующим образом:

: FLUSH SAVE-BUFFERS EMPTY-BUFFERS ;

Слово BUFFER заносит информацию на диск без учета прежнего содержимого диска (например, при инициализации диска, записи потока информации, копировании ленты на диск и т.д.). BUFFER используется словом BLOCK для назначения номера блока следующему доступному буферу. Само слово BUFFER содержимое с диска в буфер не считывает (хотя в некоторых системах считывает). К тому же оно не проверяет, был ли номер блока присвоен какому-либо буферу, и может случиться так, что один и тот же номер будут иметь два буфера. Такую ситуацию вы обязаны контролировать сами.

UPDATE ( --) Блок, доступ к которому осуществлялся в последнюю очередь, отмечается как модифицируемый. Этот блок будет впоследствии переписан во внешнюю память, если его буфер потребуется для размещения другого блока или в результате выполнения слова FLUSH.
SAVE-BUFFERS ( --) Запись содержимого всех обновленных буферов в соответствующие блоки внешней памяти. У всех буферов погашается признак обновления, но они продолжают оставаться распределенными.
FLUSH ( --) Осуществляется SAVE-BUFFERS, затем происходит погашение признака обновления всех буферов. Используется при установке или смене накопителей внешней памяти.
EMPTY-BUFFERS ( --) Все блочные буфера отмечаются как пустые независимо от им содержания. Обновленные блоки во внешнюю память не записываются.
BLOCK ( u -- a) Занесение в стек адреса первого байта в блоке u. Если данного блока еще в памяти нет, то происходит его пересылка из внешней памяти в тот буфер, к которому дольше всех не было доступа. Если блок, занимающий данный буфер, обновлялся (то есть был модифицирован), то перед считыванием блока u в буфер содержимое последнего будет переписано во внешнюю память.
BUFFER ( u -- a) Функции те же, что и у BLOCK, за исключением того, что сам блок из внешней памяти не считывается.

ОПЕРАТОРЫ ВЫВОДА

Слово EMIT берет из стека одно значение в коде ASCII, используя только младший байт, и выводит этот символ. Например, при текущей десятичной системе счисления вы получите такой результат:

65 EMIT _Aok
66 EMIT _Bok

Слово TYPE выводит всю строку символов при ее заданном начальном адресе и счетчике в следующей форме:

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

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

Зададим слову TYPE адрес, по которому, как нам известно, находится строка символов. Напомним, что начальный адрес буфера входного текста задается словом TIB (см. гл.9 о вариантах диалектов). Допустим, вы вводите команду:

TIB 11 TYPE

В результате будет выведено 11 символов из буфера входного текста, который содержит только что введенную команду:

TIB 11 TYPE<return>_TIB_11_TYPEok

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

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

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

CR 214 BLOCK 64 TYPE<return>
_(_ЭТО_БЛОК_214_)________________________________________________ok

Для вывода строки 8 нужно добавить к исходному адресу 512 (8*64):

CR 214 BLOCK 512 + 64 TYPE

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

-TRAILING ( a u1 -- a u2) Удаление незначащих пробелов из строки с заданным адресом путем уменьшения значения счетчика от u1 (счетчик исходных байтов) до u2 (счетчик байтов, полученных в результате вычеркивания пробелов).
>TYPE ( a u --) То же самое, что и TYPE, за исключением того, что перед выдачей выводимая строка помещается в pad. Используется в мультипрограммных системах для вывода строк, находящихся в блоках на диске.

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

CR 214 BLOCK 64 -TRAILING TYPE<return>
_(_ЭТО_БЛОК_214)ок

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

Слово >TYPE применяется только в мультипрограммных системах для вывода строк из буферов, находящихся на диске. Вместо того чтобы непосредственно выдавать строку с заданного адреса, оно предварительно перекачивает строку целиком в рабочую область и затем выводит ее оттуда. Поскольку все пользователи разделяют одни и те же буферы, система не может гарантировать, что к тому времени, когда TYPE завершит вывод содержимого какого-то буфера, последний будет все еще хранить прежний блок. Однако вы можете быть уверены в том, что данный буфер содержит один и тот же блок во время перекачки этого буфера в рабочую область [ДЛЯ СПЕЦИАЛИСТОВ. В мультипрограммной системе задача передает управление центрального процессора следующей задаче только на время ввода-вывода или по специальной команде, которая преднамеренно не включена в определение слова, пересылающего строки]. Так как каждой задаче отведена своя рабочая область, >TYPE может выводить из нее информацию без риска получить не те данные.

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

Block# 231
0 ( Генератор бессмысленных сообщений)
1 : АБРЕД ( -- а) 232 BLOCK ;
2 : БРЕД ( строка# столбец# -- а)
3 20 * SWAP 64 * + АБРЕД + ;
4 : .БРЕД ( столбец# колонка# --) БРЕД 20 -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

После загрузки блока (в нашем примере блока 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

и в вершине стека останется случайное число.

ОПЕРАЦИИ НАД СТРОКАМИ В ОПЕРАТИВНОЙ ПАМЯТИ

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

MOVE ( a1 a2 u --) Поячеечное копирование участка памяти длиной u байтов, начинающегося с адреса a1, в участок памяти, начинающийся с а2. Пересылка идет с a1 в сторону увеличения адресов.
CMOVE ( a1 a2 u --) Побайтное копирование участка памяти длиной u байтов, начинающегося с a1, в участок памяти, начинающийся с а2. Пересылка идет с а1 в сторону увеличения адресов.
CMOVE> ( a1 a2 u --) Копирование участка памяти длиной u байтов, начинающегося с адреса a1, в участок памяти, начинающийся с адреса а2. Копирование начинается с КОНЦА строки и продвигается в сторону уменьшения адресов.

Заметим, что для этих команд существуют определенные соглашения (рассмотренные ранее):
- если среди аргументов находятся источник и адресат, как это имело место в случае с COPY, то источник предшествует адресату;
- если среди аргументов находятся адрес и счетчик, как это имело место в случае с TYPE, то адрес предшествует счетчику.

Поэтому для перечисленных трех слов аргументы следуют в таком порядке: ( источник адресат счетчик --). Для того чтобы переслать содержимое всего буфера в рабочую область, вы могли бы, к примеру, написать:

210 BLOCK PAD 1024 CMOVE

хотя на машинах с поячеечной адресацией памяти намного быстрее выполнялась бы последовательная пересылка ячеек (ячейка за ячейкой):

210 BLOCK PAD 1024 MOVE

Слово CMOVE> позволяет пересылать некоторую строку в область, которая расположена в памяти с большими адресами, но пересекается с исходной областью [ДЛЯ НАЧИНАЮЩИХ. Предположим, вы хотите передвинуть некоторую строку в памяти на один байт вправо (например, когда текстовый редактор включает в текст некоторый символ). Применив команду CMOVE, вы скопировали бы первую букву строки во второй байт, но при этом вторая литера строки оказалась бы "испорченной".

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b01710

В результате получилась бы строка, составленная из одного и того же символа. Чтобы сохранить исходную строку, в данной ситуации нужно воспользовался словом CMOVE> ].
Для заполнения некоторого массива пробелами вы можете воспользоваться уже знакомым вам словом BLANK [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ ФИГ-ФОРТА. В вашей системе это слово BLANKS (ПРОБЕЛЫ)]. Например, внесите пробелы в 1024 байта рабочей области:

PAD 1024 BLANK

Это равносильно выражению:

PAD 1024 BL FILL

В большинстве систем слово BL определено как константа со значением 32.

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

Остроумный, но не согласующийся со стандартом прием: для заполнения участка памяти многобайтным шаблоном примените слово CMOVE. При выполнении следующего выражения 20-байтный массив будет заполнен 10-ю копиями 16-разрядного адреса слова НОП:

CREATE ТАБЛИЦА ' НОП , 18 ALLOT \ 10 ячеек
ТАБЛИЦА DUP 2+ 18 CMOVE \ инициализация выделенной памяти
\ адресом НОП
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Ср Апр 21, 2021 12:47 am

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

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

KEY<return>

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

.<return> _65_ok

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

: БЛОКИ ( u --)
SCR @ + SCR @ DO I LIST KEY DROP LOOP ;

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

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

: БЕСКОНЕЧНЫЙ 0 BEGIN DUP . 1+ FALSE UNTIL DROP ;

Можно организовать выход из такого цикла, заменив слово FALSE на KEY?:

: СКАЖИ-КОГДА 0 BEGIN DUP . 1+ KEY? UNTIL DROP
KEY DROP ;

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b01810

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

TIB 80 EXPECT

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

С помощью EXPECT вы можете сделать запрос на ввод из определения [ДЛЯ СПЕЦИАЛИСТОВ. Вы можете использовать EXPECT для снятия данных с последовательной шины, например, некоторого измерительного устройства. Так как у вас задействованы адрес и счетчик, такие данные могут быть считаны непосредственно в массив. Если вы являетесь единственным пользованием системы, то перед записью на диск можете считать данные в буфер. В случае же мультизадачной системы вы должны применить TIB и уже потом пересылать данные в указанный буфер, поскольку "вашим" буфером может воспользоваться другая задача.].
Ниже дается слово, которое при своем выполнении запрашивает имя пользователя, а затем выводит введенное имя вместе с приветствием:

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

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

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

]:

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

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

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

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

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

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

Я BACЯ<return>

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

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

BL WORD

будет просматривать входной поток в поисках фрагмента текста, ограниченного пробелами. Найденную подстроку WORD поместит в свой собственный временный буфер вместе со счетчиком символов в первом байте буфера. В Форте строка символов, предваряемая одним байтом, в котором содержится число символов данной строки, называется СТРОКОЙ СО СЧЕТЧИКОМ. Затем WORD вносит в вершину стека адрес своего временного буфера [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ ФИГ-ФОРТА. Ваша версия слова WORD ничего в вершине стека не оставляет. Для обеспечения совместимости с текстом рассматриваемого примера переопределите его:

: WORD ( -- a) WORD HERE ;

].

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b01910

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

: Я ( имя-пользователя ( --)
ИМЯ-ПОЛЬЗОВАТЕЛЯ 40 BLANK BL WORD COUNT ИМЯ-ПОЛЬЗОВАТЕЛЯ
SWAP CMOVE ;

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b02010

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b02110

и в вершине стека остаются адрес строки и значение счетчика, которые могут служить аргументами для слов 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 u) Преобразование адреса строки со счетчиком (длина которой находится в первом [байте] строки) в формат, соответствующий использованию словом TYPE а именно: в стек заносится адрес начала текста строки и ее длина.

ПРИМЕНЕНИЕ СЛОВА WORD

Помимо текстового интерпретатора многие слова Форта используют WORD. Так, слово CREATE выбирает из входного потока имя создаваемого слова, a FORGET - имя слова, которое должно быть забыто.

Вернемся к приведенному выше примеру с генератором бессмысленных сообщений. Хотелось бы иметь при таком генераторе удобные средства введения в базу данных бессмысленных фраз (текстовый редактор Форта не показывает нам, где начинается 20-й или 40-й столбец). Допустим, нам нужно определить для введения в базу данных очередного слова из входного потока слово "добавить":

начало
добавить высокий
добавить культурный
добавить уровень
добавить общий
и т.д.

Это можно сделать следующим образом:

\ загрузчик в базу данных бессмысленных сообщений
VARIABLE РЯД
VARIABLE СТОЛБЕЦ
: начало 0 РЯД ! 0 СТОЛБЕЦ ! ;
: +РЯД 1 РЯД +! ;
: +СТОЛБЕЦ СТОЛБЕЦ @ 1+ 3 /MOD РЯД +! СТОЛБЕЦ ! ;
: добавить \ бессмысленная фраза ( -- )
1 WORD COUNT РЯД @ СТОЛБЕЦ @ БРЕД DUP 20 BLANK
SWAP CMOVE UPDATE +СТОЛБЕЦ ;

Обратите внимание на то, как приведенные выше программы вычисляют соответствующие строку и столбец, а также на удачное выделение слова БРЕД, которое оставляет в вершине стека адрес в блоке пересечения заданных строки и столбца.

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

Со словом WORD могут сочетаться и другие ограничители, например кавычки и круглые скобки. Слово Форта ." использует выражение

ASCII " WORD

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

ASCII ) WORD

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

Уже знакомое вам слово TEXT упрощает определение слова "добавить":

: добавить \ бессмысленную фразу ( --)
1 TEXT PAD РЯД @ СТОЛБЕЦ @ БРЕД 20 CMOVE UPDATE
+СТОЛБЕЦ ;

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

Откуда и что читать, слово WORD "понимает" по двум указателям. Первый из них >IN [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ ФИГ-ФОРТА. В вашей системе это слово IN. Чтобы обеспечить соответствие нашему указателю, переопределите его:

: >IN ( -- a) IN ;

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

STAR 30 SPACES

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b02210

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

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

Ниже показан адрес интерпретируемого в данный момент текста:

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

На Форте адрес сканирования для слова WORD вычисляется следующим образом:

... BLK @ ?DUP IF BLOCK ELSE TIB THEN >IN @ + ...

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

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

ПРЕОБРАЗОВАНИЕ ВВОДИМЫХ ЧИСЕЛ

Как было показано в гл.7, с помощью слов <# и #> можно преобразовать число, находящееся в стеке, в строку символов в коде ASCII. Слово CONVERT (ПРЕОБРАЗОВАТЬ) выполняет обратную функцию: оно переводит строку символов ASCII, представляющих некоторое число, в двоичную форму и заносит его в стек.

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

Приведем несложный пример:

: ПЛЮС \ n2 ( n1 -- сумма)
0 0 BL WORD CONVERT 2DROP + ;

Это определение можно использовать следующим образом:

12 ПЛЮС 23 . _35_ok

Слово ПЛЮС является лучшим доказательством (для скептиков) того, что в Форте можно применять инфиксную запись выражений. Это слово начинает выполняться при одном занесенном в стек в двоичной форме аргументе 12. В первую очередь, занося в стек нуль двойной длины, мы очищаем место для накопления, после чего слово WORD сканирует входной поток для чтения второго аргумента n2 и оставляет адрес прочитанного фрагмента в вершине стека. Слово CONVERT преобразует строку по заданному адресу (пропуская первый байт со счетчиком) в число двойной длины и заносит его в стек на место нуля. 2DROP удаляет из стека последний адрес, помещенный в него словом CONVERT вместе со старшей ячейкой преобразованного числа, превращая последнее тем самым в число одинарной длины. Наконец, + складывает два верхних числа в стеке.

Если бы слово CONVERT функционировало с такими аргументами:

( а -- ud)

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

6/20/85

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

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

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

Поэтому слово ПЛЮС лучше определить так:

: ПЛЮС \ n2 ( n1 -- сумма)
BL WORD NUMBER DROP + ;

Слово NUMBER используется и самой ФОРТ-системой. Это "обработчик чисел", к которому обращается текстовый интерпретатор, если искомое слово не найдено в словаре. NUMBER пытается преобразовать полученный фрагмент в число и в случае удачи заносит его значение в стек, при неудаче же осуществляется ABORT.

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

: , - . /

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

\ Определение слова NUMBER
: NUMBER? ( адр - d t-успешное-завершение)
DUP 1+ С@ \ Получение первой цифры
ASCII - = \ Это знак минус?
DUP >R \ Запоминать флага в стеке возвратов
\ Если первым символом является "-" , то
\ к адр добавляется 1, чтобы тот указывал
\ на первую цифру ( вычитание -1 равносильно
\ прибавлению 1)
-1 DPL ! \ Отметка того, что знаков пунктуации
\ пока нет.
0 0 ROT \ В качества первоначального накапливаемого
\ значения берется 0 двойной длины.
BEGIN CONVERT \ Преобразование до первого символа, не
\ являющегося цифрой
DUP C@ DUP ASCII : = \ Это двоеточие?
SWAP ASCII . ASCII / 1+ \ Это запятая,
WITHIN OR \ дефис, точка или слэш?
WHILE 0 DPL ! REPEAT \ Если да, то переустановить DPL и
\ продолжать
-ROT R> IF DNEGATE THEN \ Перемещение d в вершину. Если
\ значение в стеке возвратов указывает минус, то число
\ делается отрицательным.
ROT С@ BL = ; \ Является ли последний, непреобраэованный,
\ символ пробелом, как это и должно быть?
: NUMBER ( адр -- d)
NUMBER? NOT ABORT" ?" ; \ Если преобразование завершилось
\ неудачей, то аварийный выход посредством ABORT.

В приведенном определении учитывается, что CONVERT вычитает из DPL по единице при обработке каждой цифры до тех пор, пока значение переменной не станет равным -1. Кроме того, в определении используется слово WITHIN, аналогичное слову ВНУТРИ (см. упражнение к гл.4).

В качестве "истины" принято арифметическое значение -1, как это определено Стандартом-83. Для более ранних систем, где значением истины является единица, в строке 4 нужно заменить "-" на "+".

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

... NUMBER DPL @ -1 = IF DROP THEN ...

ПОСТРОЕНИЕ ПРОГРАММЫ ВВОДА ЧИСЕЛ С ПОМОЩЬЮ СЛОВА KEY

В данном разделе мы покажем вам, как слово KEY может быть использовано при разработке специализированного интерпретатора ввода с клавиатуры. Допустим, вы хотите создать слово, подобное EXPECT, которое ожидало бы ввода с клавиатуры, но воспринимало бы только числовые символы. Остальные символы не должны восприниматься. Исключение составляют сигналы, посылаемые при нажатии клавиш ЗАБОЙ (в обычном значении) и RETURN (конец ввода). В отличие от EXPECT ваше слово не будет завершать свое выполнение при вводе заданного числа цифр, так как пользователь может, нажав клавишу ЗАБОЙ, исправить последнюю цифру до нажатия клавиш RETURN. Назовем это слово EXPECT# и зададим ему следующий порядок аргументов:

( a макс-длина -- факт-длина)

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

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

: ЗАБОЙ 8 EMIT ;

но в некоторых системах, где применяются устройства вывода с распределенной памятью, могут потребоваться другие определения. Кроме того, символы, получаемые словом KEY при нажатии клавиш ЗАБОЙ и RETURN, в различных системах различны. Упрятывание информации происходит следующим образом:

: ЗБ? ( с -- t=клавиша-забоя) 8 = ; \ в некот.сист. 12
: ВК? ( с -- t=клавиша-RETURN) 13 = ; \ в некот.сист. 0

Основной конструкцией слова EXPECT# является цикл BEGIN WHILE REPEAT. Здесь WHILE выполняет проверку на нажатие клавиши RETURN. Цикл начинается со слова KEY. Если введена правильная цифра, вы обрабатываете ее с помощью слова ПОЛУЧЕНИЕ (посылаете в буфер и увеличиваете соответствующий указатель). В случае "ЗАБОЯ" вы уничтожаете последний символ посредством слова НАЗАД (заполняете пробелом позицию последнего введенного символа и уменьшаете значение указателя). Используя таким образом слово KEY в цикле, можно создавать любые интерпретаторы клавиатуры или редакторы. Последнее определение, ЦИФРЫ, демонстрирует использование слова EXPECT#. Итак, в листинге не осталось больше ничего, что было бы вам не известно, поэтому никаких причин для того, чтобы отложить рассмотрение этой программы, у вас нет. (Более подробную информацию вы найдете в [1]).

Block# 350
0 \ Ввод чисел часть 1
1
2 : НАЗАД 8 EMIT ;
3 : ЗБ? ( с -- t=клавиша-забоя) 8 = ; \ в некот.сист. 12
4 : ВК? ( с -- t=клавиша-RETURN) 13 = ; \ в некот.сист. 0
5 : #? ( с -- t=правильная-цифра) ASCII 0 ASCII 9 1+ WITHIN ;
6
7
8
9
10
11
12
13
14
15

Block# 351
0 \ Ввод чисел часть 2
1 : ПОЛУЧЕНИЕ ( 1-й-адр посл-а+1 текущ-адр с - 1-й-адр
2 посл текущ')
3 >R 2DUP > IF R@ DUP EMIT OVER C! 1+ THEN R> DROP ;
4 : НАОБОРОТ ( 1-й-адр посл-а+1 текущ-адр с -- 1-й-адр поcл
5 текущ' )
6 DROP SWAP >R 2DUP < IF НАЗАД SPACE НАЗАД 1- DUP 1 BLANK
7 THEN R> SWAP ;
8 : EXPECT# ( а макс-длина -- факт.-длина)
9 OVER + OVER BEGIN KEY DUP ВК? NOT WHILE
10 DUP #? IF ПОЛУЧЕНИЕ ELSE
11 DUP ЗБ? IF НАОБОРОТ ELSE DROP
12 THEN THEN REPEAT ROT 2DROP SWAP - ;
13
14 : ЦИФРЫ ( #цифр -- d)
15 PAD SWAP 2DUP 1+ BLANK EXPECT# DROP PAD 1- NUMBER ;
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Ср Апр 21, 2021 12:53 am

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

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

-TEXT ( a1 # a2 -- ?) Сравнение двух строк длиной #, начинающийся с a1 и a2. Если сравнение успешное, на стек заносится ложь. Если нет, на стек заносится истина (положительное число, если двоичное представление строки1 > двоичного представления строки2, и отрицательное, если стр.1 < стр.2 ).
-MATCH ( d # s # -- а ?) Поиск фрагмента длиной #, начинающегося с адреса s (источник) в области памяти длиной #, начинающейся с адреса d (получатель). Если поиск завершился успешно, на стек помещается начало искомого фрагмента a заданной области памяти и ложь. В противном случае неправильный адрес и истина.

С помощью слова -TEXT вы можете либо сравнить две строки, либо проверить порядок их расположения (по алфавиту) [ДЛЯ ПОЛЬЗУЮЩИХСЯ ПРОЦЕССОРАМИ INTEL, DEC И ZILOG. Для того чтобы выполнить такую проверку, вы должны расположить байты в обратном порядке]. В гл.12 приводится пример использования слова -TEXT для выявления полного совпадения строк.

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

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

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

: -TEXT ( a1 # a2 -- f=сравнение | полож=1>2 | отр=1<2)
2DUP + SWAP DО DROP 2+ DUP 2- @
I @ - DUP IF DUP ABS / LEAVE THEN
2 +LOOP SWAP DROP ;

Слово -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 ;

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

Текстовая строка, скомпилированная в словарь со средствами получения ее адреса, называется строковым литералом. Общим словом для создания строковых литералов является слово STRING (СТРОКА) [Для пользователей всех систем, кроме полифорта. Во многих системах (но не всюду) вы можете воспользоваться следующим определением: : STRING ( с -- ) WORD C@ 1+ ALLOT ; Мы полагаем, что в таких системах буфер слова WORD начинается с НERE, поэтому здесь не требуется пересылка строки и счетчика в словарь. Адрес, оставляемый в стеке словом WORD, является адресом со счетчиком, там что слово С@ помещает значение этого счетчика в вершину стека. Затем слово ALLOT продвигает указатель словаря по длине строки (плюс один из-за счетчика), резервируя память для нее в словаре.]. Оно функционирует следующим образом:

CREATE СООБЩЕНИЕ BL STRING ПРИВЕТ
СООБЩЕНИЕ COUNT TYPE _ПРИВЕТ_ok

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

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

CREATE "МАРКИРОВКА"
ASCII " STRING Брак___Мелкие__Средние_Крупные_Оч.крупнОшибка "

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

"МАРКИРОВКА" 1+ 16 + 8 TYPE

Теперь переопределим слово МАРКИРОВКА так, чтобы оно выбирало из стека номер категории от нуля до пяти и использовало его как индекс в нашем строковом массиве:

: МАРКИРОВКА ( категория# --)
8 * "МАРКИРОВКА" + 8 TYPE SPACE ;

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

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

: МАРКИРОВКА 0 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 (получатель). Если поиск завершился успешно, на стек помещается начало искомого фрагмента в заданной области памяти и ложь. В противном случае неправильный адрес и истина.
STRING ( с --) Компиляция строкового литерала, ограниченного символом c, в словарь как строки со счетчиком.
LIT" xxx" период-выполнения: ( -- a) Компиляция строкового литерала ххх, ограниченного двойной кавычкой. В период выполнения на стек помещается адрес строки со счетчиком. Используется только внутри определений.

ОСНОВНЫЕ ТЕРМИНЫ

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

УПРАЖНЕНИЯ

1. Введите несколько известных цитат в некоторый доступный блок, скажем в блок 228. Определите слово с именем ЗАМЕНА, которое берет из стека два значения в коде ASCII и заменяет все вхождения первого символа в блоке 228 на второй символ. Так следующее выражение заменит все литеры А литерами Е:

65 69 ЗАМЕНА

2. Определите слово с именем ПРОГНОЗ, которое будет выдавать на вашем терминале некоторый прогноз, например "Вы получите хорошие новости по почте". Прогноз должен выбираться случайным образом из списка, включающего 16 (или менее) вариантов. Каждый прогноз может иметь длину до 64 символов.

3. а) Определите слово ДА/НЕТ?, которое можно использовать в прикладной программе, требующей утвердительного или отрицательного ответа пользователя. Определение должно ожидать нажатия одной клавиши, после которого послать в стек значение истина, если была нажата клавиша Y, и ложь при нажатии любой другой клавиши.
б) Иногда пользователь нажимает эту клавишу, работая в режиме нижнего регистра. Перепишите определение ДА/НЕТ? таким образом, чтобы оно воспринимало нажатие клавиши Y в обоих регистрах как "да".
в) Пользователь может по ошибке вместо клавиши Y (например, при записи файла на диск, над которым он проработал последние три часа) нажать другую клавишу. Перепишите определение ДА/НЕТ? таким образом, чтобы оно воспринимало символы, поступающие только при нажатии клавиш Y и N в верхнем и нижнем регистрах и не прекращало своего выполнения до тех пор, пока не получит сигналы от этих клавиш с записью в стек истины в случае Y и лжи в случае N.

4. Согласно восточной легенде Будда наделял всех людей, родившихся в определенном году, чертами, присущими одному из 12 животных. На этой основе построен циклический календарь. Цикл составляет 12 лет, по истечении которых он возобновляется. Например, считается, что 1900 год был "годом крысы". Внутри цикла годы, обозначенные названием животного, чередуются в следующем порядке: год крысы, год быка, год тигра, кролика, дракона, змеи, лошади, барана, обезьяны, петуха, собаки, свиньи. Напишите слово с именем ЖИВОТНОЕ, которое бы выводило название конкретного животного в зависимости от его места в приведенном списке, например:

0 .ЖИВОТНОЕ _КРЫСА_ok

Далее напишите слово с именем (ГОРОСКОП), отражающим искусство составления гороскопа Для рожденных под тем или иным циклическим знаком так, чтобы оно могло брать в качестве аргумента год рождения и выводить название соответствующего животного.

Наконец, напишите слово с именем ГОРОСКОП, которое запрашивало бы у пользователя год рождения. При этом на экран должны выводиться четыре символа подчеркивания, отмечая моле для ввода года рождения, после чего должен отработать четыре раза "ЗАБОЙ", возвращая курсор в первоначальное положение. Используйте определение слова EXPECT#. введенное в настоящей главе ("Построение программы ввода чисел с помощью слова KEY".) После нажатия клавиши RETURN на экране должно появиться название соответствующего животного по циклическому календарю.

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

начало
добавить высокий, культурный, уровень

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

6. Мы полагаем, что слово CONVERT может применяться при анализе числа, представленного, скажем, таким замысловатым способом: "6/20/88". Напишите определение с именем >ДАТА, которое сканировало бы подобные числа но заданному адресу и оставляло в вершине стека дату в виде двух 16-разрядных чисел. При этом старшая ячейка содержала бы год (1988 ), а младшая - месяц и день, причем месяц бы находился в старшем байте. Затем создайте слово СКАНИРОВАНИЕ-ДАТЫ, которое сканировало бы входной поток в поисках даты и оставляло бы в вершине стека то же значение, что и слово >ДАТА.

7. В гл.8 было показано, как создаются массивы ячеек (массив из 16-разрядных чисел) в словаре. В данном упражнении нужно создать виртуальный массив (не в оперативной памяти, а на диске), состоящий из 16-разрядных значений. Сначала найдите три доступных смежных блока, где вы будете хранить свой массив. В блоках не должно быть никакой текстовой информации, так как в массиве придется хранить двоичные числа. Определите переменную, которая указывала бы на первый из трех блоков. Далее создайте слово с именем ЭЛЕМЕНТ со следующей стековой нотацией:

( индекс -- адрес)

Это слово переводит индекс ячейки в абсолютный адрес внутри блочного буфера. Обратите внимание на то, что в первом блоке будут храниться только 512 ячеек. Кроме того, оставляемый в вершине стека адрес указывает па второй блок набора и т.д. Слово ЭЛЕМЕНТ также должно вызывать UPDATE.

Теперь созданное вами нужно проверить. Напишите программу инициализации первых 600 ячеек некоторыми значениями и выведите их содержимое на экран, после чего переопределите слово ЭЛЕМЕНТ так, чтобы первый элемент массива резервировался бы для хранения счетчика запомненных элементов. Кроме того, определите слово ИСПОЛЬЗОВАНО для занесения в стек значения счетчика. Напишите программу очищения массива. Определите слово ПОМЕСТИТЬ, которое выбирает из стека 16-разрядное значение и добавляет его к массиву на первое свободное место.

Далее определите слово ВВОД, которое добавляет к массиву два 16-разрядных значения, взятых из стека, причем значение, взятое вторым, добавляется первым. И наконец, определите слово ТАБЛИЦА для вывода информации на экран: шесть чисел на строку. (Благодарим за пример К.Хэрриса).

ЛИТЕРАТУРА

1. Ham, Michael, "Think Like a User, Write Like a Fox", Forth Dimensions, VI/3, p.23.
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

vikt144 поставил(а) лайк

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Чт Апр 22, 2021 12:28 am

ГЛАВА 11. РАСШИРЕНИЕ КОМПИЛЯТОРА: ОПРЕДЕЛЯЮЩИЕ И КОМПИЛИРУЮЩИЕ СЛОВА

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

ЧТО ТАКОЕ ОПРЕДЕЛЯЮЩЕЕ СЛОВО?

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

:
VARIABLE
CONSTANT
CREATE

Все они обладают одним общим свойством: "определять" слова и добавляют новые имена к словарю. В отличие от других языков Форт позволяет создавать свои собственные определяющие слова. Для чего это нужно? Вообще говоря, определяющие слова способствуют хорошему разбиению программы. Мы уже неоднократно излагали вам концепцию разбиения (см. гл.8 ). Такая программа легко читается, легко воспринимается и легко исправляется.

Использование определяющих слов способствует хорошему разбиению, потому что дает возможность создавать целые КЛАССЫ, или СЕМЕЙСТВА, слов с похожими свойствами. Признаки, объединяющие члены некоторого семейства, задаются не в определении каждого члена, а в определяющем слове.

Прежде чем предложить вам пример, рассмотрим, как специфицируются определяющие слова. Основой всех определяющих слов является простейшее из них - слово CREATE [ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ ФИГ-ФОРТА. Не забудьте переопределить слово CREATE следующим образом:

: CREATE <BUILDS DOES> ;

]. Это слово выбирает из входного потока имя и создает для него в словаре заголовок.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b14010

Слово CREATE считается родителем, а слово ПРИМЕР - ребенком. Что делает ребенок в период выполнения? Он помещает свой собственный pfa в стек. А откуда он знает, что нужно делать именно это? Мы не задавали непосредственно ему никакой программы для выполнения. Ответ прост - ПРИМЕР вообще не содержит кода периода выполнения. Его указатель кода указывает на родителя (CREATE), у которого код периода выполнения есть.

Предположим, что в Форте нет слова VARIABLE. Мы можем его определить:

: VARIABLE CREATE 0 , ;

Мы обратились к слову CREATE внутри определения через двоеточие. Что при этом произойдет? Давайте проследим за выполнением в хронологическом порядке:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15110

: VARIABLE CREATE 0 , ;

Определяется определяющее слово VARIABLE

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b14810

VARIABLE АПЕЛЬСИНЫ

Исполняется слово VARIABLE, в свою очередь выполняя две функции:
- CREATE. Создает с помощью CREATE заголовок в словаре с именем АПЕЛЬСИНЫ и указателем кода, который ссылается на код периода выполнения слова CREATE;
- 0 , засылает 16-разрядный нуль в поле параметров вновь созданной переменной и выделяет ячейку памяти.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15210

АПЕЛЬСИНЫ

Исполняется слово АПЕЛЬСИНЫ. Так как указатель кода слова АПЕЛЬСИНЫ ссылается на код периода выполнения CREATE , pfa этого слова помещается в вершину стека. Конечно, мы могли бы обойтись без слова VARIABLE. Вполне достаточно ввести следующее:

CREATE АПЕЛЬСИНЫ 0 ,

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

Пример с определением слова VARIABLE демонстрирует лишь половину возможностей механизма определяющих слов. Если бы мы вместо VARIABLE воспользовались словом CREATE , то нам пришлось бы подкорректировать единственное место - в фазе 1, где происходит определение слова АПЕЛЬСИНЫ. И напротив, в фазе 3 слово АПЕЛЬСИНЫ вело бы себя одинаково при определении посредством как CREATE, так и VARIABLE.

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

: CONSTANT CREATE , DOES> @ ;

Здесь "собака зарыта" в выполнении слова DOES>, которое отмечает начало кода периода выполнения для всех своих детей. Как вы знаете, константы (т.е. дети определяющего слова CONSTANT) засылают в стек свои значения, которые хранятся в их поле параметров. Поэтому слово @, которое следует за DOES> , выбирает значение константы из ее собственного pfa.

В любом определяющем слове CREATE отмечает начало действий, выполняемых в период компиляции (фаза 2), a DOES> - конец действий периода компиляции и начало операций периода выполнения (фаза 3).

Проследим еще раз все наши действия:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15110

: CONSTANT CREATE , DOES> @ ;

Определение определяющего слова CONSTANT.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b14810

76 CONSTANT ТРОМБОНЫ

Исполнение слова CONSTANT, которое в свою очередь выполняет следующие три действия:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b16710

Создает с помощью CREATE заголовок словарной статьи с именем ТРОМБОНЫ. Выбирает из стека значение (например, 76) и заносит его в поле параметров константы. Устанавливает указатель поля кода слова ТРОМБОНЫ на код, следующий за словом DOES> .

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15210

ТРОМБОНЫ

Выполнение слова ТРОМБОНЫ. Поскольку указатель поля кода слова ТРОМБОНЫ теперь указывает код, следующий за DOES> , выбирается значение (76) и помещается в вершину стека. Обратите внимание на то, что в фазе 3 слово DOES> сначала помещает в вершину стека pfa ребенка. Иными словами, определение

: VARIABLE CREATE 0 , ;

эквивалентно следующему:

: VARIABLE CREATE 0 , DOES> ;

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

: CONSTANT ( n --) CREATE ,
DOES> ( -- n) @ ;

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

DOES> период-выполнения: ( -- a) Используется при создании определяющих слов. Отмечается конец участка периода компиляции и начала участка периода выполнения. Операции периода выполнения определены на высокоуровневом форте. Во время выполнения на стеке будет находиться pfa определенного слова.

ОПРЕДЕЛЯЮЩИЕ СЛОВА ВЫ МОЖЕТЕ СПЕЦИФИЦИРОВАТЬ САМИ

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

: 1ПРИЛАГАТЕЛЬНОЕ 10 CHOOSE 0 .БРЕД ;
: 2ПРИЛАГАТЕЛЬНОЕ 10 CHOOSE 1 .БРЕД ;
: СУЩЕСТВИТЕЛЬНОЕ 10 CHOOSE 2 .БРЕД ;

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

: ЧАСТЬ ( столбец# --) 10 CHOOSE SWAP .БРЕД ;
: 1ПРИЛАГАТЕЛЬНОЕ 0 ЧАСТЬ ;
: 2ПРИЛАГАТЕЛЬНОЕ 1 ЧАСТЬ ;
: СУЩЕСТВИТЕЛЬНОЕ 2 ЧАСТЬ ;

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

: ЧАСТЬ ( столбец# --) CREATE ,
DOES> @ 10 CHOOSE SWAP .БРЕД ;
0 ЧАСТЬ 1ПРИЛАГАТЕЛЬНОЕ
1 ЧАСТЬ 2ПРИЛАГАТЕЛЬНОЕ
2 ЧАСТЬ СУЩЕСТВИТЕЛЬНОЕ

Приведенное определение слова ЧАСТЬ аналогично определению слова CONSTANT [ДЛЯ ПУРИСТОВ. Иногда имеет смысл объединять существующие определяющие слова, если они выполняют большую часть тех функций (или все), которые должны выполняться во время компиляции. Так, выражение CREATE , с тем же эффектом можно заменить словом CONSTANT . Стандарт, однако, не рекомендует пользоваться такими приемами и не во всех системах это получится], с той лишь разницей, что в период выполнения помимо занесения в стек номера столбца оно также определяет номер строки посредством датчика случайных чисел и обращается к слову .БРЕД .

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

360 CONSTANT ВАСИЛЬЕВ
385 CONSTANT СИМОНЧИК
410 CONSTANT РЯБИНИНА
455 CONSTANT ИВАНОВА
460 CONSTANT ПЕТРОВА
485 CONSTANT ДЕМИН

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

: +СТУДЕНТ ( n -- n+25 n) DUP 25 + SWAP ;
360 \ Начало участка памяти, отведенного студентам
+СТУДЕНТ CONSTANT ВАСИЛЬЕВ
+СТУДЕНТ CONSTANT СИМОНЧИК
+СТУДЕНТ CONSTANT РЯБИНИНА
+СТУДЕНТ CONSTANT ИВАНОВА
+СТУДЕНТ CONSTANT ПЕТРОВА
+СТУДЕНТ CONSTANT ДЕМИН
. .( Конец участка памяти, отведенного студентам) CR

Последняя точка выбирает из стека номер блока.

Но и этот прием блекнет перед методом использования определяющих слов. Убедитесь сами:

: СТУДЕНТ ( n -- n+25) CREATE DUP , 25 +
DOES> ( -- n) @ ;
360 \ Начало участка, отведенного студентам
СТУДЕНТ ВАСИЛЬЕВ
СТУДЕНТ СИМОНЧИК
СТУДЕНТ РЯБИНИНА
СТУДЕНТ ИВАНОВА
СТУДЕНТ ПЕТРОВА
СТУДЕНТ ДЕМИН
. .( Конец участка, отведенного студентам) CR

Определяющее слово СТУДЕНТ и создает "константы", и управляет всеми вычислениями во время компиляции.

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

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

CREATE КЛАПАНЫ 30 ALLOT \ байтовый массив установки клапанов
: КЛАПАН ( i -- а) \ преобр. номера клапана в абсолютный адрес
КЛАПАНЫ + ;

Здесь слово КЛАПАН вычисляет индекс массива КЛАПАНЫ. Например, при выполнении выражения

6 КЛАПАН С@

будет опрошен гидравлический клапан 6.

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

: МАССИВ ( #байтов --) \ определение одномерного массива байтов
CREATE ALLOT
DOES> ( i -- a) + ;
30 МАССИВ КЛАПАН
6 КЛАПАН С@

Рассмотрим выполнение этого определения. В фазе 1 определяется слово МАССИВ. В фазе 2 исполняется МАССИВ, который в свою очередь обращается к слову CREATE (чтобы определить КЛАПАН) и слову ALLOT (для резервирования 30 байтов под массив). В фазе 3 исполняется слово КЛАПАН , инициируя код периода выполнения слова МАССИВ с добавлением индекса (6) к начальному адресу массива.

Если внести изменения в определение определяющего слова перед его повторной компиляцией, то тем самым можно изменить характеристики всех слов, входящих в данное семейство. Такая возможность значительно упрощает разработку программ. Например, когда нужно при описании массива заполнить его нулями, вы должны соответствующим образом создать определение МАССИВ. Сначала вы определяете слово, которое аналогично ALLOT , но "обнуляет" выделенный участок памяти:

: 0ALLOT ( #байтов --) HERE OVER ERASE ALLOT ;

Затем подставляете в определение МАССИВ вместо ALLOT слово 0ALLOT:

: МАССИВ ( #байтов --) \ определение одномерного массива байтов
CREATE 0ALLOT
DOES> ( i -- a) + ;

Можно также выделить в отдельный фрагмент некоторые отладочные процедуры, необходимые при разработке программы, а затем удалить их, предварительно убедившись в том, что она работает правильно. Ниже приводится вариант слова МАССИВ, где во время выполнения проверяется, не вышел ли индекс массива за установленные границы.

: МАССИВ ( #байтов --) CREATE DUP , ALLOT
DOES> ( i -- a) 2DUP @ U< NOT ABORT" Выход эа границу " + 2+ ;

Это происходит следующим образом:

DUP , ALLOT

Компиляция счетчика и выделение заданного количества байтов.

DOES> 2DUP @

По заданному на стеке индексу во время выполнения вычисляется: ( i pfa i # ).

U< NOT

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

ABORT" Выход за границу"

Аварийное завершение при выходе числа за диапазон.

+ 2+

В противном случае сложение индекса с pfa и добавление числа 2 для пропуска ячейки, содержащей счетчик.

Существует еще один способ использования определяющих слов, который помогает при создании программ. Допустим, вы вдруг решаете, что все ваши массивы, определенные с помощью слова МАССИВ , слишком велики, чтобы хранить их в памяти компьютера, и должны быть помещены на диск. Единственное, что вы должны в таком случае сделать, переопределить фрагмент периода выполнения в слове МАССИВ . Это новое определение вычислит номер блока, в котором содержится заданный байт, считает блок посредством BLOCK в некоторый буфер и оставит в вершине стека адрес требуемого байта относительно начала буфера. Массив, определенный подобным образом, может храниться в нескольких последовательных блоках (с использованием тех же средств, что и в упр.10.7).

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

: МАТРИЦА ( #строк #столбцов --) CREATE OVER , * ALLOT
DOES> ( строка столбец -- a) DUP @ ROT * + + 2+ ;

[ДЛЯ ЛЮБИТЕЛЕЙ ОПТИМИЗАЦИИ. Этот вариант будет выполняться еще быстрее

: МАТРИЦА ( #строк #столбцов --)
OVER CONSTANT HERE 2+ , * ALLOT
DOES> ( строка столбец -- а) 2@ ROT * + + ;

].

Для того чтобы создать массив размером 4*4 байта, вы должны написать:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b16910

Выбрать же, к примеру, байт в строке 2 и в столбце 1 можно следующим образом:

2 1 ТАБЛИЦА С@

Вот так кратко можно описать выполнение слова МАТРИЦА. Поскольку аппаратные средства компьютера позволяют хранить только одномерные массивы, второе измерение необходимо моделировать. Мы представляем себе, что наш массив выглядит следующим образом:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b14110

а на самом деле в памяти машины он хранится в виде

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b14210

Если вам требуется адрес байта, расположенного в строке 2 столбца 1, то вы можете умножить номер столбца (1) на число строк в каждом столбце (4), а затем прибавить номер строки (2). В результате получается, что вам нужен шестой байт машинного представления массива. Примерно такие вычисления делают в период выполнения элементы, составляющие слово МАТРИЦА. Но для того чтобы их производить, как вы можете заметить, любое составляющее слово должно "знать" число строк в каждом столбце конкретного массива. Для этих целей слово МАТРИЦА во время компиляции вносит число строк в столбце в начало массива. Для любознательных приведем стековые эффекты фрагмента периода выполнения слова МАТРИЦА:

ОПЕРАЦИЯСОДЕРЖИМОЕ СТЕКА
строка столбец pfa
DUP @строка столбец рfa #строк
ROTстрока pfa #строк столбец
*строка pfа индекс-столбца
+ +адрес
2+скорректированный-адрес

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

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

\ Шаблоны с использованием определяющих слов
: STAR 42 EMIT ;
: .РЯД ( b --) \ вывод звездочки на каждый бит из байта
CR 8 0 DO DUP 128 AND IF STAR ELSE SPACE THEN
2* LOOP DROP ;
: ФОРМА ( b1 b2 b3 b4 b5 b6 b7 b8 --)
\ определение формы из 8-строк
CREATE 8 0 DO С, LOOP
DOES> DUP 7 + DO I С@ .РЯД -1 +LOOP CR ;

\ формы:
HEX
18 18 3C 5A 99 24 24 24 ФОРМА ЧЕЛОВЕК
81 42 24 18 18 24 42 81 ФОРМА КОНЬ
AA AA FE FE 38 38 38 FE ФОРМА ЗАМОК
DECIMAL

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

2 BASE ! _ok
00111001 .РЯД
___***__*ok
DECIMAL _ok

Наше определяющее слово ФОРМА берет из стека восемь аргументов и определяет шаблон, который при своем выполнении выводит решетку 8*8 элементов, соответствующую этим восьми аргументам:

_ЧЕЛОВЕК
____**___
____**___
___****__
__*_**_*_
_*__**__*
___*__*__
___*__*__
___*__*__
_ok

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

ЧТО ТАКОЕ КОМПИЛИРУЮЩЕЕ СЛОВО?

Если обычные слова Форта появляются внутри определения через двоеточие, то они компилируются в словарь. Эти слова во время компиляции ведут себя пассивно. КОМПИЛИРУЮЩИЕ СЛОВА также появляются внутри определения через двоеточие, но в противоположность первым активно влияют на процесс компиляции. Такие слова, как IF , THEN , BEGIN , REPEAT и ." являются компилирующими.

Создатели Форта проявили последовательность и в данном случае. Коль уж существует тенденция не включать в язык все возможные определяющие слова, то компилирующих операторов в самом языке немного. Возможность управлять компиляцией путем образования собственных компилирующих слов предоставляет вам такую свободу, какую не может обеспечить ни один из известных языков программирования. Это средство позволяет локализовать информацию внутри соответствующих определений (не рассредоточивая ее по всей программе), что упрощает написание программы, облегчает ее чтение, восприятие и сопровождение. Вероятно, ваша программа будет выглядеть более привлекательной, если в язык включить оператор выбора вариантов, который сравнивает текстовые фрагменты. А, может быть, вы хотели бы добавить к вашим операторам управления средства жесткого аварийного контроля? Не исключено, что вам захочется иметь оператор цикла DO , использующий 32-разрядный индекс. Все это в ваших силах.

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

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

Компилятор двоеточия функционирует аналогично текстовому интерпретатору. Он выбирает из входного потока слова и пытается отыскать их в словаре. Однако, вместо того чтобы (как ИНТЕРПРЕТАТОР) исполнять эти слова немедленно, он, как правило, компилирует их адреса в словарь. Но компилятор распознает компилирующие слова и только их исполняет сразу, подобно текстовому интерпретатору.

Каким образом компилятор двоеточия отличает компилирующие слова? По биту немедленного исполнения данного определения (гл.9 "Структура словарной статьи"): если бит сброшен, то компилируется адрес слова, если установлен, что слово немедленно исполняется. Такие слова называются СЛОВАМИ НЕМЕДЛЕННОГО ИСПОЛНЕНИЯ (immediate).

Слово IMMEDIATE делает слово немедленно исполняемым. Его формат:

: имя определение ; IMMEDIATE

т.е. это слово выполняется сразу после компиляции определения. Допустим, у нас есть определение:

: ТЕСТ ; IMMEDIATE

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

: 2CRS CR ТЕСТ CR ;

то будет скомпилирован следующий фрагмент словаря:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b14310

Как видите, определение скомпилировано без слова ТЕСТ. На самом деле оно выполнено во время компиляции слова 2CRS . Поскольку слово ТЕСТ ничего не выполняет, оно бесполезно. Приведем другой пример. Предположим, что у нас есть слово с именем ТЮЛЬПАН и определение:

: ТЕСТ COMPILE ТЮЛЬПАН ; IMMEDIATE

Теперь переопределим слово 2CRS точно так же, как и ранее:

: 2CRS CR ТЕСТ CR ;

и получим следующий результат:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b14410

На сей раз слово ТЕСТ во время компиляции определения 2CRS скомпилировало адрес слова ТЮЛЬПАН. На самом деле мы нашим определением как бы сказали:

: 2CRS CR ТЮЛЬПАН CR ;

и что компилировать, а что нет, определяет ТЕСТ, потому что это слово немедленного исполнения.

Обратите внимание на слово COMPILE (КОМПИЛЯЦИЯ). Мы ввели его как бы между прочим, поскольку его функции проще понять в контексте.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b14510

: ТЕСТ COMPILE ТЮЛЬПАН ; IMMEDIATE

COMPILE вычисляет адрес следующего слова определения и запоминает его в виде числа:

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b14610

: 2CRS CR ТЕСТ CR ;

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

Приведем теперь очень полезный пример. Допустим, вы написали отладочное средство с именем ОТЛАДКА , которое хотите использовать в любом месте своей программы. Так как желательно иметь возможность при необходимости включать и отключать это средство, воспользуемся словом ТЕСТ, определенным следующим образом:

: ТЕСТ ПРОВЕРКА? IF ОТЛАДКА THEN ;

Слово ТЕСТ при выполнении проверяет флаг и определяет, обращаться к слову ОТЛАДКА или нет. Казалось бы, все хорошо, однако много времени уходит на остановку для проверки флага на каждом шаге цикла. Если вы не работаете в отладочном режиме, вам вряд ли нужно всякий раз проверять режим. Проблема решается путем переопределения слова ТЕСТ:

: ТЕСТ ПРОВЕРКА? IF COMPILE ОТЛАДКА THEN ; IMMEDIATE

В такой ситуации для включения отладочного средства придется перекомпилировать программу, но это займет немного времени. В отладочном режиме слово ТЕСТ скомпилирует ОТЛАДКА в соответствующие места программы. В противном случае оно вообще ничего компилировать не будет. Проверка IF будет осуществляться в период компиляции.

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15110

: dot" R> COUNT 2DUP + >R TYPE ;
: ." COMPILE dot" ASCII " STRING ; IMMEDIATE

Определение dot" и ." .

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b14810

: ВСТРЕЧА ." Эй, ты " ;

Исполнение ." , которое является словом немедленного выполнения (и поэтому исполняется во время компиляции слова ВСТРЕЧА). Оно в свою очередь осуществляет компиляцию:
- адреса слова dot" в определение слова ВСТРЕЧА:
-- ВСТРЕЧА
-- Поле связи
-- Поле кода
-- dot"
-- ...
- строки, ограниченной двойной кавычкой как строки со счетчиком:
-- ВСТРЕЧА
-- Поле связи
-- Поле кода
-- dot"
-- 7 Э Й , _ Т Ы
-- ...

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15210

ВСТРЕЧА

Выполнение слова ВСТРЕЧА, которое вызывает слово dot" , а оно уже выводит строку на экран [ДЛЯ ОЧЕНЬ ЛЮБОЗНАТЕЛЬНЫХ. Слово dot" начинает свое выполнение с того, что получает адрес стека возвратов (в нашем случае - адрес строки со счетчиком). COUNT преобразует этот адрес отдельно в адрес и значение счетчика для TYPE. Но мы должны привести в порядок указатель стека возвратов, чтобы он при возврате показывал бы за строку. Адрес мы вычисляем путем сложения копий адреса и счетчика, полученных с помощью 2DUP , а затем подкорректированный адрес засылаем в стек возвратов. (Если у вас есть листинги исходных текстов, то, прежде чем экспериментировать, проверьте определение слова ." )].

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

IMMEDIATE ( --) Последнее определенное слово становится немедленно исполняемым, то есть во время компиляции оно будет не компилироваться, а выполняться.
C0MPILE xxx ( --) Применяется при определении компилирующего слова. Когда это компилирующее слово будет в свою очередь использоваться в исходном определении, адрес поля кода xxx будет скомпилирован в словарную статью, так что когда вновь созданное определение выполняется, выполняется и xxx.
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Чт Апр 22, 2021 12:49 am

НЕСКОЛЬКО ДОПОЛНИТЕЛЬНЫХ СЛОВ УПРАВЛЕНИЯ КОМПИЛЯЦИИ

Как вы помните, число, которое появляется в определении через двоеточие, называется литералом (самоопределенным). Например, число 4 в следующем определении является литералом:

: ПЛЮС-ЧЕТЫРЕ 4 + ;

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15310

Использование литерала в определении через двоеточие требует двух ячеек. В первой содержится адрес некоторой программы, которая, отработав, поместит содержимое второй ячейки (само число) в вершину стека [Для борющихся за экономию памяти. В то время как изображение литерала требует двух ячеек, ссылка на константу занимает только одну ячейку. Таким образом, посредством определения чисел как констант можно экономить память в тех случаях, когда вы используете это число в программе достаточное количество раз, чтобы получаемая экономия перекрыла расходы на создание заголовка константы. Разницу во время выполнения константы и литерала заметить трудно]. Имя этой процедуры может меняться. Назовем ее КОДОМ ПЕРИОДА ВЫПОЛНЕНИЯ ДЛЯ ЛИТЕРАЛА, или просто (LITERAL). Компилятор, встречая некоторое число, сначала компилирует код периода выполнения для литерала, а затем само число.

Слово LITERAL (ЛИТЕРАЛ) вы наиболее часто будете употреблять при компиляции литерала. Это слово компилирует как код периода выполнения, так и само значение, например:

4
: ПЛЮС-ЧЕТЫРЕ ( n -- n+4) LITERAL + ;

[ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ, НЕ ДОПУСКАЮЩИХ ТАКИХ ДЕЙСТВИЙ. В некоторых Форт-системах при обработке двоеточия запоминается указатель стека данных, а при обработке точки с запятой проверяется соответствие текущего указателя стека и запомненного. Смысл этой проверки заключается в том, чтобы программист не допускал грубых ошибок. После компиляции определения указатель стека должен совпадать с указателем до компиляции. К сожалению, подобный механизм препятствует передаче аргументов слову LITERAL . Если ваша система при попытке воспользоваться описанным приемом аварийно завершит работу, "обманите" ее следующим образом:

: ИЗВНЕ ( литерал -- мусор) 0 SWAP ; IMMEDIATE
4 : ПЛЮС-ЧЕТЫРЕ ИЗВНЕ LITERAL + ; DROP

]

Здесь слово LITERAL занесет число 4, помещенное в вершину стека перед компиляцией, в элемент словаря как литерал. В результате мы получим элемент словаря, идентичный показанному на приведенном выше рисунке.

Можно привести пример более интересного применения слова LITERAL . Вспомните, что в гл.8 был образован массив с именем ПРЕДЕЛЫ , состоящий из пяти ячеек, в которых хранятся значения предельной температуры для соответствующих горелок. Чтобы упростить доступ к массиву, мы создали слово с именем ПРЕДЕЛ. Эти два определения выглядели следующим образом:

CREATE ПРЕДЕЛЫ 10 ALLOT
\ массив из 5 ячеек, содерж.предел.знач.
: ПРЕДЕЛ ( #горелки -- адрес-пред-знач) 2* ПРЕДЕЛЫ + ;

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

HERE 10 ALLOT \ массив из 5 предельных значений
: ПРЕДЕЛ ( #горелки -- адр-пред-знач) 2* LITERAL + ;

В первой строке мы помещаем в вершину стека адрес начала массива (HERE), а во второй - заносим этот адрес как литерал в определение слова ПРЕДЕЛ. Таким образом, мы ликвидировали заголовок слова ПРЕДЕЛЫ и сэкономили память словаря.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15410

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

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

180 BLOCK 3 64 * +

но слишком много времени будет занимать всякий раз при использовании этого определения выполнение фразы: 3 64 * . В качестве альтернативы можно записать следующее:

180 BLOCK 192 +

однако трудно сразу сообразить, что означает здесь 192. Лучшим решением является такое выражение:

180 BLOCK [ 3 64 * ] LITERAL +

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15510

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

: НАПЕЧАТАЙ-ЭТО [ BLK @ ] LITERAL LIST ;

При исполнении слова ПЕЧАТЬ выводится тот блок, в котором оно определено. (Во время компиляции в BLK содержится номер последнего загруженного блока, LITERAL заносит этот номер в определение как литерал, так что во время выполнения последней будет служить аргументом для LIST ).

Здесь уместно дать определение LITERAL:

: LITERAL ( n -- ) COMPILE (LITERAL) , ; IMMEDIATE

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

Следующее слово для управления компиляцией - [COMPILE] . Допустим, вы хотите переименовать слово IF, но делать это так, как показано ниже:

: если IF ; IMMEDIATE

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

: если [COMPILE] IF ; IMMEDIATE
: иначе [COMPILE] ELSE ; IMMEDIATE
: то [COMPILE] THEN ; IMMEDIATE

то у вас появится возможность по-новому записывать условия:

: ветвление ( ? --) если ." Истина " иначе ." Ложь "
то ." флаг" ;

Вас может удивить, почему нельзя использовать слово COMPILE непосредственно:

: если COMPILE IF ; IMMEDIATE

Вспомните, что COMPILE осуществляет так называемую отсроченную компиляцию, т.е. компилирует IF не в то слово, которое мы имеем в виду, а в то, которое инициирует это слово (например, "ветвление"). С другой стороны, слово [COMPILE] компилирует слова немедленного выполнения в традиционном смысле, иначе они бы исполнялись. Между прочим квадратные скобки в слове [COMPILE] означают, что данное слово "выполняется во время компиляции" - таково еще одно соглашение об именовании в Форте. Вы можете смоделировать слово [COMPILE] следующим образом:

: если [ ' IF , ] ; IMMEDIATE

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

Теперь наступает для вас час испытаний. Если вы его переживете, можете считать себя специалистом по компилирующим словам. Предположим, у нас имеются слова НЕГАТИВНОЕ и -НЕГАТИВНОЕ, которые изменяют обычное изображение на экране негативным и, наоборот, негативное обычным соответственно для любого правильного текста. Наша цель - создать слово Н." для автоматического изменения режима экрана на негативный, вывода строки и возвращения к нормальному режиму.

Существуют два решения этой задачи и оба они представляют интерес. Для начала условимся, что изменение режима должно осуществляться тогда, когда строка ВЫВОДИТСЯ, А НЕ КОМПИЛИРУЕТСЯ. Первое решение заключается в создании слова с именем нточка" , аналогично слову dot", и слово Н." , которое имитирует ." , но вместо слова dot" компилирует слово нточка" :

: dot" R> COUNT 2DUP + >R TYPE ;
: ." COMPILE dot" ASCII " WORD C@ 1+ ALLOT ; IMMEDIATE
: нточка" НЕГАТИВНОЕ R> COUNT 2DUP + >R TYPE -НЕГАТИВНОЕ ;
: H." COMPILE нточка" ASCII " WORD С@ l+ ALLOT ; IMMEDIATE

Но это решение далеко не изящно и зависит от реализации. Другой вариант - вызов слова ." :

: H." COMPILE НЕГАТИВНОЕ [СОМРILE] ."
COMPILE -НЕГАТИВНОЕ ; IMMEDIATE

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

: ТЕСТ H." Ура!" ;

то компилируется следующий фрагмент:
- ТЕСТ
- Поле связи
- Поле кода
- НЕГАТИВНОЕ
- dot"
- 4 У р а !
- -НЕГАТИВНОЕ
- EXIT

Наше компилирующее слово выполняет три функции:
- компилирует адрес слова НЕГАТИВНОЕ в ТЕСТ (так что НЕГАТИВНОЕ будет выполняться в период исполнения слова ТЕСТ );
- инициирует слово ." , которое в свою очередь компилирует dot" , и компилирует фрагмент, набранный в строке;
- компилирует адрес слова -НЕГАТИВНОЕ.

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

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

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

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

LITERAL период-компиляции: ( n --) период-выполнения: ( -- n) Используется только внутри определения через двоеточие. Во время компиляции значение из стека компилируется как литерал в определение. Во время выполнения это значение будет помещено на стек.
[ ( -- ) Переключение с режима компиляции на режим интерпретации.
] ( -- ) Переключение на режим компиляции.
[COMPILE] xxx ( -- ) При использовании внутри определения через двоеточие вызывает компиляции слова немедленного исполнения xxx, как если бы оно не было словом немедленного исполнения, xxx будет выполняться при выполнении данного определения.

ПОЛЕЗНЫЙ ПРИЕМ. Ввод с клавиатуры длинных определений. В некоторых Форт-системах нет возможности вводить с клавиатуры определения, состоящие из нескольких строк, потому что при нажатии клавиши RETURN работа в режиме компиляции прекращается. Выход из такого положения - инициировать компилятор в начале каждой строки:

: ?ОБЪЕМ ( длина ширина высота -- )<return>
] 6 > ROT 22 > ROT 19 > AND AND<return>
] IF ." Подходит " THEN ;<return>_ok

ФЛАГ СОСТОЯНИЯ

Введем последний термин, имеющий отношение к процессу компиляции,- СОСТОЯНИЕ. В большинстве Форт-систем есть переменная с именем STATE (СОСТОЯНИЕ), в которой содержится "истина", если вы работаете в режиме компиляции, и "ложь", если вы работаете в режиме интерпретации. Покажем способ вывода значения переменной STATE:

: .СОСТОЯНИЕ STATE ? ; IMMEDIATE

Введите:

.СОСТОЯНИЕ _0_ok

В момент вызова .СОСТОЯНИЕ Форт-система находилась в режиме интерпретации (то, что .СОСТОЯНИЕ является словом немедленного исполнения, не играет роли. Интерпретатор не проверяет бит немедленного исполнения). Теперь вызовите слово .СОСТОЯНИЕ из нового определения:

: ТЕСТ .СОСТОЯНИЕ ; _-1_ok

На сей раз значение STATE равно "истине", поскольку .СОСТОЯНИЕ было инициировано компилятором.

В каких случаях возникает необходимость знать состояние? Всегда, когда вы хотите создать слово, которое должно "ДЕЛАТЬ ВИД", что его поведение одинаково как внутри определения, так и вне его, а на самом деле оно проявляет себя по-разному. В качестве примера можно привести слово ASCII. При обычном использовании это слово появляется внутри определения через двоеточие:

: ТЕСТ ( -- ascii-a) ASCII A ;

При ИСПОЛНЕНИИ слова ТЕСТ в вершину стека вносится число 65. ASCII осуществляет преобразование во время компиляции. Оно должно быть компилирующим словом: выбирать из входного потока символ, компилировать его как литерал, чтобы последний мог быть занесен в стек во время выполнения слова ТЕСТ. Вы можете создать компилирующий вариант слова ASCII следующим образом:

: ASCII ( -- с)
\ Компиляция: с ( --)
BL WORD 1+ С@ [COMPILE] LITERAL ; IMMEDIATE

(ПРИМЕЧАНИЕ. Стековый комментарий в первой строке определяет поведение слова во время выполнения - это синтаксис использования слова. Комментарий во второй строке показывает, что должно произойти во время компиляции, в частности должно быть выполнено считывание символа из входного потока и ничего не оставлено в стеке).

В приведенном выше определении слово WORD выбирает из входного потока текст, ограниченный пробелом, и вносит в вершину стека адрес участка памяти, где будет храниться строка со счетчиком. Мы считаем, что такой текст содержит только один символ, поэтому пропускаем счетчик байтов посредством 1+ и выбираем значение с помощью С@ . Затем инициируем слово LITERAL, компилирующее код периода выполнения для литерала, за которым следует само значение. Это все, что нам требуется.

Попытаемся заставить ASCII выполняться ВНЕ ОПРЕДЕЛЕНИЯ. Например, выражение

ASCII A

внесет в вершину стека значение 65 (что может оказаться полезным при составлении таблиц и т.д.). Далее "попросим" ASCII выполнить что-нибудь такое, что оно еще не делало. Создадим следующий вариант определения:

: ASCII ( -- с) BL WORD 1+ C@ ;

Для того чтобы одно и то же слово ASCII могло выполняться в обоих вариантах, оно должно реагировать на состояние

: ASCII ( -- c)
\ Компиляция: c ( -- )
\ Интерпретация: с ( -- с)
BL WORD 1+ С@
STATE @ IF [COMPILE] LITERAL THEN ; IMMEDIATE

Такое слово называется ЗАВИСИМЫМ ОТ СОСТОЯНИЯ. На первый взгляд зависимость от состояния кажется свойством разумным и даже желательным, особенно для слова ASCII, которое используется только при компиляции. Однако когда программист пытается повторно применять зависимые слова в другом определении и хочет, чтобы они выполнялись обычным образом, возникают трудности. Приведем пример использования компилирующего слова, зависимого от состояния, в определении другого компилирующего слова. По мере того как растет глубина вложенности слова, зависимого от состояния, программисту приходится затрачивать все больше усилий на то, чтобы держать под контролем, что и где должно выполняться.

Применение простых слов безопаснее, чем зависимых от состояния, так как их поведение предсказуемо. В некоторых ранее созданных системах слово ." зависело от состояния. С введением Стандарта-83 оно было разделено на два отдельных слова, управляющих использованием во время компиляции - ." - и в период выполнения во время интерпретации блоков при их загрузке - .( .

Слово ' постигла участь описанных выше слов и теперь это простое слово. Его функции внутри определения аналогичны функциям, выполняемым во время интерпретации: оно выявляет определение, имя которого находится во входном потоке во время исполнения. Вариант апострофа, функционирующего как компилирующее слово, называется ['] .

Некоторые разработчики Форта считают, что зависимость от состояния не имеет права на существование. Применительно к ASCII возможны два решения: 1) разрешить его использование только внутри определения и, если возникает необходимость, создать слово с другими функциями, скажем ascii (на нижнем регистре), для режима интерпретации; 2) сделать его пригодным только для интерпретации:

: ТЕСТ [ ASCII A ] LITERAL ;

Можно договориться и ввести другое слово:

: [ASCII] ASCII [COMPILE] LITERAL ; IMMEDIATE

которое рекомендуется применять следующим образом:

: ТЕСТ [ASCII] A ;

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

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

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

оператор
следующий оператор
следующий оператор

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

условие IF истина ELSE ложь THEN оператор

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15610

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15710

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15810

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15910

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

ЗАКЛЮЧЕНИЕ

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

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

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

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

: ТЕСТ 0 BEGIN DUP . 1+ QUERY INTERPRET 0 UNTIL ;

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b16010

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b16110

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

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

Итак, существуют два способа расширения Форт-компилятора:
- добавление новых, специализированных компиляторов посредством создания новых определяющих слов;
- расширение имеющегося компилятора посредством создания новых компилирующих слов.

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b16310

СЛОВА

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

DOES> период-выполнения: ( -- a) Используется при создании определяющих слов. Отмечается конец участка периода компиляции и начала участка периода выполнения. Операции периода выполнения определены на высокоуровневом форте. Во время выполнения на стеке будет находиться pfa определенного слова.
IMMEDIATE ( --) Последнее определенное слово становится немедленно исполняемым, то есть во время компиляции оно будет не компилироваться, а выполняться.
COMPILE xxx ( --) Применяется при определении компилирующего слова. Когда это компилирующее слово будет в свою очередь использоваться в исходном определении, адрес поля кода xxx будет скомпилирован в словарную статью, так что когда вновь созданное определение выполняется, выполняется и xxx.
LITERAL период-компиляции: ( n --) период-выполнения: ( -- n) Используется только внутри определения через двоеточие. Во время компиляции значение из стека компилируется как литерал в определение. Во время выполнения это значение будет помещено на стек.
[ ( --) Переключение с режима компиляции на режим интерпретации.
] ( --) Переключение на режим компиляции.
[COMPILE] xxx ( --) При использовании внутри определения через двоеточие вызывает компиляции слова немедленного исполнения xxx, как если бы оно не было словом немедленного исполнения, xxx будет выполняться при выполнении данного определения.
STATE ( -- a) Переменная, содержащая флаг состояния системы: истину при компиляции и ложь при интерпретации.
INTERPRET Интерпретация текста из входного потока по указателю >IN до исчерпания входного потока.

ОСНОВНЫЕ ТЕРМИНЫ

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

УПРАЖНЕНИЯ

1. Введите определяющее слово с именем ЗАГРУЗКА, которое будет определять слова, загружающие при своем исполнении некоторый блок. Например, выражение

690 ЗАГРУЗКА КОРРЕСПОНДЕНЦИЯ

должно определить слово КОРРЕСПОНДЕНЦИЯ. В результате выполнения последнего должен быть загружен блок 600.

2. Определите слово с именем СИСТЕМА. , создающее слова для вывода чисел в конкретных системах счисления. Например, выражение

16 СИСТЕМА. Н.

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

DECIMAL
17 DUP Н. .<return> _11_17_ok

3. Определите слово с именем МНОГО, чтобы оно получало адрес некоторого слова, например CR или STAR, и создавало множественное число этого слова, в нашем случае CRS или STARS. Вы будете задавать адрес слова в единственном числе посредством апострофа. Например, фраза

' CR МНОГО CRS

определит слово CRS так же, как и выражение

: CRS ?DUP IF 0 DO CR LOOP THEN ;

4. На французский язык слова DO и LOOP переводятся как TOURNE и RETOURNE соответственно. Определите TOURNE и RETOURNE как французские имена слов управления циклом, используя слова DO и LOOP, после чего проверьте их, написав цикл на французском языке.

5. Напишите слово с именем РАЗ, которое вызовет исполнение оставшихся во входном потоке символов до возврата каретки. Число исполнений задается числом, находящимся в вершине стека, например:

7 РАЗ 42 EMIT SPACE<return>_*_*_*_*_*_*_*_ok

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

ЛИТЕРАТУРА

1. "Add a Break Point Tool," Forth Dimensions, Vol.V, No.1, p.19.

ЗАМЕЧАНИЕ

Вот она - расплата за легкость главы 8. Оказывается, Forth совсем не такой умный, что понимает любые наши капризы. Вместо этого, он просто позволяет нам копаться в своем компиляторе. Не удивительно, что существует огромное количество фортеров, не понявших этой главы, и предпочитающих писать на Forth в стиле языка С.
G.
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

vikt144 поставил(а) лайк

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Пт Апр 23, 2021 12:35 am

ГЛАВА 12. ТРИ С ПОЛОВИНОЙ ПРИМЕРА

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

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

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

ОТКАЧКА ФАЙЛА

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

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

Для каждого человека отводится некоторая ЗАПИСЬ, которая состоит из четырех ПОЛЕЙ с именами: фамилия, имя, работа, телефон.

ПОИСК ИНФОРМАЦИИ. Вы можете просматривать файл в поисках содержимого какого-либо поля, используя слово НАЙТИ, за которым должны следовать имя поля и его содержимое:

найти работа диктор<return>_Дан_Рэйвер_ok

Если в поле "работа" содержится строка "диктор", то система выведет фамилию диктора. При отсутствии файла с такими атрибутами система выдаст сообщение: "Сведений нет". В том случае, когда поле с искомыми атрибутами найдено, запись с соответствующей информацией становится ТЕКУЩЕЙ. Вы можете вывести содержимое любого поля текущей записи с помощью слова "дать". Например, если вы ввели упомянутую выше строку, то теперь можете написать:

дать телефон<return>_555-9876_ok

Команда "найти" применяется только для поиска ПЕРВОГО поля с указанными атрибутами. Для того чтобы добраться до следующего такого поля, воспользуйтесь командой "еще". В частности, чтобы найти еще одного диктора, нужно ввести

еще<return>_Конни_Чанг_ok

а затем

еще<return>_Франк_Рейнольдз_ok

Если в данном файле больше нет сведений о дикторах, то при посылке команды "еще" вы получите сообщение: "Больше нет", т.е. полей с такими атрибутами в файле не осталось.

Для получения списка лиц, у которых в соответствующем поле информация совпадает с атрибутами, применявшимися при последнем поиске, введите команду "все":

все
_Дан_Рэйэер
_Конни_Чанг
_Фрэнк_Рейнольда
_ok

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

фио Уандэр,Стив<return>_Стив_Уандэр_ok

(после запятой не должно быть пробела, поскольку запятая отмечает конец первого поля и начало второго). Подобно командам "найти" и "еще" слово "фио" выводит найденное имя.
С помощью слова "пара" вы можете осуществить поиск ЛЮБОЙ ПАРЫ полей, для чего необходимо задать имена обоих полей и их содержимое, разделив операнды запятой. В частности, чтобы найти некоего комментатора по имени Дан, вы должны ввести:

пара работа диктор,имя Дан<return>_Дан_Рэйвер_ok

СОПРОВОЖДЕНИЕ ФАЙЛОВ. Если вам требуется ввести новую запись, вы должны применить команду "внести", разместив за ней операнды: фамилия, имя, место работы, телефон, причем операнды отделяются только запятой, например:

внести Нуреев,Рудольф,танцовщик балета,355-1234<return>_ok

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

изменить работа хореограф<return>_ok

Для удаления текущей записи вы должны ввести команду "удалить":

удалитъ<return>_ok

После того как вы что-то добавляли к записям, модифицировали их либо удаляли, обязательно введите слово FLUSH, если собираетесь отключить компьютер или сменить диск:

FLUSH_ok

ПРОГРАММИСТУ О СТРУКТУРЕ ПРИКЛАДНОЙ ПРОГРАММЫ

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

Итак, обратимся к листингу. Обратите внимание на то, что вид данной программы несколько отличается от всех остальных, показанных в книге. Здесь использованы соглашения, принятые в фирме FORTH, Inc. Блоки, расположенные на левой стороне разворота (у меня - первыми в парах.- G.], называются БЛОКАМИ СОПРОВОЖДЕНИЯ. Они содержат комментарий к исходному тексту блоков на правой стороне разворота. Например, определение слова HELP располагается в строке 1 блока 240, а комментарий к этому слову - в строке 1 блока 561. Наличие блоков сопровождения имеет очевидные преимущества. Прежде всего документация всегда находится вместе с программой. В большинстве систем есть слово, которое обеспечивает связь между исходным текстом блока и комментарием к блоку в обе стороны (в полиФорте Q). Программист получает возможность в ходе разработки программы создавать документацию и вносить в нее изменения. С помощью слов LOCATE и Q можно сделать доступным описание, относящееся к любому слову.

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

Слово HELP определено в блоке 240. Оно выводит текст БЛОКА ПОДСКАЗОК (блок 561), который содержит все команды уровня пользователя. Блок подсказок появляется при загрузке блока 240 (так что он доступен пользователю при компиляции программы) или при вводе пользователем слова HELP.

Выражение 241 243 THRU в строке 2 блока 240 загружает фрагмент программы более низкого уровня. Остальные операторы блока загружают слова самого высокого уровня. Помещая группу команд в блок загрузки, а не в последний блок, пользователь тем самым помещает комментарий к этим командам в блок сопровождения блока загрузки. Обратите внимание на девять команд пользователя в блоке 240. Как просты их определения, несмотря на то, что они реализуют очень мощные функции.

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

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b14710

Например, поле "работа" начинается с 28-го байта каждой записи, а его длина составляет 24 байта. Мы вправе сделать длину записи равной в точности 64 байтам, чтобы при распечатке файла посредством LIST столбцы были бы выровнены по вертикали. Такая структура записи выбрана из соображений удобства при программировании именно нашей задачи. Если вы внесете изменения в программу, то можете сделать записи нужной вам длины и состоящими из требуемого числа полей [ДЛЯ ТЕХ, КТО ХОЧЕТ МОДИФИЦИРОВАТЬ НАШУ ФАЙЛОВУЮ СИСТЕМУ. При изменении параметров полей убедитесь, что начальный байт каждого последующего поля правильно стыкуется с предыдущим полем. Например, если первое поле имеет длину 30 байт после такого выражения

CREATE 1ПОЛЕ 0 , 30 ,

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

CREATE 2ПОЛЕ 30 , 12 ,

и т.д. Установите значение /ЗАПИСЬ равным длине всей записи (начальный байт последнего поля плюс длина поля). Используя /ЗАПИСЬ, система автоматически подсчитывает число записей, которые она может разместить в одном блоке ( 1024 /ЗАПИСЬ / ), и определяет соответствующую константу ЗАП/БЛК (см также упражнение в конце главы).

Вы можете изменить расположение вновь созданного файла (например, создать несколько различных файлов) путем изменения в строке 5 значения константы ФАЙЛ . Можно изменить и максимальное число блоков, отводимых под ваш файл, заменив число 2 на другое в той же строке. Это значение будет переведено в максимальное число записей путем умножения его на значение, содержащееся в ЗАП/БЛК , и храниться в виде константы МАКС-ЗАП ].

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

CREATE работа 28 , 24 ,

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b16210

Таким образом, при вводе имени поля в вершину стека вносится адрес таблицы, описывающей поле "работа", по которому мы можем выбрать любую из двух характеризующих его величин. Назовем каждый такой элемент ТАБЛИЦЕЙ СПЕЦИФИКАЦИИ ПОЛЯ, или для краткости ТАБЛИЦЕЙ ПОЛЕЙ.

Часть функций нашей программы обусловлена требованиями, предъявляемыми командами "найти", "еще" и "все", т.е. по команде "найти" должен не только осуществляться поиск заданной строки в содержимом полей данного типа, но и "запоминаться" и сама строка, и тип поля, чтобы команды "все" и "еще" смогли бы воспользоваться этой информацией. Можно указать тип поля только одним значением - адресом таблицы полей данного типа. Это означает, что мы можем "запомнить" тип поля, послав его адрес в переменную с помощью слова ЗАПОМНИТЬ . Переменная ТИП служит для обозначения типа поля.

Для того чтобы запомнить СТРОКУ, мы определили буфер с именем ЧТО , куда строка может быть помещена. (Память для буфера определяется в рабочей области PAD , где она может повторно использоваться, и при этом не расходуется память, выделенная под словарь).

Слово ЗАПОМНИТЬ имеет два назначения: запоминать тип заданного поля в ТИП и заданную строку символов в ЧТО. Если вы посмотрите на слово "найти", обращенное к конечному пользователю, то заметите, что оно в первую очередь посредством слова ЗАПОМНИТЬ запоминает информацию, по которой должен осуществляться поиск, после чего исполняет внутреннее слово -НАЙТИ , осуществляющее по информации, хранимой в ТИП и ЧТО, поиск аналогичной строки. Слова "еще" и "все" тоже используют слово -НАЙТИ, но без слова ЗАПОМНИТЬ. Они осуществляют поиск полей по содержимому, которое было запомнено командой ЗАПОМНИТЬ при последнем применении команды "найти".

Так как с помощью слова "дать" можно получить любую информацию из записи, которую мы уже нашли, применив команду "найти", нам нужен указатель текущей записи. Таким указателем служит переменная ЗАПИСЬ#. Операции, выполняемые в блоке 242 словами ВВЕРХ и ВНИЗ, должны показаться вам тривиальными.

Слово ЗАПИСЬ использует переменную ЗАПИСЬ# для вычисления абсолютного адреса (машинного адреса в буфере на каком-то диске) начала текущей записи. Поскольку ЗАПИСЬ применяет слово BLOCK, оно гарантирует, что данная запись действительно СУЩЕСТВУЕТ в буфере.

Обратите внимание на то, что ЗАПИСЬ допускает расположение одного файла в нескольких смежных блоках. /MOD делит значение, находящееся в переменной ЗАПИСЬ# , на число записей в одном блоке (в нашем случае 16, так как каждая запись имеет длину в 64 байта). Частное указывает тот блок относительно первого блока, где должна находиться обрабатываемая запись, а остаток определяет смещение этой записи относительно начала вычисленного блока.

В таблице полей содержатся ОТНОСИТЕЛЬНЫЕ значения адреса поля и длины. Однако нам для таких слов, как TYPE , MOVE и -TEXT , часто требуется знать абсолютные адрес и длину. Поэтому покажем, как в определении слова ПОЛЕ адрес таблицы полей преобразуется в абсолютные адрес и длину, а затем как в определении слова .ПОЛЕ используется слово ПОЛЕ .

Слово ПОМЕСТИТЬ с помощью слова ПОЛЕ вычисляет адрес и счетчик поля назначения для слова ЧТЕНИЕ . Последнее выбирает из входного потока строку, ограниченную запятой, и помещает эту строку в поле назначения. Заметьте, что ЧТЕНИЕ инициирует слово ПОДРОВНЯТЬ, которое введено для урезания числе символов перекачиваемой строки в том случае, если источник превышает поле назначения.

В определении слова ПУСТАЯ в блоке 243 обращают на себя внимание два обстоятельства. Первое из них - это способ обнаружения пустой записи. Если первый байт какой-либо записи не содержит информации, то можно считать, что вся запись пуста (в этом заключается принцип выполнения слова "внести"). Если в первом байте содержится некоторый символ, значение которого в коде ASCII меньше 33 (код 32 соответствует пробелу), значит, в данном байте находится невидимый символ и, следовательно, строка пуста. В пустом блоке могут находиться нули или он может быть полностью заполнен пробелами, но в любом случае такие записи будут рассматриваться как пустые. По обнаружении пустой записи LEAVE завершает цикл. В переменной ЗАПИСЬ# находится номер свободной записи.

Второе обстоятельство, связанное со словом ПУСТАЯ, заключается в том, что это слово прерывает выполнение посредством АBORT при заполнении файла, т.е. если оно прошлось по всем записям и не обнаружило среди них ни одной пустой. Для того чтобы обойти все записи, можно воспользоваться циклом DO , но как узнать по окончании выполнения цикла, была ли обнаружена во время просмотра хотя бы одна пустая запись?

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

Мы применяем аналогичный прием в определении слова -НАЙТИ . Это слово оставляет в вершине стека флаг для слова, которое его выполняет, а именно: "найти", "еще", "все" или (ПАРА) . Флаг показывает, была ли найдена заданная строка до конца файла. Каждое из перечисленных слов внешнего уровня в зависимости от состояния флага должно принимать соответствующее решение. Если заданная строка не найдена, то в вершине стека будет значение истины (отсюда и название слова -НАЙТИ ).

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

SWAP NOT SWAP

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

ТИП @
DUP ПОЛЕ

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

ТИП @ ПОЛЕ

ВНУТРИ цикла. Мы отказались от этого потому, что всегда стремимся минимизировать число операций, выполняемых внутри цикла: операции внутри цикла повторяются многократно и занимают очень много времени.

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

ЛИСТИНГ ПРОГРАММЫ
[Т.к. "разворотов" в html нет, то буду размещать блоки парами: левый (561-564, 565 и 566 - пустые) / правый (240-245).- G.].

561 LIST
0 HELP описание директив ФАЙЛОВОЙ СИСТЕМЫ.
1 внести - пополняет базу данных. Данные вводятся в 4 поля текущей
2 записи. Следите за правильным использованием запятых и пробелов
3 Использование: внести Рэйзер,Дан,диктор,555-1212
4 удалить - заполнение " " текущей и обновление дискового буфера.
5 изменить - размещение входного текста в заданном поле текущей.
6 Использование: изменить работа программист
7 найти - поиск входного фрагмента в памяти и индикация его
8 наличия или отсутствия. Использование: найти имя Дан
9 дать - выдача данных из указанного поля текущей записи.
10 Использование: дать телефон
11 еще - выдача следующего за ЗАПИСЬ# найденного фрагмента.
12 все - выдача искомых полей всех подходящих записей базы данных.
13 пара - поиск записи по содержимому двух полей.
14 Использование: пара работа диктор,телефон 535-9876
15 фио - поиск по имени и фамилии. Использование: фио Рэйзер,Дан

240 LIST
0 ( Простая файловая система ) DECIMAL
1 : HELP SCR @ 561 LIST SCR ! ; HELP
2 241 243 THRU
3 : внести ПУСТАЯ фамилия ПОМЕСТИТЬ имя ПОМЕСТИТЬ
4 работа ПОМЕСТИТЬ телефон ПОМЕСТИТЬ ;
5 : удалить ЗАПИСЬ /ЗАПИСЬ BLANK UPDATE ;
6 : изменить ' >BODY ПОМЕСТИТЬ ;
7 : найти ( поле текст) ' >BODY ЗАПОМНИТЬ ВВЕРХ -НАЙТИ IF
8 ОТСУТСТВУЕТ ELSE .ИМЯ THEN ;
9
10 : дать ( поле) ' >BODY .ПОЛЕ ;
11 : еще ВНИЗ -НАЙТИ IF . " Больше нет " ELSE .ИМЯ THEN ;
12 : все ВВЕРХ BEGIN CR -НАЙТИ NOT WHILE .ИМЯ ВНИЗ REPEAT ;
13
14 : пара ' >BODY ЗАПОМНИТЬ ' >BODY PAD 80 ЧТЕНИЕ имя (ПАРА) ;
15 : фио фамилия ЗАПОМНИТЬ PAD 80 ЧТЕНИЕ имя (ПАРА) ;

562 LIST
0 Текст в ASCII запоминается на диск в очередную длиной в одну
1 строку, на которую указывает ЗАПИСЬ#, с возможность" доступа к
2 четырем полям этой записи по именам полей. Для поиска в базе
3 данных по содержимому входного буфера применяется слово -TEXT.
4 Удачный поиск завершается выходом из цикла (EXIT) и установкой
5 ЗАПИСЬ# на вывод содержимого поля, определяемого ТИП .
6 Имя каждого поля содержит смещение относительно начала текущей
7 записи и счетчик (длину) в байтах. С помощью этих имен мы можем
8 в слове ПОЛЕ для доступа к данным выдавать виртуальные адреса.
9 Мы можем осуществлять доступ к полям заданной текущей ЗАПИСЬ#
10 по имени поля, а затем работать в окрестности этого адреса.
11 Значение ЗАПИСЬ# последовательно увеличивается.
12 ТИП содержит адрес интересующего нас поля; используется для
13 входа в запись, на которую указывает ЗАПИСЬ# .
14 ЧТО является буфером, содержащим входной тест, по которому
15 осуществляется поиск в базе данных.

241 LIST
0 ( Поля)
1 VARIABLE ЗАПИСЬ# ( текущая запись)
2 VARIABLE ТИП ( последнее используемое поле в таблице полей)
3 : ЧТО ( -- a) PAD 100 + ;
4 ( смещение) ( длина)
5 CREATE фамилия 0 , 16 ,
6 CREATE имя 16 , 12 ,
7 CREATE работа 28 , 24 ,
8 CREATE телефон 52 , 12 ,
9
10 64 CONSTANT /ЗАПИСЬ ( число байт в одной записи)
11 1024 CONSTANT /БЛОК ( число байт в одном блоке)
12 /БЛОК /ЗАПИСЬ / CONSTANT ЗАП/БЛК ( число записей в блоке)
13 244 CONSTANT ФАЙЛЫ ( с данного блока начинаютcя файлы)
14 2 ( блоки) ЗАП/БЛК * CONSTANT МАКС-ЗАП ( макс. число записей)
15

563 LIST
0 ВВЕРХ устанавливает указатель записи в начало базы данных.
1 ВНИЗ устанавливает указатель на следующую запись.
2
3 ПОДРОВНЯТЬ устанавливает счетчик для CMOVE в пределах длины
4 поля.
5 ЧТЕНИЕ заполняет буфер пробелами и заносит в него указанный
6 счетчик байтов. Ограничителем поля является знак ","
7 в коде ASCII.
8 ЗАПИСЬ оператор, работающий с виртуальной памятью и
9 вырабатывавший адрес внутри дискового блочного буфера. Этот
10 адрес является началом текущей записи.
11 ПОЛЕ выбирает смешение и длину в одной из четырех переменных
12 для получения адреса и счетчика, необходимых при выводе
13 информации.
14 ПОМЕСТИТЬ помечает символы, введенные с клавиатуры, в
15 обновляемый дисковый елочный буфер.

242 LIST
0 ( Записи)
1 : ВВЕРХ 0 ЗАПИСЬ# ! ;
2 : ВНИЗ 1 ЗАПИСЬ# +! ;
3 : ПОДРОВНЯТЬ ( а # а #) >R SWAP R> MIN CMOVE ;
4 : ЧТЕНИЕ ( a #) 2DUP BLANK ASCII , WORD COUNT
5 2SWAP ПОДРОВНЯТЬ ;
6 : ЗАПИСЬ ( -- а) ЗАПИСЬ# @ ЗАП/БЛК /MOD ФАЙЛЫ + BLOCK
7
8 SWAP /ЗАПИСЬ * + ;
9
10 : ПОЛЕ ( a -- a' n) 2@ ЗАПИСЬ + SWAP ;
11
12 : ПОМЕСТИТЬ ( а) ПОЛЕ ЧТЕНИЕ UPDATE ;
13
14
15

564 LIST
0 .ПОЛЕ вывод информации из заданного поля записи.
1 .ИМЯ вывод имени и фамилии из заданной записи
2
3 ЗАПОМНИТЬ устанавливает, ЧТО собой представляет текстовый
4 аргумент и ТИП поля, по которому будет осуществляться поиск.
5 ПУСТАЯ установка указателя записи на следующую доступную
6 (свободную) запись; если достигнуто МАКС-ЗАП, то ABORT.
7 Запись, первый байт которой равен 32 (пробел) или 0, пустая.
8 -НАЙТИ побайтное сравнение фрагмента ЧТО с содержимым выбранного
9 поля каждой записи. Если сравнение успешно или цикл завершен,
10 выдается флаг "истина". Это логическое значение используется
11 словами (ПАРА), найти, все и еще (ПАРА).
12 По адресам двух полей выбираются два текстовых фрагмента и
13 сравниваются с содержимым выбранных полей каждой записи.
14
15

243 LIST
0 ( Выдача информации)
1 : .ПОЛЕ ( a) ПОЛЕ -TRAILING TYPE SPACE ;
2 : .ИМЯ имя .ПОЛЕ фамилия .ПОЛЕ ;
3
4 : ЗАПОМНИТЬ ( a) DUP ТИП ! 2+ @ ASCII , TEXT
5 PAD ЧТО ROT CMOVE ;
6
7 : ПУСТАЯ TRUE МАКС-ЗАП 0 DO I ЗАПИСЬ# ! ЗАПИСЬ C@ 33 < IF
8 NOT LEAVE THEN LOOP ABORT" Переполнение файла" ;
9 : -НАЙТИ ( - t) TRUE ТИП @ МАКС-ЗАП ЗАПИСЬ# @ DO
10 I ЗАПИСЬ# ! DUP ПОЛЕ ЧТО -TEXT 0= IF
11 SWAP NOT SWAP LEAVE THEN LOOP DROP ;
12 : ОТСУТСТВУЕТ ." Сведения отсутствуют " ;
13 : (ПАРА) ( a) МАКС-ЗАП 0 DO I ЗАПИСЬ# ! -НАЙТИ IF
14 ОТСУТСТВУЕТ LEAVE ELSE DUP ПОЛЕ PAD -TEXT 0= IF
15 .ИМЯ LEAVE THEN THEN LOOP DROP ;

244 LIST
_0 Филлмор_________Миллард_____президент_______________нет телефона
_1 Линкольн________Авраам______президент_______________нет телефона
_2 Бронте__________Эмилия______писатель________________нет телефона
_3 Рэйзер__________Дан_________диктор__________________555-9876
_4 Фитцджеральд____Элла________певец___________________555-6789
_5 Чанг____________Конни_______диктор__________________555-9653
_6 Маккартни_______Пол_________композитор______________555-1212
_7 Вашингтон_______Джордж______президент_______________нет телефона
_8 Рейнольдз_______Фрэнк_______диктор__________________555-7865
_9 Силлз___________Беверли_____оперная_звезда__________нет телефона
10 Форд____________Генри_______капиталист______________нет телефона
11 Дьюхерст________Коллин______актриса_________________555-1234
12 Уандер__________Стив________композитор______________555-0097
13 Фуллер__________Бакминстер__известный_архитектор____555-3456
14 Ролз____________Джон________философ_________________555-4567
15 Трюдо___________Гарри_______юморист_________________555-4321

245 LIST
_0 Ван_____________Еарен_______Абигейл обозреватель____555-2233
_1 Симончик________Елена_______программист_____________нет телефона
_2 Томпсон_________Хантер.С____журналист_______________555-9452
_3 Маркузе_________Герберт_____философ_________________555-1849
_4 Джаббар_________Карим_______Абдул_баскетболист______555-4721
_5 Маги____________Трэйвис_____вымышленный_детектив____555-8792
_6 Дидьон__________Джоан_______писатель________________555-0089
_7 Фразетта________Фрэнк_______артист__________________555-9091
_8 Хенсон__________Джим________кукловод________________555-0101
_9 Саган___________Карл________астроном________________555-7070
10
11
12
13
14
15

[ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ ФИГ-ФОРТА.
1. Прежде чем загрузить программу с файловой системой, убедитесь в том, что приведенные ниже определения скомпилированы ПЕРВЫМИ.

: VARIABLE 0 VARIABLE ;
: CREATE <BUILDS DOES> ;
: BLANK ( a # -- ) BLANKS ;
: WORD ( -- a) WORD HERE ;
: >IN ( -- a) IN ;

Затем замените выражение блока 243

ABORT" Переполнение файла"

на следующее:

IF ." Переполнение файла" QUIT THEN

а в блоке 240 замените каждое вхождение выражения

' >BODY

на

[COMPILE] '

Наконец, загрузите необходимые дополнительные команды, указанные в примечании 3.

2. ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ ПОЛИФОРТА. Перед загрузкой программы работы с файлами убедитесь, что вы сначала скомпилировали следующее определение:

: >BODY ;

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

3. ДЛЯ ПОЛЬЗОВАТЕЛЕЙ СИСТЕМ, В КОТОРЫХ НЕТ СЛЕДУЮЩИХ СЛОВ. Загрузите, если нужно, приведенные ниже определения (после того, как осуществится загрузка определений, указанных в примечаниях 1 и 2):

-1 CONSTANT TRUE
: ASCII ( -- с) BL WORD 1+ С@ COMPILE LITERAL ; IMMEDIATE
: \ IN @ 64 / 1+ 64 * IN ! ;
: -TEXT ( a1 # a2 - ?) 2DUP + SWAP DO DROP 2+ DUP 2- @ I @ - DUP IF DUP ABS / LEAVE THEN 2 +LOOP SWAP DROP ;
: TEXT ( c) PAD 80 BLANK WORD СОUNT PAD SWAP CMOVE ;

].
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Броуди. Начальный курс программирования на языке ФОРТ. 1990 Empty Re: Броуди. Начальный курс программирования на языке ФОРТ. 1990

Сообщение автор Gudleifr Пт Апр 23, 2021 12:42 am

БЕЗ ВЗВЕШИВАНИЯ

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

Вычислим вес конусообразной "кучи" некоторого материала, зная ее высоту и угол откоса, а также плотность материала. Чтобы сделать нашу задачу более конкретной, давайте взвесим кучи песка, гравия и цемента. Крутизна каждой кучи, называется УГЛОМ ЕСТЕСТВЕННОГО ОТКОСА, зависит от вида материала. Например, песчаные кучи более крутые, чем из гравия.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b16510

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

Существует формула вычисления массы конусообразной кучи высотой h (в футах), углом естественного откоса Q (в градусах) [В оригинале - Тета.- G.] и плотностью материала D (в фунтах на кубический фут):

W = pi*h3*D/(3*tg2(Q))

[ДЛЯ СКЕПТИКОВ. Объем конуса V вычисляется по формуле V = (pi*b2*h)/3, где b - радиус основания; h - высота. Мы можем найти радиус основания, зная угол откоса, или, более точно, тангенс этого угла. Тангенсом некоторого угла называется отношение катета противолежащего (на рисунке h) к катету прилежащему (на рисунке b):

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b17010

Если обозначить этот угол через Q, то tg(Q) = h/b. Следовательно, b = h/tg(Q). Подставив это выражение в формулу для V и затем умножив результат на плотность D (в фунтах на кубический фут), получим приведенную выше формулу].

Запишем эту формулу на Форте. Условимся считать, что аргументы нашей программы будут вводиться в такой последовательности: название материала, например ПЕСОК, а затем высота кучи. В результате выполнения программы мы должны получить массу кучи сухого песка. Допустим, что для любого материала его плотность и угол естественного откоса остаются неизменными. Поэтому можно записать указанные две величины для каждого вида материала в некоторую таблицу. Так как в конечном итоге нам потребуется тангенс угла, а не его величина в градусах, будем хранить значение тангенса. Например, угол естественного откоса для цемента составляет 35o, а его тангенс равен .700 . Мы будем хранить это значение в виде целого 700.

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b14910

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

Броуди. Начальный курс программирования на языке ФОРТ. 1990 G9b15010

Начнем решение задачи с установления порядка величин. Высота всех трех куч лежит в пределах от 5 до 50 футов. Вычислив массу кучи цемента, мы получим 35000000 фунтов. Но так как кучи не имеют форму правильного конуса, а мы берем средние значения величин, точность вычислений не может превышать четырех или пяти порядков [ДЛЯ СПЕЦИАЛИСТОВ-МАТЕМАТИКОВ. На самом деле, так как высота в нашем примере выражается тремя цифрами, мы не можем ожидать точности большей, чем три порядка. Однако в учебных целях мы выберем точность, превышающую четыре порядка]. Если перевести наш результат в тонны, то получим 17500т. Это значение вполне удовлетворяет представлению числа одинарной длины, так что в нашей программе условимся применять арифметические операции только над значениями одинарной длины.

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

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

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

15 ФУТОВ 2 ДЮЙМОВ КУЧА

где слова ФУТОВ и ДЮЙМОВ представляли бы футы и дюймы с точностью до десятых долей дюйма, а слово КУЧА производило бы необходимые вычисления. Слова ФУТОВ и ДЮЙМОВ можно было бы определить так:

: ФУТОВ ( футы -- вес-в-масштабе) 10 * ;
: ДЮЙМОВ ( вес-в-масштабе -- вес-в-масштабе') 100 12 */ 5 + 10 / + ;

использование слова ДЮЙМОВ не обязательно. Таким образом, выражение 23 ФУТОВ поместит в вершину стека число 230, выражение 15 ФУТОВ 4 ДЮЙМОВ - число 153 и т.д. (Между прочим довольно легко организовать ввод данных и в десятых долях дюйма с десятичной точкой, например: 15.2 . В таком случае слово NUMBER переводит вводимое число в значение двойной длины. Так как мы имеем дело только с числами одинарной длины, для того чтобы удалить старший байт, достаточно просто применить слово DROP).

При написании определения КУЧА нужно попытаться обеспечить максимальную точность, не выходя за границы 15 разрядов. По нашей формуле первое, что требуется сделать,- возвести аргумент в куб. Однако напомним, что аргументом может служить значение высоты вплоть до 50 футов, т.е. при выбранном масштабе 500. Даже если мы возведем это значение лишь В КВАДРАТ, то получим 250000, что уже превышает возможности представления одинарной точности. Но для того чтобы выразить ответ в тоннах, рано или поздно в ходе вычислений нам придется выполнить деление на 2000, поэтому выражение

DUP DUP 2000 */

будет одновременно возводить аргумент в квадрат и переводить в тонны, так как под промежуточный результат в операции */ отводится слово двойной длины. Если наш аргумент равен 500, то в результате получится 125.

Высота кучи может оказаться равной всего лишь пяти футам. Возведение этого значения в квадрат дает 25, а при делении последнего числа на 2000 средствами целочисленной арифметики мы получим нуль, что свидетельствует о неудачном выборе масштаба с небольшими значениями аргумента. Для обеспечения максимальной точности мы не должны при масштабировании получать меньшие значения, чем требуется. Число 250000 вполне представимо средствами арифметики одинарных чисел, если его предварительно разделить на 10, поэтому мы начнем наше определение КУЧА следующим выражением:

DUP DUP 10 */

На данном этапе результат с учетом масштабирования окажется в десять раз большим (25000 вместо 2500.0), чем требуется.

Далее необходимо возвести аргумент в КУБ. Непосредственное умножение опять приведет к результату двойной длины и, значит, масштабирование должно производиться с применением операции */ . Как видите, выбрав в качестве делителя 1000, мы продолжаем оставаться в пределах одинарной длины. Теперь наш результат будет в десять раз меньшим (12500 вместо 125000), чем требуется, и все еще сохраняется точность в пять знаков. Согласно приведенной выше формуле нужно умножить аргумент на pi. Вы знаете, что на Форте это можно сделать с помощью следующего выражения:

355 113 */

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

355 339 */

что не вызывает никаких проблем с масштабированием. Затем полученное выражение делим на квадрат тангенса (путем последовательного ДВОЙНОГО деления на значение тангенса). Поскольку значение тангенса, хранимое в таблице, увеличено в 1000 раз, чтобы выполнить деление, нужно делимое умножить на 1000 и разделить на табличное значение тангенса:

1000 ТЕТА @ */

Такое действие мы должны произвести дважды, поэтому оформим его в виде определения с именем /TAN (для деления-на-тангенс) и применим это определение дважды в определении слова КУЧА. Наш результат пока еще будет в десять раз меньше, чем на самом деле (26711 вместо 267110 при максимальных значениях аргументов).

Нам остается лишь умножить полученное значение на величину плотности материала, максимальное значение которой равно 131 фунт на кубический фут. Во избежание переполнения уменьшаем плотность в 100 раз, применяя выражение

ПЛОТНОСТЬ @ 100 */

Проверив выполнение программы в этой точке с данными о куче цемента высотой 50 футов, получим число 34991, что превышает 15 разрядов. Теперь пора принять во внимание значение 2000. Вместо

ПЛОТНОСТЬ @ 100 */

мы можем написать:

ПЛОТНОСТЬ @ 200 */

и наш ответ будет приведен к целому числу тонн.

Этот вариант программы вы найдете на распечатке блока 246, которая приводится ниже. Как уже упоминалось ранее, в блоке 248 находится вариант той же программы с использованием арифметических операций над данными двойной длины. Здесь вы вводите высоту в виде числа двойной точности с одним знаком после точки, представляющим десятые доли фута, например 50.0 футов, а далее следует слово ФУТЫ.

Выполняя целочисленные арифметические операции над числами двойной длины, мы в состоянии округлять полученный вес кучи до БЛИЖАЙШЕГО целого фунта. В нашем случае применение целочисленной арифметики двойной длины открывает не меньшие возможности, чем те, которые предоставляет более мощная арифметика с плавающей точкой. Ниже для сравнения приводятся результаты, полученные с помощью калькулятора, имеющего диапазон представления чисел в 10 десятичных цифр, средств Форта одинарной длины и средств Форта двойной длины. Результаты представлены для кучи цемента высотой 50 футов с использованием табличных значений:

В ФУНТАХВ ТОННАХ
калькулятор34995.63417.497817
16-разрядный форт-17.495
32-разрядный форт34995.63417.497817

Результаты же работы нашей программы таковы:

246 LOAD_ok ( компиляция варианта с одинарной длиной)
ЦЕМЕНТ_ok
10 ФУТОВ КУЧА_=_138_тонн_цемента_ok
10 ФУТОВ 3 ДЮЙМОВ КУЧA_=_151_тонн_цемента_ok
СУХОЙ-ПЕСОК_ok
10 ФУТОВ КУЧА =_81_тонн_песка_ok
248 LOAD_ok ( компиляция варианта с двойной длиной)
ЦЕМЕНТ_ok
10.0 ФУТОВ_=_279939_фунтов_цемента_или_139.969_тонн_ok

ЗАМЕЧАНИЕ ПО ПОВОДУ КОМПИЛЯЦИИ СТРОК. Определение слова МАТЕРИАЛ требует трех аргументов для каждого материала, одним из которых является адрес некоторой строки. Слово .МАТЕРИАЛ с помощью этого адреса выводит название рассматриваемого материала. Для помещения этой строки в словарь и передачи адреса слову МАТЕРИАЛ мы определили слово с именем ," (запятая-кавычки).

В первую очередь оно вносит в вершину стека значение HERE для слова МАТЕРИАЛ, так как именно по данному адресу будет компилироваться строка, а затем инициирует слово STRING для компиляции строкового литерала, ограниченного двойной кавычкой.
В некоторых системах это слово можно определить следующим образом:

: STRING ( с) WORD С@ 1 + ALLOT ;

Block# 246
0 \ Определение массы конической кучи - одинарная длина
1 VARIABLE ПЛОТНОСТЬ VARIABLE ТЕТА VARIABLE H.M.
2 : ," ( --a) HERE ASCII " STRING ;
3 : .МАТЕРИАЛ H.M. @ COUNT TYPE SPACE ;
4 : МАТЕРИАЛ ( 'строка плотность тета -- ) CREATE , , ,
5 DOES> DUP @ ТЕТА ! 2+ DUP @ ПЛОТНОСТЬ ! 2+ @ H.M. ! ;
6
7 : ФУТОВ ( футы -- вec-в-масштабе) 10 * ;
8 : ДЮЙМОВ ( вес-в-масштабе -- вес-в-масштабе')
9 100 12 */ 5 + 10 / + ;
10
11 : /TAN ( n - n') 1000 TETA @ */ ;
12 : КУЧА ( вес-в-масштабе -- )
13 DUP DUP 10 */ 1000 */ 355 339 */ /TAN /TAN
14 ПЛОТНОСТЬ @ 200 */ ." = " . ." тонн " .МАТЕРИАЛ ;
15 247 LOAD

Block# 247
0 \ Таблица материалов
1 \ адрес-строки, плотность, тета
2 ," цемента" 131 700 МАТЕРИАЛ ЦЕМЕНТ
3 ," рыхлого гравия" 93 649 МАТЕРИАЛ РЫХЛЫЙ-ГРАВИЙ
4 ," плотного гравия" 100 700 МАТЕРИАЛ ПЛОТНЫЙ-ГРАВИЯ
5 ," сухого песка" 90 734 МАТЕРИАЛ СУХОЙ-ПЕСОК
6 ," мокрого песка" 118 900 МАТЕРИАЛ МОКРЫЙ-ПЕСОК
7 ," глины" 120 727 МАТЕРИАЛ ГЛИНА
8
9
10
11
12 ЦЕМЕНТ
13
14
15

Block# 248
0 \ Масса конической кучи - двойная длина
1 VARIABLE ПЛОТНОСТЬ VARIABLE TETA VARIABLE H.M.
2 : ," ( -- a) HERE ASCII " STRING ;
3 : .МАТЕРИАЛ H.M. @ COUNT TYPE SPACE ;
4 : DU.3 ( du -- ) <# # # # ASCII . HOLD #S #> TYPE SPACE ;
5 : МАТЕРИАЛ ( 'строка плотность тета -- ) CREATE
6 DOES> DUP @ TETA ! 2+ DUP @ ПЛОТНОСТЬ ! 2+ @ H.M. ! ;
7 : КУБ ( d -- d') 2DUP OVER 10 M*/ DROP 10 M*/
8 : /TAN ( d -- d') 1000 TETA @ M*/ ;
9 : ФУТОВ ( d -- ) КУБ 355 339 M*/ ПЛОТНОСТЬ @ 1 M*/
10 /TAN /TAN 5 M+ 1 I0 M*/
11 2DUP ." = " D. ." фунтов " .МАТЕРИАЛ
12 1 2 M*/ ." или " DU.3 ." тонн " ;
13 247 LOAD
14
15
Gudleifr
Gudleifr
Admin

Сообщения : 3246
Дата регистрации : 2017-03-29

Вернуться к началу Перейти вниз

Страница 1 из 2 1, 2  Следующий

Вернуться к началу

- Похожие темы

 
Права доступа к этому форуму:
Вы не можете отвечать на сообщения