Выбор по целому
Выбор по целому — распространенная конструкция в языках программирования. Она является обобщением условного оператора, который осуществляет выбор между двумя последовательностями операторов — частью «то» и частью «иначе» — по логическому значению (ИСТИНА или ЛОЖЬ) условия. В конструкции выбора по целому в качестве значения условия выступает целое число, и выбор осуществляется между несколькими альтернативными ветвями, каждая из которых соответствует определенному значению условия или некоторому множеству таких значений. Как правило, множества значений условия для разных ветвей не должны пересекаться. Обычно эти множества задают явным перечислением отдельных значений или указанием диапазона для них. Разберем два варианта реализации выбора по целому. В первом используется переключатель, во втором — вложенные условные операторы.
В случае реализации через переключатель каждая ветвь должна задаваться отдельным форт-словом. Пусть имеется несколько ветвей, каждая из которых задается своим порядковым номером. Из адресов компиляции этих ветвей в соответствии с их порядковыми номерами строится вектор-переключатель, и нужная ветвь выбирается по порядковому номеру. Определяющее слово SWITCH (переключатель) компилирует такой вектор в поле параметров определяемого слова и задает выбор альтернативы в исполняющей части:
: SWITCH ( ->) ?EXEC CREATE SMUDGE ] DOES> ( I:НОМЕР ВЕТВИ,PFA->) SWAP 1- 2 * + @ EXECUTE ;
Создающая часть строит новую словарную статью и переключает текстовый интерпретатор в состояние компиляции. Таким образом, следующие слова будут компилироваться в поле параметров определяемого слова. Слово SMUDGE выставляет разряд «Не готов» в байте-счетчике поля имени, делая данную статью «невидимой» при поиске слов в словаре. Этот разряд будет сброшен словом ; (точка с запятой), которое завершает компиляцию такого переключателя. Например:
: W1 ." ПОНЕДЕЛЬНИК" ; : W2 ." ВТОРНИК" ; : W3 ." СРЕДА" ; : W4 ." ЧЕТВЕРГ" ; : W5 ." ПЯТНИЦА" ; : W6 ." СУББОТА" ; : W7 ." ВОСКРЕСЕНЬЕ" ; SWITCH ДЕНЬ-НЕДЕЛИ W1 W2 W3 W4 W5 W6 W7 ;
Порядок использования переключателя ДЕНЬ-НЕДЕЛИ иллюстрирует следующий протокол работы:
> 3 ДЕНЬ-НЕДЕЛИ СРЕДА OK > 5 ДЕНЬ-НЕДЕЛИ ПЯТНИЦА OK
Описанный механизм аналогичен определению вектора, рассмотренному в . Его также можно задавать по-разному в зависимости от конкретных требований. В приведенной реализации неправильное значение выбирающего условия (выходящее за диапазон от 1 до 7) приведет к непредсказуемому результату, поскольку соответствующий контроль отсутствует. Чтобы ввести его в реализацию, нужно каким-то образом подсчитать число скомпилированных ссылок при завершении компиляции. Для этого можно применить способ, аналогичный компиляции переходов в шитом коде, введя вместо точки с запятой специальное слово, отмечающее конец переключателя.
Для реализации конструкции выбора через вложенные условные операторы создаются специальные слова, которые обрамляют всю конструкцию и каждую ее ветвь. В этом случае ветвь является обычной последовательностью форт-слов, аналогичной части «то» или части «иначе» условного оператора. Как и в случае условного оператора, конструкцию выбора можно использовать только внутри определений через двоеточие, т.е. в состоянии компиляции текстового интерпретатора. При этом все ее специальные слова имеют признак немедленного исполнения и компилируют необходимые проверки и переходы.
: CASE ( ->A0:CSP,4) ?COMP CSP @ !CSP 4 ; IMMEDIATE : OF ( 4->A:>MARK,1,5) 4 ?PAIRS COMPILE OVER COMPILE = [COMPILE] IF COMPILE DROP 5 ; IMMEDIATE : ENDOF ( A:>MARK,1,5->A2:>MARK,1,4) 5 ?PAIRS [COMPILE] ELSE 4 ; IMMEDIATE : ENDCASE ( A0,A1,1, ..,AN,4->) 4 ?PAIRS COMPILE DROP BEGIN SP@ CSP @ = 0= WHILE [COMPILE] THEN REPEAT ( А0) CSP ! ; IMMEDIATE : ДЕНЬ-НЕДЕЛИ ( N->) CASE 1 OF ." ПОНЕДЕЛЬНИК" ENDOF 2 OF ." ВТОРНИК" ENDOF 3 OF ." СРЕДА" ENDOF 4 OF ." ЧЕТВЕРГ" ENDOF 5 OF ." ПЯТНИЦА" ENDOF 6 OF ." СУББОТА" ENDOF 7 OF ." ВОСКРЕСЕНЬЕ" ENDOF CR . ." - ДЕНЬ НЕДЕЛИ?" ABORT ENDCASE ;
Слова CASE (выбор) и ENDCASE (конец выбора) ограничивают конструкцию и обеспечивают правильную компиляцию вложенных операторов. Слово CASE, проверив, что текстовый интерпретатор находится в состоянии компиляции, сменяет глобальную переменную CSP (сокращение от CURRENT STACK POINTER — текущий указатель стека), сохраняя на стеке ее прежнее значение (слово !CSP засылает в переменную CSP адрес текущей вершины стека). Слова OF (из) и ENDOF (конец из) ограничивают отдельную ветвь. Во время работы скомпилированного определения перед началом исполнения каждой ветви на стеке лежат два значения: число, представляющее условие выбора, и номер данной ветви. Слово OF компилирует текст OVER = IF DROP, который обеспечивает передачу управления на данную ветвь, если эти два значения совпали, причем в этом случае они оба снимаются со стека. Если же значения оказались разными, то управление передается на текст, следующий за словом ENDOF для данной ветви, которое эквивалентно слову ELSE. Наконец, слово ENDCASE компилирует операцию DROP, чтобы снять со стека оставшееся там значение условия, и разрешает все накопившиеся на стеке выходы из ветвей на текущую точку, исполняя для этих ветвей слово THEN. Его последним действием является восстановление прежнего значения переменной CSP, которое к этому моменту оказывается на вершине стека.
В приведенных определениях можно несколько уменьшить объем кода, компилируемого для входа в каждую ветвь, если ввести для этого специальное слово, вслед за которым в шитый код компилируется адрес перехода на следующую ветвь:
: (OF) ( N:УСЛОВИЕ,I:НОМЕР ВЕТВИ->) OVER = IF DROP R> 2+ ELSE R> @ THEN >R ; : OF ( 4->A:>MARK,2,5) 4 ?PAIRS COMPILE (OF) >MARK 2 5 ; IMMEDIATE
Исполнение слова (OF) аналогично ?BRANCH, в зависимости от условия оно переустанавливает указатель интерпретации, либо обходя скомпилированную за ним ссылку, либо устанавливая указатель по значению этой ссылки. Аналогичным образом наряду со словами (OF) и ОF можно определить пары (<OF) и <OF, (>OF) и >OF, (<OF<) и <OF<, выполняющие сравнение значения условия с заданным значением, выбирающим данную ветвь, не на равенство, а на неравенство указанного вида.При этом слово (<OF<) сравнивает значение условия с двумя значениями, определяющими интервал. Например:
: ПРИЕМ ( N:НОМЕР ДНЯ->) CASE 3 OF ." НЕПРИЕМНЫЙ" ENDOF 1 5 <OF< ." ПРИЕМНЫЙ" ENDOF 6 7 <OF< ." ВЫХОДНОЙ" ENDOF CR . ." - НОМЕР ДНЯ?" ABORT ENDCASE ." ДЕНЬ" ;
В данном примере множества выбирающих значений пересекаются, и выбор ветви определяется для этого случая порядком расположения ветвей в конструкции выбора.