Skip to main content

Блог инженера

Блог о минимализме, инжинерии и программировании.



Чертим N-угольник на Форт.

  | #Форт#программирование#unfinished

В предыдущем примере графической программы был Фрагмент, который не соответствует дао Форта. Количество сторон фигуры было “вшито” в программный код, да ещё неправильным образом. Определение:

: SIDES 14 ;

Задало количество шагов фигуры. Правильнее было бы:

14 CONSTANT SIDES

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

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

15 CONSTANT SIDES
VARIABLE CORNERS SIDES 2* CELLS ALLOT 

: 2CELLS 2* CELLS ;

: CALC-CORNERS
360 SIDES / 0  ( ADIF )
SIDES 0 DO
DUP DUP        ( ADIF ANGLE ANGLE ANGLE )
SIN	10 /       ( ADIF ANGLE ANGLE SIN )
SWAP           ( ADIF ANGLE SIN ANGLE )
COS	10 /       ( ADIF ANGLE SIN COS )
CORNERS I 2* CELLS + 2!	( ADIF 0 )
OVER +         ( ADIF 0+ADIF )
LOOP
DROP DROP      ( ) ;

: DRAW-CORNERS 
SIDES 1- 0 DO
SIDES I 1+ DO
CORNERS I 2CELLS + 2@
MOVETO
CORNERS J 2CELLS + 2@
LINETO
LOOP
LOOP ;

: NGON
2200 2200 800 800 INITGRAPH
1100 1100 SETORIGIN
CALC-CORNERS
DRAW-CORNERS 
STROKE
CLOSEGRAPH ;

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

VARIABLE CORNERS 100 CELLS ALLOT 

: 2CELLS 2* CELLS ;

: CALC-CORNERS { SIDES -- }
360 SIDES / 0  ( ADIF )
SIDES 0 DO
DUP DUP        ( ADIF ANGLE ANGLE ANGLE )
SIN	10 /       ( ADIF ANGLE ANGLE SIN )
SWAP           ( ADIF ANGLE SIN ANGLE )
COS	10 /       ( ADIF ANGLE SIN COS )
CORNERS I 2* CELLS + 2!	( ADIF 0 )
OVER +         ( ADIF 0+ADIF )
LOOP
DROP DROP      ( ) ;

: DRAW-CORNERS { SIDES -- }
SIDES 1- 0 DO
SIDES I 1+ DO
CORNERS I 2CELLS + 2@
MOVETO
CORNERS J 2CELLS + 2@
LINETO
LOOP
LOOP ;

: NGON
2200 2200 800 800 INITGRAPH
1100 1100 SETORIGIN
15 DUP
CALC-CORNERS
DRAW-CORNERS 
STROKE
CLOSEGRAPH ;

Теперь мы передаём количество сторон в слова для расчёта и рисования через стек. Мы используем DUP, чтобы удвоить число на стеке, т.к. использование числа под локальную перменную снимает его со стека. Технически, можно было бы просто возвращать число на стек в конце определения слова, но пока я не вижу в этом нужды. Проблема в другом, в самом начале определения я резервирую память под хранение промежуточных значений и теперь неизвестно, а сколько памяти нужно резервировать. Я беру 100 ячеек памяти и подразумеваю, что больше 50 углов чертить не придётся. Но даже с “опасным” стилем программирования в Форт - это чересчур.

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

About Mikhail Kiselev

Photo of Mikhail Kiselev

Приветствую в моём блоге! 😄 Меня зовут Михаил. Я инженер и программист. Живу в Израиле. Но мой блог связан с работой в Сибири и на Сахалине, путешествую где придётся. Я предпочитаю пост в блог посту в твиттер. Описание полезной технологии или гаджета предпочитаю описанию заката или посиделок в кафе.