Данная группа заданий посвящена исключительно условному оператору «IF».
Пример 1. Если данное целое число положительное, то увеличить его на единицу, иначе ничего не делать.
: IF1 ( A -> {[A++]OR[A]} )
DUP 0 > \ A-> A Bool
IF 1+ THEN .
;
5 IF1
6 Ok
0 IF1
0 Ok
-5 IF1
-5 Ok
Конструкция «IF» берет со стека число A, если оно истинно (любое число не равное нулю), то выполняются инструкции до ключевого слова THEN, то есть увеличивает его на единицу, в данном случае, иначе сразу переходит на код после «THEN».
В системе SP-Forth есть специальные константы «TRUE» и «FALSE», которые можно выполнить. Они оставляют на стеке соответствующие числа, так можно заметить, что SP-Forth кодирует истину числом «-1». Этими константами и логическими выражениями можно управлять ходом выполнения программы.
Пример 2. Добавляется к предыдущему примеру дополнительное условие «в противном случае» (иначе). Для этого в SP-Forth есть специальное слово ELSE. Вся конструкция выглядит так:
IF ( код выполняется, если условие истина) ELSE ( иначе, то есть когда ложь ) THEN
: IF2 ( A -> {[A++]OR[A-2]} ) \ A>0: ? A++ , A-2
DUP 0 > \ A -> A Bool
IF 1+ ELSE 2- THEN .
;
5 IF2
6 Ok
0 IF2
-2 Ok
-5 IF2
-7 Ok
Пример 3. Отличается от предыдущего добавлением дополнительного условия, при равенстве нулю. Добавим всего одну строчку после описания.
: IF3 ( A -> ) \ {[A++]OR[A-2]OR[10]}
DUP 0 = IF DROP 10 . EXIT THEN
DUP 0 >
IF 1+ ELSE 2- THEN . ;
10 IF3
11 Ok
0 IF3
10 Ok
-10 IF3
-12 Ok
Пример 4. В наборе из трех чисел определить количество положительных. Нужно завести счетчик, и каждое число проверить отдельно. Если истина счетчик увеличивается на единицу. В конце выводим его значение.
: IF4 ( A B C -> N ) \ N= количество положительных
0 \ A B C -> A B C 0 (0 – счетчик)
SWAP 0 > IF 1+ THEN \ A B C 0 -> A B N
SWAP 0 > IF 1+ THEN \ A B N -> A N
SWAP 0 > IF 1+ THEN \ A N -> N
.
;
1 2 3 IF4
3 Ok
0 0 0 IF4
0 Ok
-1 -2 -3 IF4
0 Ok
-1 0 2 IF4
1 Ok
Пример 5. Модификация предыдущего примера, добавляется количество отрицательных чисел. Создадим две переменные - количество положительных (N+) и отрицательных чисел (N-). Проинициализируем их нулями.
VARIABLE N+
VARIABLE N-
: IF5 ( A B C -> ) \ N+ = количество положительных N- = количество отрицательных
0 N+ ! 0 N- ! \ инициализация N+ и N- нулями
DUP 0 > IF DROP N+ @ 1+ N+ ! ELSE \ если C>0, то N+ увеличиваем на единицу
0 < IF N- @ 1+ N- ! THEN \ A B C -> A B, если C<0, то увеличиваем N-
THEN
DUP 0 > IF DROP N+ @ 1+ N+ ! ELSE \ аналогично для B
0 < IF N- @ 1+ N- ! THEN \ A B -> A
THEN
DUP 0 > IF DROP N+ @ 1+ N+ ! ELSE \ для A
0 < IF N- @ 1+ N- ! THEN \ A ->
THEN
N+ @ . \ выводим сначала число положительных чисел
N- @ . \ затем отрицательных
;
1 2 3 IF5
3 0 Ok
-1 0 1 IF5
1 1 Ok
0 0 0 IF5
0 0 Ok
Если не обнулять N+ и N-, то при каждом вызове слова «IF5» будет происходить суммирование с предыдущими значениями количества положительных и отрицательных чисел, и результат будет не корректный. Этой проблемы не будет, если результат оставляется на стеке, как в коде ниже, без использования переменных.
: IF5 ( A B C -> ) \ N+ = количество положительных N- = количество отрицательных
0 0 \ A B C -> A B C 0 0 (0 0 - счетчики N+ и N-)
ROT DUP 0 > IF DROP SWAP 1+ SWAP ELSE
0 < IF 1+ THEN \ A B C -> A B N+ N-
THEN
ROT DUP 0 > IF DROP SWAP 1+ SWAP ELSE
0 < IF 1+ THEN \ A B -> A N+ N-
THEN
ROT DUP 0 > IF DROP SWAP 1+ SWAP ELSE
0 < IF 1+ THEN \ A -> N+ N-
THEN
SWAP . \ выводим сначала число положительных чисел
. \ затем отрицательных
;
Какой вариант проще и понятней пускай каждый решает для себя сам. Оба варианта в учебных целях хороши. Особенность Форта – во многих задачах можно обойтись без явного объявления переменных.
Пример 6. Из двух чисел вывести большее (найти максимум). Дублируем оба числе и сравниваем, затем удаляем меньшее из них, результат выводим на экран.
: IF6 ( A B -> MAX[A,B] )
2DUP > \ A B -> A B A>B
IF DROP ELSE SWAP DROP THEN .
;
1 2 IF6
2 Ok
2 1 IF6
2 Ok
1 1 IF6
1 Ok
-1 -2 IF6
-1 Ok
0 -1 IF6
0 Ok
Пример 7. Схож с предыдущим. Определяем меньшее из двух чисел и печатаем порядковый номер. Сравниваем без сохранения исходных данных, так как на печать отправляем только номер минимума.
: IF7 ( A B -> number_of_min[A,B] )
> \ A B -> A>B
IF 2 ELSE 1 THEN .
;
1 2 IF7
1 Ok
2 1 IF7
2 Ok
1 1 IF7
1 Ok
При равенстве выводит 1-ый из них, в принципе не ошибка.
Пример 8. Вывести два числа в порядке убывания. Похож на Пример 6. Простая сортировка двух чисел.
: IF8 ( A B -> MAX[A,B] MIN[A,B] )
2DUP > \ A B -> A B A>B
IF SWAP THEN . .
;
1 2 IF8
2 1 Ok
2 1 IF8
2 1 Ok
3 3 IF8
3 3 Ok
Пример 9. Значения переменных A и B, перераспределить. Минимальное присвоить A, а максимальное B.
Сначала создадим эти переменные вещественного типа. Затем присвоим им входные значения. Далее стандартная проверка. При необходимости обмен значениями.
FVARIABLE A FVARIABLE B
: IF9 ( A B -> ) \ A=MIN[A,B] B=MAX[A,B]
B F! \ сохраняем значение в B
A F! \ сохраняем значение в A
A F@ B F@ FOVER FOVER F< \ -> A B A<B
IF FDROP FDROP ELSE A F! B F! THEN \ если A<B удаляем A B, иначе меняем значения местами
A F@ F. \ выводим A
B F@ F. \ выводим B
;
12E-1 -12E-1 IF9
-1.2000000 1.2000000 Ok
-12E-1 12E-1 IF9
-1.2000000 1.2000000 Ok
Вторая и третья строчки сохраняет ввод пользователя в переменные A и B. Четвертая считывает их, дублирует и сравнивает, результат которой обрабатывает пятая, либо удаляя со стека не нужные для замены значения, либо сохраняет поменяв их местами.
SP-Forth позволяет решать данный пример без применения переменных. Код куда проще в этом случае.
: IF9 ( A B -> ) \ A=MIN[A,B] B=MAX[A,B]
FOVER FOVER F< \ A B -> A B A<B
IF FSWAP THEN \ если A<B, т. к. сначала будет распечатана вершина
F. F.
;
12E-1 -12E-1 IF9
-1.2000000 1.2000000 Ok
-12E-1 12E-1 IF9
-1.2000000 1.2000000 Ok
Результат тот же, а код уменьшился почти в два раза.
Пример 10. Похож на предыдущий. Вместо перемены местами значений присваиваем ноль в случае равенства A и B, и сумму A+B, в противном случае. Объявление переменных теперь по условию задачи целого типа:
VARIABLE A VARIABLE B
: IF10 ( A B -> )
B ! A ! \ сохранение в A и B
A @ B @ 2DUP = \ A B -> A B A=B
IF 2DROP 0 ELSE + THEN \ если A=B, то оставляем X=ноль, иначе сумму X=A+B
DUP A ! B ! \ X-> A=X B=X
A @ . \ выводим A
B @ . \ выводим B
;
Вот результат работы, написанного выше слова:
5 10 IF10
15 15 Ok
3 3 IF10
0 0 Ok
И в этом примере нет необходимости создавать переменные. Упрощенный вариант будет выглядеть так:
: IF10 ( A B -> )
2DUP = \ A B -> A B A=B
IF 2DROP 0 ELSE + THEN \ если A=B, то оставляем X=ноль, иначе сумму X=A+B
DUP . .
;
5 10 IF10
15 15 Ok
3 3 IF10
0 0 Ok
Результат идентичен, а слово не только короче, но и проще.
Пример 11. Если A=B, то присвоить A и B ноль, иначе максимум из A и B. Сперва напишем, как в условии с переменными, аналогично предыдущему примеру.
: IF11 ( A B -> )
B ! A ! \ сохранение A и B
A @ B @ 2DUP = \ A B -> A B A=B
IF 2DROP 0 ELSE \ если A=B, то оставляем X=ноль, иначе X=MAX[A,B]
2DUP > IF DROP ELSE SWAP DROP THEN \ если A>B, то X=A, иначе X=B
THEN
DUP A ! B ! \ X-> A=X B=X
A @ . \ выводим A
B @ . \ выводим B
;
50 100 IF11
100 100 Ok
0 0 IF11
0 0 Ok
10 10 IF11
0 0 Ok
100 50 IF11
100 100 Ok
Теперь, с чувством выполненного долга, можем написать упрощенный вариант без использования переменных.
: IF11 ( A B -> )
2DUP = \ A B -> A B A=B
IF 2DROP 0 ELSE \ если A=B, то оставляем X=ноль, иначе X=MAX[A,B]
2DUP > IF DROP ELSE SWAP DROP THEN \ если A>B, то X=A, иначе X=B
THEN
DUP . .
;
Результаты теста те же (проверить самостоятельно), а программка меньше и яснее.
Пример 12. Из трех чисел определить минимум.
: IF12 ( A B C -> MIN[A,B,C] )
2DUP < \ A B C -> A B C B<C
IF DROP ELSE SWAP DROP THEN \ A B C -> A MIN[B,C]
2DUP < \ A MIN[B,C] -> A MIN[B,C] A<MIN[B,C]
IF DROP ELSE SWAP DROP THEN \ A MIN[B,C] -> MIN{A,MIN[B,C]}
.
;
1 2 3 IF12
1 Ok
-1 -2 -3 IF12
-3 Ok
10 0 -10 IF12
-10 Ok
В принципе код должен быть понятен, ничего сложного, сначала мы выясняем минимум из B и C, а затем снова минимум, но уже из A и MIN[B,C].
Пример 13. Из трех чисел определить среднее (которое лежит между минимумом и максимумом).
: IF13 ( A B C -> X ) \ MIN[A,B,C]<=X<= MAX[A,B,C]
2DUP > \ A B C -> A B C B>C
IF SWAP THEN \ A B C -> A B C – «(B C) - отсортировано»
DUP 2OVER DROP < \ A (B C) -> A (B C) C<A
IF . 2DROP EXIT ELSE \ A (B C) -> – «(B C A) - отсортирован», C-среднее
DUP 2OVER > \ A (B C) -> A (B C) C A>B, если A>B, то A-среднее
IF 2DROP DROP . EXIT ELSE \ A B C C -> - A-среднее
2DROP . DROP EXIT THEN \ иначе B-среднее
THEN
;
1 2 3 IF13
2 Ok
3 2 1 IF13
2 Ok
2 1 3 IF13
2 Ok
2 3 1 IF13
2 Ok
1 3 2 IF13
2 Ok
3 1 2 IF13
2 Ok
-1 -2 -3 IF13
-2 Ok
-1 0 -2 IF13
-1 Ok
-1 0 1 IF13
0 Ok
Если логика работы не понятна, то можно производить полную сортировку, затем выбрать средний элемент (второй в массиве из трех чисел).
: IF13 ( A B C -> X ) \ MIN[A,B,C]<=X<= MAX[A,B,C]
2DUP > \ A B C -> A B C B>C
IF SWAP THEN \ A B C -> A (B C) – «(B C) - отсортирован»
DUP 2OVER DROP < \ A (B C) -> A (B C) C<A
IF ROT ELSE \ A (B C) -> (B C A) – «(B C A) - отсортирован», C-среднее
DUP 2OVER > NIP \ A (B C) -> A (B C) A>B, если A>B, то A-среднее
IF ROT SWAP THEN \ A (B C) C -> (B A C), иначе B-среднее ничего не делаем
THEN
DROP . DROP \ (A B C) -> - «на месте B всегда будет средний элемент»
;
На тех же данных дает те же результаты. Каждый может убедиться в этом сам. NIP аналогично DROP удаляет элемент на стеке, но не вершину, а предпоследний, который «под ним».
Третий вариант – это комбинация двух примеров, решенных ранее. Сначала определяется наименьшее и удаляется (пример 12), затем из оставшихся двух найти максимум (пример 6), также удаляем его. Для этого немного переработаем пример 12.
: IF12 ( A B C -> X Y ) \ X Y - 2 числа из A B C, кроме минимума
2DUP > \ A B C -> A B C B>C
IF SWAP THEN \ A B C -> A (B C), «(B C) - отсортирован», теперь минимум либо B, либо A
DUP 2OVER > NIP \ A (B C) -> A (B C) C A>B
IF SWAP DROP ELSE \ A (B C) C A>B -> A C, если A>B, то B-минимум
ROT DROP THEN \ A (B C) C A>B -> (B C), иначе A
. . \ вывод оставшихся двух чисел
;
1 2 3 IF12
3 2 Ok
1 3 2 IF12
3 2 Ok
2 1 3 IF12
3 2 Ok
2 3 1 IF12
3 2 Ok
3 1 2 IF12
2 3 Ok
3 2 1 IF12
2 3 Ok
-1 -2 -3 IF12
-2 -1 Ok
-1 0 -2 IF12
0 -1 Ok
-1 0 1 IF12
1 0 Ok
На тестовых данных корректно удаляется минимальный элемент. Теперь пример 6 должен удалять максимальный элемент.
: IF6 ( A B -> X ) \ X-оставшийся после удаления MAX[A,B]
2DUP < \ A B -> A B A<B
IF DROP ELSE NIP THEN
;
В пример 12 удаляем предпоследнюю строчку вывода обработанных чисел на экран. Они будут параметрами для примера 6. Окончательно получим:
: IF12 ( A B C -> X Y ) \ X Y - 2 числа из A B C, кроме минимума
2DUP > \ A B C -> A B C B>C
IF SWAP THEN \ A B C -> A (B C), «(B C) - отсортирован», теперь минимум либо B, либо A
DUP 2OVER > NIP \ A (B C) -> A (B C) A>B
IF SWAP DROP ELSE \ A (B C) A>B -> A C, если A>B, то B-минимум
ROT DROP THEN \ A (B C) A>B -> (B C), иначе A
;
Теперь пример 13 окончательно примет вид:
: IF13 ( A B C -> X ) \ MIN[A,B,C]<=X<= MAX[A,B,C]
IF12 IF6 .
;
Самый короткий и простой вариант Примера 13, если не считать переработку двух ранее написанных примеров.
Пример 14. Теперь из трех чисел выводим минимум затем максимум. Не зря в предыдущем примере возились с третьим вариантом ее решения, через два других примера. Переделаем пример 12 так, чтобы не удалять минимум, а распечатать его на экран.
: IF12 ( A B C -> X Y ) \ X Y - 2 числа из A B C, кроме минимума
2DUP > \ A B C -> A B C B>C
IF SWAP THEN \ A B C -> A (B C), «(B C) - отсортирован», теперь минимум либо B, либо A
DUP 2OVER > NIP \ A (B C) -> A (B C) A>B
IF SWAP . ELSE \ A (B C) A>B -> A C, если A>B, то B-минимум
ROT . THEN \ A (B C) A>B -> (B C), иначе A
;
Перепишем пример 6, так чтобы он распечатал MAX[A,B] и удалял со стека лишнее число.
: IF6 ( A B -> ) \ распечатать MAX[A,B]
2DUP > \ A B -> A B A>B
IF DROP ELSE SWAP DROP THEN .
;
Осталось из двух изменённых слов собрать Пример 14.
: IF14 ( A B C -> ) \ распечатать MIN[A,B] распечатать MAX[A,B]
IF12 IF6 ;
1 2 3 IF14
1 3 Ok
1 3 2 IF14
1 3 Ok
2 1 3 IF14
1 3 Ok
2 3 1 IF14
1 3 Ok
3 1 2 IF14
1 3 Ok
3 2 1 IF14
1 3 Ok
-1 -2 -3 IF14
-3 -1 Ok
-1 0 -2 IF14
-2 0 Ok
-1 0 1 IF14
-1 1 Ok
Пример 15. Из трех чисел найти сумму двух наибольших. В отличии от предыдущего вначале ищем максимум из трех, затем второй максимум из двух оставшихся и печатаем их сумму.
: IF12 ( A B C -> X Y ) \ X Y - 2 числа из A B C, кроме минимума
2DUP > \ A B C -> A B C B>C
IF SWAP THEN \ A B C -> A (B C), «(B C) - отсортирован»
ROT OVER OVER < \ A (B C) -> (B C) A C<A
IF ROT ROT ELSE \ (B C) A C<A -> A (B C), если A>C, то A-максимум
ROT THEN \ (B C) A C<A -> C A B, иначе C-максимум
;
После работы переписанного слова IF12 на стеке лежит максимум (A, если A>C или C, в противном случае), за которым идут две оставшиеся исходных числа. Осталось найти максимум из оставшихся модифицированным словом IF6.
: IF6 ( A B -> MAX[A,B] )
2DUP < \ A B -> A B A<B
IF SWAP THEN
DROP
;
Теперь главное слово Примера 15, будет вызывать написанные выше слова и производить сложение результатов их работы. Первое слово IF12 находит максимум и отправляет на место «A», затем «IF6» аналогично, определив второй максимум, ставит на место «B» удалив третий минимальный элемент.
: IF15 ( A B C -> ) \ MAX1[A,B,C]+MAX2[A,B,C]
IF12 IF6 + .
;
1 2 3 IF15
5 Ok
-1 -2 -3 IF15
-3 Ok
0 1 2 IF15
3 Ok
Пример 16. Если данные вещественные числа FA, FB, FC упорядочены по возрастанию, то удвоить их, иначе заменить на противоположенное. В стековом комментарии обозначения «F:» и «I:» говорят о том, в каком стеке происходят изменения (вещественный или целочисленный). Наши исходные данные вещественного типа, а потому вся работа с ними ведется в соответствующем месте, но результаты их сравнения попадают в целочисленный стек, а потому следует особо обращать внимание на это, дабы исключить любые ошибки в понимании кода, и неправильные манипуляции с данными, в следствии чего, либо будет не хватка данных, либо останутся «лишние», баланс на двух стеках будет нарушен, при ошибочных манипуляциях на «не том стеке».
FVARIABLE FA FVARIABLE FB FVARIABLE FC
: IF16 ( FA, FB, FC -> )
FC F! FB F! FA F! \ F: FA FB FC ->
FA F@ FB F@ F< \ I: -> FA<FB
FB F@ FC F@ F< AND IF \ I : [FA<FB]AND[FB<FC]
FA F@ 2E F* FA F! \ FA= FA*2
FB F@ 2E F* FB F! \ FB= FB*2
FC F@ 2E F* FC F! \ FC= FC*2
ELSE \ Иначе заменяем на противоположенные
FA F@ FNEGATE FA F! \ FA= -FA
FB F@ FNEGATE FB F! \ FB= -FB
FC F@ FNEGATE FC F! \ FC= -FC
THEN
FA F@ F. \ Печатаем результат в FA
FB F@ F. \ FB.
FC F@ F. \ FC.
;
- Во второй строчке сохраняем значения в переменные
- В третей и четвертой считываем значения из переменных и сравниваем «FA<FB» и «FB<FC», если оба истина, то исходные числа образуют возрастающую последовательность, а, следовательно, необходимо удвоить исходные числа, что и делает строчки с пятой по седьмую.
- В противном случае меняем знак на противоположенный, что и делают строчки с десятой по двенадцатую.
- И окончательно проверяем результат, распечатав измененные переменные, в строках с четырнадцатой по шестнадцатую.
Проверим работу слова на тестовых данных.
11E-1 21E-1 31E-1 IF16
2.2000000 4.2000000 6.2000000 Ok
31E-1 21E-1 11E-1 IF16
-3.1000000 -2.1000000 -1.1000000 Ok
1E 2E 3E IF16
2.0000000 4.0000000 6.0000000 Ok
2E 2E 3E IF16
-2.0000000 -2.0000000 -3.0000000 Ok
1E 2E 2E IF16
-1.0000000 -2.0000000 -2.0000000 Ok
Пример 17. Если данные вещественные числа FA, FB, FC упорядочены по возрастанию или убыванию, то удвоить их, иначе заменить на противоположенное. Получаем из предыдущего примера добавлением дополнительного условия, проверки на убывание, т. е. двойного сравнения FA>FB>FC. В стандартный список слов входит только сравнение вещественных чисел на «меньше» - «F<» (нет слова «F>»), поэтому заменим два сравнения «FA>FB и FB>FC» на «FB<FA и FC<FB», ибо лень искать библиотеки с такими словами, а лень – двигатель прогресса. Если вам понадобится в будущем слово «F>», то можете воспользоваться таким определением:
: F> ( FA FB -> FA>FB ) FSWAP F< ;
А слова «F>=», «F<=» и «F<>» (не равно, можно обозначить как «F!=») определяются так:
: F>= ( FA FB -> FA>=FB ) FOVER FOVER F> F= OR ;
: F<= ( FA FB -> FA<=FB ) FOVER FOVER F< F= OR ;
: F<> ( FA FB -> FA<>FB ) F= 0= ;
: F!= ( FA FB -> FA!=FB ) F= 0= ;
2E 3E F>= B.
False Ok
3E 2E F>= B.
True Ok
3E 3E F>= B.
True Ok
2E 3E F<= B.
True Ok
3E 2E F<= B.
False Ok
3E 3E F<= B.
True Ok
2E 3E F!= B.
True Ok
3E 3E F!= B.
False Ok
2E 3E F<> B.
True Ok
3E 3E F<> B.
False Ok
FVARIABLE FA FVARIABLE FB FVARIABLE FC
: IF16 ( FA, FB, FC -> X1 X2 X3 )
FC F! FB F! FA F! \ F: FA FB FC ->
FA F@ FB F@ F< \ I: -> FA<FB
FB F@ FC F@ F< AND \ I: -> [FA<FB]AND[FB<FC]
FB F@ FA F@ F< \ I: -> [FA<FB]AND[FB<FC] FB<FA
FC F@ FB F@ F< AND OR \ I : [FA<FB]AND[FB<FC]OR[FB<FA]AND[FC<FB]
IF FA F@ 2E F* FA F! \ FA= FA*2
FB F@ 2E F* FB F! \ FB= FB*2
FC F@ 2E F* FC F! \ FC= FC*2
ELSE \ Иначе заменяем на противоположенные
FA F@ FNEGATE FA F! \ FA= -FA
FB F@ FNEGATE FB F! \ FB= -FB
FC F@ FNEGATE FC F! \ FC= -FC
THEN
FA F@ F. \ Печатаем результат в FA
FB F@ F. \ FB.
FC F@ F. \ FC.
;
Вынесем в отдельные слова три участка кода, которые меняют значение переменных на противоположенное, удваивают их и распечатка результатов. Данные манипуляции вовсе не обязательны, они исключительно для наглядности, разгрузив тело слова IF17, уделяем больше внимания на логику ее работы.
: -FA-FB-FC ( -> )
FA F@ FNEGATE FA F! \ FA= -FA
FB F@ FNEGATE FB F! \ FB= -BA
FC F@ FNEGATE FC F! \ FC= -CA
;
: 2*FAFBFC ( -> )
FA F@ 2E F* FA F! \ FA= FA*2
FB F@ 2E F* FB F! \ FB= FB*2
FC F@ 2E F* FC F! \ FC= FC*2
;
: FAFBFC. ( -> )
FA F@ F. \ Печатаем результат в FA
FB F@ F. \ FB.
FC F@ F. \ FC.
;
Теперь IF17 перепишем:
: IF17 ( FA, FB, FC -> )
FC F! FB F! FA F! \ F: FA FB FC ->
FA F@ FB F@ F< \ I: -> FA<FB
FB F@ FC F@ F< AND \ I: -> [FA<FB]AND[FB<FC]
FB F@ FA F@ F< \ I: -> [FA<FB]AND[FB<FC] FB<FA
FC F@ FB F@ F< AND OR \ I : [FA<FB]AND[FB<FC]OR[FB<FA]AND[FC<FB]
IF 2*FAFBFC \ FA= FA*2 FB= FB*2 FC= FC*2
ELSE -FA-FB-FC \ Иначе заменяем на противоположенные
THEN
FAFBFC.
;
Проверка работы слова:
1E 2E 3E IF17
2.0000000 4.0000000 6.0000000 Ok
3E 2E 1E IF17
6.0000000 4.0000000 2.0000000 Ok
1E 2E 1E IF17
-1.0000000 -2.0000000 -1.0000000 Ok
1E 4E 3E IF17
-1.0000000 -4.0000000 -3.0000000 Ok
3E 2E 3E IF17
-3.0000000 -2.0000000 -3.0000000 Ok
Какой вариант больше нравится вам решать. Если все понятно и в первом варианте, то второй это наглядный пример структурирования кода, не все задачи будут такими тривиальными, а навыки по грамотной организации кода будет полезно развить с самого начала. Может не всем понравится стиль, когда в одной строчке пишут много операторов, например «FC F@ FB F@ F< AND OR». Это дело вкуса. Опытные программисты смогут сами переделать стиль под себя, а увеличивать количество строк мне не нравится. Стековые комментарии должны развеять все непонятные моменты в коде.
Пример 18. Определить номер числа в наборе из трех целых, не равный двум другим, которые равны между собой.
: IF18 ( A B C -> N ) \ N={A,B,C}
2DUP = \ A B C -> A B C B=C
IF 2DROP DROP 1 . EXIT THEN \ Если B=C, то «A» – искомое, печатаем 1 и выходим, иначе, B<>C,
\ но тогда либо A=B, либо A=C
DROP = \ A B C -> A=B , если истина, то «C» №3
IF 3 ELSE 2 THEN . \ иначе, «B» №2
;
1 2 2 IF18
1 Ok
2 1 2 IF18
2 Ok
1 1 2 IF18
3 Ok
Сначала проверяется равенство B=C. Выполнение которой означает, что неравное остальным двум, то есть искомое число – это A, номер один. Печатаем «1» и выходим. В противном случае, возможны два варианта: либо A=B, либо A=C. Достаточно проверить одно из условий. Для определенности проверяется A=B. Если равенство истинно, то искомое число третье, иначе второе.
Пример 19. Аналогичен предыдущему примеру, с тем отличием, что даны четыре целых числа.
: IF19 ( A B C D -> N ) \ N={A|B|C|D}
2DUP = \ A B C D -> A B C D C=D
IF DROP IF18 EXIT THEN \ если C=D удаляем D и сводим задачу к предыдущей, иначе либо C, либо D искомое, неравное остальным трем
DROP = \ A B C D -> A B= C - если истина, то искомое D, №4
IF 4 ELSE 3 THEN . \ иначе C, №3
DROP
;
Проверим, написанное слово:
2 1 1 1 IF19
1 Ok
1 2 1 1 IF19
2 Ok
1 1 2 1 IF19
3 Ok
1 1 1 2 IF19
4 Ok
Все просто. Первая проверка C=D. Выполнение которой означает, что искомого числа среди них нет. Чтобы упростить нам работу удаляется последнее число, что позволяет свести задачу к предыдущему. В противном случае, искомое число либо C, либо D. Тем самым круг поиска резко сужается. Достаточно проверить какое из этих двух чисел отличается от остальных равных между собой. Для определенности проверяется B=C, в случае истинности печатаем 4, иначе 3.
Пример 20. Даны 3 точки FA, FB, FC. Определить которая из двух последних ближе к первой, и определить это расстояние.
Для решения данного примера нужно сравнить два модуля: |FA-FB|<|FA-FC|. При истинности печатаем точку «B» и расстояние |FA-FB|, иначе «C» и соответственно |FA-FC|.
: IF20 ( FA, FB, FC -> ) \ {«B» |FA-FB|} или {«C» |FA-FC|}
FC F! FB F! FA F!
FA F@ FB F@ F- FABS \ |FA-FB|
FA F@ FC F@ F- FABS \ |FA-FB| -> |FA-FB| |FA- FC|
F< \ |FA-FB| |FA- FC| -> |FA-FB|<|FA- FC|
IF .” B “ FA F@ FB F@ F- FABS F.
ELSE .” C “ FA F@ FC F@ F- FABS F.
THEN
;
1E 2E 3E IF20
B 1.0000000 Ok
1E 5E 3E IF20
C 2.0000000 Ok
Пример 21. Произвести анализ точки с целочисленными координатами (X,Y).
- Если координаты точки (0,0), то вывести 0.
- Если координаты точки (x,0) , то вывести 1.
- Если координаты точки (0,y) , то вывести 2.
- Иначе вывести 3.
: IF21 ( X Y -> N )
2DUP 0 = SWAP 0 = AND \ X Y -> X Y {Y=0}AND{X=0}
IF 2DROP 0 . EXIT THEN \ Если {Y=0}AND{X=0}, то печатаем «0» и выходим
0 = \ X Y -> X Y=0
IF DROP 1 . EXIT THEN \ Если {Y=0}, то печатаем «1» и выходим
0 = \ X -> X=0
IF 2 . EXIT THEN \ Если {X=0}, то печатаем «2» и выходим
3 . \ Иначе печатаем «3»
;
Тест написанного слова:
0 0 IF21
0 Ok
1 0 IF21
1 Ok
0 1 IF21
2 Ok
1 2 IF21
3 Ok
Пример 22. Дана точка с координатами (X,Y), которая не лежит на координатных осях. Определить в какой четверти она лежит.
Для этого нужно проверить следующие неравенства:
- X>0 и Y>0 – 1-ая четверть.
- X<0 и Y>0 – 2-ая четверть.
- X<0 и Y<0 – 3-я четверть.
- X>0 и Y<0 – 4-ая четверть.
Заметим, что в первых двух Y-положительный, а в остальных отрицательный. Логично сначала проверить на положительность Y, тем самым сузив поиск, затем проверить X, локализовав четверть окончательно.
: IF22 ( X Y -> N ) \ N – координатная четверть
0 > \ X Y -> X Y>0 - если истина тогда либо 1-ая, либо 2-ая четверть
IF 0 > \ X -> X>0 - если истина, тогда 1-ая четверть, иначе 2-ая
IF .” 1-ая четверть” ELSE .” 2-ая четверть” THEN
ELSE 0 > \ X -> X>0 - иначе либо 3-я, либо 4-ая четверть
IF .” 4-ая четверть” ELSE .” 3-я четверть” THEN
THEN
;
\ Протестируем:
1 1 IF22
1-ая четверть Ok
-1 1 IF22
2-ая четверть Ok
-1 -1 IF22
3-я четверть Ok
1 -1 IF22
4-ая четверть Ok
Пример 23. Даны целочисленные координаты трех вершин прямоугольника, стороны которого параллельны координатным осям, по ним определить координаты четвертой стороны. Для решения этой задачи воспользуемся решенным примером IF18. Так как x4={x1|x2|x3} (то есть либо x4=x1, либо x4=x2, либо x4=x3), причем тот из них, который не равен двум остальным, равным между собой, то задача сильно упрощается. Это вытекает из условия задачи, из того что стороны параллельны координатным осям, следовательно, координаты двух пар вершин у которых абсциссы равны (эти пары вершин образуют стороны параллельные оси OY). Это же в равной степени относится и к y-координатам всех вершин, то есть y4={y1|y2|y3} } (либо y4=y1, либо y4=y2, либо y4=y3).
: IF23 ( X1 Y1 X2 Y2 X3 Y3 -> X4 Y4)
Y ! X ! \ X1 Y1 X2 Y2 X3 Y3 -> X1 Y1 X2 Y2
ROT SWAP Y @ \ X1 Y1 X2 Y2 -> X1 X2 Y1 Y2 Y3
IF18 \ X1 X2 Y1 Y2 Y3 -> X1 X2 Y4
ROT ROT X @ \ X1 X2 Y4 -> Y4 X1 X2 X3
IF18 SWAP \ Y4 X1 X2 X3 -> X4 Y4
;
Чтобы слово работало правильно перепишем подзадачу IF18, чтобы оно оставляло на стеке не номер числа, а саму координату.
: IF18 ( A B C -> N ) \ N={A|B|C}
2DUP = \ A B C -> A B C B=C
IF 2DROP EXIT THEN \ Если B=C, то A – искомое, оставляем «A», иначе, B<>C, но тогда либо A=B, либо A=C
ROT ROT 2DUP = \ A B C -> A=B
IF 2DROP ELSE –ROT 2DROP THEN
;
Разумеется, IF18 необходимо вводить перед ее вызовом, иначе либо будет ошибка если вы ранее ее не вводили, либо сработает старый вариант и результат будет неправильный.
1 1 1 3 7 3 IF23
Ok ( 7 1 )
Пример 24. Вычислить значение функции f(x)={2*sin(x), при x>0 и 6-x, при x<=0} для вещественного аргумента.
: IF24 ( F: X -> F{X} )
FDUP 0E FSWAP F< \ F: X -> X 0<X
IF FSIN 2E F* \ если 0<X истина, то F: X -> 2*sin(X)
ELSE 6E FSWAP F- THEN \ иначе, F: X -> 6-X
;
0E IF24 F.
6.0000000 Ok
1570796E-6 IF24 F.
2.0000000 Ok
Пример 25. Аналогичен предыдущему случаю только работаем с целыми числами. Если {X>2} или {X<-2}, то умножаем на два, иначе на минус три.
: IF25 ( X -> F{X} )
DUP DUP 2 > \ X -> X X X>2
SWAP -2 < OR \ X X X>2 -> X {X>2}OR{X<-2}
IF 2 ELSE -3 THEN *
;
-4 IF25 .
-8 Ok
4 IF25 .
8 Ok
-1 IF25 .
3 Ok
1 IF25 .
-3 Ok
0 IF25 .
0 Ok
Пример 26. Снова работаем с вещественными числами. Сначала рассмотрим положительную полуось: X>0 (вторая строка). Здесь может быть два случая: первый 0<X<2 (проверка в третьей строке, и в четвертой возводим аргумент в квадрат, для получения значения функции), второй - иначе (пятая строка, заменяем аргумент числом «4»), то есть когда X>=2. Перебрав все варианты положительных X, остается рассмотреть (X<=0), вариант иначе (шестая строчка), здесь мы меняем знак аргумента на противоположенный.
: IF26 ( F: X ->)
FDUP 0E FSWAP F< \ F: X -> X 0<X - если X>0
IF FDUP 2E F< \ F: X -> X X<2, - если 0<X<2
IF FDUP F* \ F: X -> X*X
ELSE FDROP 4E THEN \ иначе F: 4
ELSE FNEGATE THEN \ если {0<X} – ложь, то есть X<=0
;
-1E IF26 F.
1.0000000 Ok
1E IF26 F.
1.0000000 Ok
15E-1 IF26 F.
2.2500000 Ok
3E1 IF26 F.
4.0000000 Ok
Если такой перебор вариантов вам не понятен, то приведем более традиционный метод. Используем раннее определенные слова:
: F> ( FA FB -> FA>FB ) FSWAP F< ;
: F>= ( FA FB -> FA>=FB ) FOVER FOVER F> F= OR ;
: F<= ( FA FB -> FA<=FB ) FOVER FOVER F< F= OR ;
: F<> ( FA FB -> FA<>FB ) F= 0= ;
: F!= ( FA FB -> FA!=FB ) F= 0= ;
: IF26 ( F: X ->)
FDUP 0E F<= \ F: X -> X X<=0
IF FNEGATE EXIT THEN \ F: X -> -X, при X<=0
FDUP FDUP 0E F> FSWAP 2E F< AND \ F: X -> X 0<X<2
IF FDUP F* ELSE \ F: X -> X**2, при 0<X<2
FDROP 4E THEN \ F: X -> 4, иначе
;
Хотя и не все приведенные слова были использованы в примере, будет полезно их добавить к себе в файл, сохранив с расширением «.F», открыв его Форт-система уже будет иметь в своем словаре эти инструменты. И вам не придется заниматься рутиной, «копи-пастить».
Пример 27. В этом примере сложно только условие, которое означает что, если целая часть числа четная (включая ноль), то печатаем единицу, если нечетная – минус единицу, а если число отрицательное, то выводим на экран ноль. Обратите внимание, что в стековой нотации входящий параметр X находится на стеке вещественных чисел (что обозначено «F:»), а результат на целочисленном стеке (I:). Единственное зачем это важно, чтобы вы не путались и не пытались в других примерах сложить их, или провести какие-то другие совместные манипуляции, что неизбежно приведет к некорректным результатам, необязательно с выводом на экран сообщения об ошибке.
: IF27 ( F: X -> I: {0|1|-1} ) \ здесь фигурные скобки означают множество, 0,1 или -1
FDUP F0< \ если число F в вещественном стеке <0,
IF 0 EXIT THEN \ то оставляем «0»
F>D D>S \ иначе превращаем в целое и работаем с обычным стеком, для целых чисел
2 MOD \ если остаток при делении на 2 равен 1, что трактуется как истина
IF -1 \ оставляем -1
ELSE 1 THEN \ иначе 1
;
1.9990E IF27 .
-1 Ok
2.9990E IF27 .
1 Ok
0E IF27 .
1 Ok
-1.9990E IF27 .
0 Ok
-2.9990E IF27 .
0 Ok
Пример 28. По номеру года определить количество дней в году. По сути нужно определить является ли год високосным (и вывести 366 в случае истинности), если нет - печатаем 365.
: IF28 ( Y -> {365|366} ) \ Y – год, выводим 365 (обычный) или 366 (високосный)
DUP 4 MOD \ Если остаток от деления на 4 не НОЛЬ, что трактуется как
IF DROP 365 EXIT THEN \ истина, оставляем 365 и выходим
DUP 100 MOD 0 = \ Y -> Y {Y MOD 100}=0
SWAP 400 MOD AND \ Y {Y MOD 100}=0 -> {Y MOD 100}=0&{Y MOD 1000}
IF 365 EXIT THEN \ не високосный, если делится на 100 и не делится на 1000
366 \ иначе високосный
;
300 IF28
365 Ok
1300 IF28
365 Ok
1900 IF28
365 Ok
1200 IF28
366 Ok
2000 IF28
366 Ok
Пример 29. Для данного целого числа вывести строку-описание вида: (знак) (четность). Дублируем вершину стека и сравниваем с нулем (вторая строка), если истина выводим на экран " Ноль" и завершаем работу слова, иначе снова дублируем и проверяем на отрицательность, в результате получим на экране сообщение либо «Отрицательное» (в случае истины), либо «Положительное» (в противном случае). В четвертой строке делим на два и по остатку определяем четность исходного числа.
: IF29 ( N -> )
DUP 0 = IF ." Ноль" DROP EXIT THEN \ N=0
DUP 0 < \ N -> N N<0
IF ." Отрицательное " ELSE ." Положительное " THEN
2 MOD \ N -> {N MOD 2}
IF ." нечетное число" ELSE ." четное число" THEN
;
-3 IF29
Отрицательное нечетное число Ok
-2 IF29
Отрицательное четное число Ok
-1 IF29
Отрицательное нечетное число Ok
0 IF29
Ноль Ok
1 IF29
Положительное нечетное число Ok
2 IF29
Положительное четное число Ok
3 IF29
Положительное нечетное число Ok
Пример 30. Нужно в описании вывести четность числа и его значность (количество его цифр). Вход число в диапазоне 1 -999. Не сильно отличается от предыдущего, аналогично определяем четность. Далее поделив на «100» по частному (если он больше нуля) узнаем трехзначное ли это число, если истина, то задача решена, иначе проверяем на двузначность, если и здесь ложь, делаем вывод, что число однозначное.
: IF30 ( N -> )
DUP 2 MOD \ N -> N {N MOD 2}
IF ." Нечетное " ELSE ." Четное " THEN \ Если {N MOD 2}=1, «Нечетное», иначе «Четное»
DUP 100 / \ Если Целая_часть{N/100}>0, то трехзначное
IF ." трехзначное число " DROP EXIT THEN
10 / \ Если Целая_часть{N/10}>0, то двузначное
IF ." двузначное число " EXIT THEN
." однозначное число " \ иначе однозначное
;
5 IF30
Нечетное однозначное число Ok
16 IF30
Четное двузначное число Ok
355 IF30
Нечетное трехзначное число Ok
998 IF30
Четное трехзначное число Ok