В предыдущем примере графической программы был Фрагмент, который не соответствует дао Форта. Количество сторон фигуры было “вшито” в программный код, да ещё неправильным образом. Определение:
: 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 после того, как оно начнёт использоваться в определениях. Также нет динамических массивов.