Читать книгу «Язык программирования Форт (Forth). Решение задач по программированию. Версия 2.» онлайн полностью📖 — Arsen Gonian — MyBook.
agreementBannerIcon
MyBook использует cookie файлы
Благодаря этому мы рекомендуем книги и улучшаем сервис. Оставаясь на сайте, вы соглашаетесь с политикой обработки персональных данных.
cover

FSWAP FOVER F*      \ Y C1 -> C1 Y*C1= CY

;

3E 9E 10E B33 F. F.

30.000000 3.0000000 Ok

Проверили на тех же данных. И снова не забываем об обратном порядке при печати со стека, не важно с какого (целочисленного или вещественного). Чтобы в начале вывести цену за 1 кг, можно использовать слово FSWAP перед «F.» или переписать строчку

FSWAP FOVER F*      \ Y C1 -> C1 Y*C1= CY

таким образом

FDUP F. F*            \ Y C1 -> Y*C1= CY

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

Пример 34. Продолжение детских задачек.

: B34 ( X A Y B -> CX CY CX/CY )            \ CX=A/X CY=B/Y CX/CY

SWAP /                        \ X A Y B -> X A B/Y=CY

SWAP ROT /                  \ X A CY -> CY A/X=CX

SWAP 2DUP /                  \ CY CX -> CX CY CX/CY

;

5 30 5 15 B34

Ok ( 6 3 2 )

Цена шоколадных конфет равна 30/5= 6 р/кг, ирисок 15/5=3 р/кг, и соответственно шоколадные конфеты дороже ирисок в 6/3=2 раза. Перепишем для вещественных аргументов, чтобы не терять точность для «неудобных данных».

: B34 ( X A Y B -> CX CY CX/CY )            \ CX=A/X CY=B/Y CX/CY

FSWAP F/                  \ X A Y B -> X A B/Y=CY

FSWAP FROT F/                  \ X A CY -> CY A/X=CX

FSWAP FOVER FOVER F/      \ CY CX -> CX CY CX/CY

;

5E 30E 5E 15E B34 F. F. F.

2.0000000 3.0000000 6.0000000 Ok

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

Пример 35. Детская задачка на движение лодки в стоячей и движущейся воде.

VARIABLE T2

: B35 ( V U T1 T2 -> S )      \ S=S1+S2, S1=V*T1, S2=(V-U)*T2

T2 !                  \ V U T1 T2 -> V U T1

ROT DUP ROT *            \ V U T1 -> U V V*T1=S1

SWAP ROT –            \ U V S1 -> S1 V-U

T2 @ *                  \ S1 V-U -> S1 [V-U]*T2=S2

+                  \ S=S1+S1

;

Манипуляции на стеке неоправданно усложняются с четырьмя и более элементами, поэтому мы сохраним значение T2 в одноименной переменной. Сперва мы вычисляем путь S1 в стоячей воде, затем считывая время T2 из переменной – путь S2 и соответственно ему скорость. Ответ есть сумма двух путей. Проверим написанное слово.

10 5 1 1 B35

Ok ( 15 )

1 час в стоячей воде со скоростью 10 – это путь равный 10*1=10 и такое же время со скоростью 10-5=5. В итоге общий путь 10+5=15.

С вещественными данными слово будет иметь вид:

FVARIABLE FT2

: B35 ( V U T1 T2 -> S )      \ S=S1+S2, S1=V*T1, S2=(V-U)*T2

FT2 F!                  \ V U T1 T2 -> V U T1

FROT FDUP FROT F*      \ V U T1 -> U V V*T1=S1

FSWAP FROT F-            \ U V S1 -> S1 V-U

FT2 F@ F*            \ S1 V-U -> S1 [V-U]*T2=S2

F+                  \ S=S1+S1

;

10E 5E 1E 1E B35 F.

15.000000 Ok

На предыдущих данных, как и положено, дает тот же результат, но с типом float.

Пример 36. Без комментариев, сразу приведем решение.

: B36 ( V1 V2 S0 T -> S )            \ S=S0+(V1+V2)*T

2SWAP + * +            \ V1 V2 S0 T -> S0+T*(V1+V2)

;

3 5 100 2 B36

Ok ( 116 )

\ 3+5=8, 8*2=16, 16+100=116.

FVARIABLE FS0

: B36 ( V1 V2 S0 T -> S )            \ S=S0+(V1+V2)*T

FSWAP FS0 F!            \ V1 V2 S0 T -> V1 V2 T

FROT FROT F+ F*      \ V1 V2 T -> T*(V1+V2)

FS0 F@ F+            \ T*(V1+V2)+S0

;

3E 5E 100E 2E B36 F.

116.00000 Ok

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

Пример 37. Абсолютно аналогичен предыдущему примеру, за исключением, двух моментов – сумма заменяется разностью, и разность помещается в модуль.

: B37 ( V1 V2 S0 T -> S )            \ S=|S0-T*(V1+V2)|

2SWAP + * – ABS      \ V1 V2 S0 T -> |S0-T*(V1+V2)|

;

3 5 100 2 B37

Ok ( 84 )

\ 3+5=8, 8*2=16, |100-16|=84. Проверим как работает модуль, для этого положим S0=10.

3 5 10 2 B37

Ok ( 6 )

\ 3+5=8, 8*2=16, |10-16|=6. Все верно, код работает корректно.

FVARIABLE FS0

: B37 ( V1 V2 S0 T -> S )            \ S=|T*(V1+V2)-S0|

FSWAP FS0 F!            \ V1 V2 S0 T -> V1 V2 T

FROT FROT F+ F*      \ V1 V2 T -> T*(V1+V2)

FS0 F@ F- FABS            \ T*(V1+V2) –> |T*(V1+V2)-S0|

;

3E 5E 100E 2E B37 F.

84.000000 Ok

3E 5E 10E 2E B37 F.

6.0000000 Ok

Для вещественного аргумента мы получили тот же ответы в соответствующем формате.

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

: B38 ( A B -> X )                  \ X=-B/A

–1E F* FSWAP F/            \ A B -> -B/A

;

10E 3E B38 F.

–0.3000000 Ok

Код «-1E F*» можно заменить на «FNEGATE», который лучше, но для начала может быть менее наглядным.

Пример 39. Решение квадратного уравнения. Обратите внимание, что в названии вещественных переменных опущены префиксы «F», так как в этих словах используются переменные только одного типа. Если вас это пугает, то вы легко можете добавить их. Если все слова вы добавляете в один файл, то эта проблема будет актуальной, так же вы можете в названиях слов добавлять префикс I (Integer), для функций с целочисленными аргументами и F, с вещественными, иначе SP-Forth, будет выводить сообщения «B39 isn't unique ()», что не является ошибкой, но предупреждением, о том, что при вызове слова будет вызвано определенное последней слово. Могут возникнуть скрытые ошибки логики работы. Так если вы определили целочисленное слово, затем вещественное и после выполняете попеременно слова для разных аргументов, то как было сказано будет вызвана исключительно последняя реализация, что неизбежно приведет к ошибке, связанное с тем что слова будут работать с аргументами в разных стеках, в итоге, будет либо нехватка аргументов, либо порча других данных не предназначенных для этого. Изменив названия при помощи префиксов, вы избавитесь от этих проблем.

FVARIABLE A

: B39 ( A B C -> X1 X2)      \ X1,X2=(-B+-SQRT(D))/2*A

FROT FDUP A F!       F*            \ A B C -> B C*A

FOVER FDUP F*                  \ B C*A -> B C*A B^2

FSWAP 4E F* F- FSQRT            \ B C*A B^2 -> B SQRT{B^2-4*C*A=D}

FSWAP -1E F* FSWAP            \ B D^0.5 -> -B D^0.5

FOVER FOVER F+ A F@            \ -B D^0.5 -> -B D^0.5 –B+D^0.5 A

2E F* F/                  \ -B D^0.5 –B+D^0.5 A -> -B D^0.5 [–B+D^0.5]/[A*2]=X1

FROT FROT F- A F@            \ -B D^0.5 X1 -> X1 –B-D^0.5 A

2E F* F/                  \ X1 –B-D^0.5 A -> X1 [–B-D^0.5]/[A*2]=X2

FOVER FOVER F<            \ X1 X2 –> X1 X2 X1<X2?

IF FSWAP THEN

;

1E 4E 1E B39 F. F.

–3.7320508 -0.2679491 Ok

X^2+4*X+1=0, D=4^2-4*1*1=16-4=12. X1,X2=(-4+-12^0.5)/(2*1)=-2+-SQRT(3)

X1=-2- 1,732= -3,732, X2=-2+1,732=-0,268.

А вот случай, когда D=0

1E 2E 1E B39 F. F.

–1.0000000 -1.0000000 Ok

Если манипуляции со стеком вам не понятны, то мы можем переписать последнее слово с использованием 3-переменных A, B, C, по классической схеме.

FVARIABLE A

FVARIABLE B

FVARIABLE C

: B39 ( A B C -> X1 X2)      \ X1,X2=(-B+-SQRT(D))/2*A, X1,X2

C F! B F! A F!      \ A B C ->

B F@ FDUP -1E F* FSWAP FDUP F*      \ -> -B B^2

A F@ C F@ F* -4E F* F+ FSQRT            \ -B B^2 -> -B {B^2+A*C*(-4)}^0.5=D^0.5

FOVER FOVER F+                  \ -B D^0.5 -> -B D^0.5 -B+D^0.5

A F@ 2E F* F/                        \ -B D^0.5 -B+D^0.5-> -B D^0.5 [-B+D^0.5]/[A*2]=X1

FROT FROT F-                        \ -B D^0.5 X1 -> X1 -B-D^0.5

A F@ 2E F* F/                        \ X1 [-B-D^0.5]/[A*2]=X2

FOVER FOVER F<                  \ X1 X2 X1<X2?

IF FSWAP THEN

;

1E 4E 1E B39 F. F.

–3.7320508 -0.2679491 Ok

Ответ как в первом случае. Какой проще и понятнее решайте сами.

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

FVARIABLE A1 FVARIABLE B1 FVARIABLE C1

FVARIABLE A2 FVARIABLE B2 FVARIABLE C2

: B40 ( A1 B1 C1 A2 B2 C2 -> X Y ) \ X=(C1*B2-C2*B1)/D, Y=(A1*C2-A2*C1)/D, D=A1*B2-A2*B1

C2 F! B2 F! A2 F! C1 F! B1 F! A1 F!             \ A1 B1 C1 A2 B2 C2 ->

A1 F@ B2 F@ F* A2 F@ B1 F@ F* F-             \ -> A1*B2-A2*B1=D

FDUP .” D=“ FDUP F. CR                         \ D -> D D

C1 F@ B2 F@ F* C2 F@ B1 F@ F* F- FSWAP F/       \ D D -> D (C1*B2-C2*B1)/D=X

A1 F@ C2 F@ F* A2 F@ C1 F@ F* F- FROT F/       \ D X -> X (A1*C2-A2*C1)/D=Y

;

Здесь мы создали 6 новых вещественных переменных, чтобы не путаться в коэффициентах.

Первая строка – стандартное описание логики работы слова.

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

Вычисление D.

Дублируем D, так как он нам понадобится два раза, также мы его копируем и печатаем для проверки промежуточных вычислений. После отладки код «.” D=“ FDUP F. CR» можно удалять.

Вычисление X

Вычисление Y

Проверим корректность работы слова на следующих данных

3x+5y=7      (A1=3      B1=5      C1=7)

6x+y=4            (A2=6      B2=1      C2=4)

3E 5E 7E 6E 1E 4E B40 F. F.

D= -27.000000

1.1111111 0.4814814 Ok

D=3*1-6*5=3-30=-27, совпадает с результатом выданном словом. X=(7*1-4*5)/(-27)=13/27=(0,481). Y=(3*4-6*7)/(-27)=(12-42)/(-27)=30/27=1,(1). Видим, что слово работает корректно.

Integer 1-30

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

Пример 1. Перевести см в м без округления. Здесь вы увидите всю красоту Форта.

Rm=Rcm/100 – целочисленно

: I1 ( Rcm -> Rm )

100 /

;

503 I1

Ok ( 5 )

Что означает в 503 см содержится 5 полных метров.

Пример 2. Отличается от предыдущего заменой 100 на 1000.

Mt= Mkg/1000

: I2 ( Mkg -> Mt )

1000 /

;

12683 I2

Ok ( 12 )

В 12683 кг 12 полных тонн.

Пример 3. Так же отличается от предыдущего константой. Теперь 1000 меняем на 1024.

DkB= DB/1024

: I3 ( DB -> DkB )

1024 /

;

2050 I3

Ok ( 2 )

2050 B – это 2 полных kB.

Пример 4. Так же простейшая задачка на целочисленное деление.

Для определенности A>B, результат равен A/B.

: I4 ( A B -> A/B )

/

;

15 4 I4

Ok ( 3 )

В отрезке длиной 15 размещается 3 целых отрезка длиной 4.

Пример 5. Отличается от предыдущего примера заменой целочисленного деления на взятие остатка от деления.

: I5 ( A B -> остаток{A/B} )

MOD

;

15 4 I5

Ok ( 3 )

15/4 – остаток равен 3 – все верно, результат теста корректный.

Примеры 1-5 настолько просты, что даже нет необходимости создавать соответствующие слова. Можно просто ввести число-операнд затем тело слова. Результат получите в скобках на стеке. Чтобы распечатать его и не засорять стек нажмите «.» и «Enter». Перепишем эти примеры для наглядности.

«503 100 / .»

«12683 1000 / .»

«2050 1024 / .»

«15 4 / .»

«15 4 MOD .»

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

Пример 6. Вывести число десятков и единиц двузначного числа. Для этого используем операцию /MOD, которая одновременно вычисляет и целую часть, и остаток от деления.

: I6 ( AB -> A B )

10 /MOD SWAP

;

45 I6

Ok ( 4 5 )

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

45 I6 SWAP . .

4 5 Ok

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

: I6. I6 SWAP . . ;

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

45 I6.

4 5 Ok

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

Разумеется, эти два слова можно объединить в одно по схеме:

: I6. 10 /MOD SWAP SWAP . . ;

: I6. 10 /MOD . . ;

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

Пример 7. Вычислить сумму и произведение цифр двузначного числа. Дальнейшее развитие предыдущего примера.

: I7 ( AB -> A+B A*B )

10 /MOD      \ AB -> A B

2DUP +            \ A B -> A B A+B

ROT ROT *      \ A B A+B -> A+B A*B

;

45 I7

Ok ( 9 20 )

Сумма цифр числа 45 равна 4+5=9, а произведение 4*5=20, тест корректен.

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

Пример 8. Перестановка местами цифр в двузначном числе.

: I8 ( AB -> BA )

10 /MOD SWAP 10 * +

;

45 I8

Ok ( 54 )

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

Пример 9. Вывести число сотен в трехзначном числе.

: I9 ( ABC -> A )

100 /

;

578 I9

Ok ( 5 )

Все верно. Число сотен в числе 578 равно 5.

Пример 10. Аналогичен предыдущему. Выводим сначала число единиц затем десятков.

: I9 ( ABC ->C B )

10 /MOD 10 MOD

;

123 I9

Ok ( 3 2 )

Пример 11. Вычислить сумму и произведение цифр трехзначного числа.

: I11 ( ABC -> A+B+C A*B*C )

10 /MOD 10 /MOD      \ ABC -> C B A

DUP 2OVER + +            \ C B A -> C B A A+C+B

SWAP 2SWAP * *      \ C B A A+C+B -> A+C+B A*C*B

;

456 I11

Ok ( 15 120 )

Сумма 4+5+6=15, а произведение 4*5*6=120.

Пример 12. Перевернуть трехзначное число справа на лево.

: I12 ( ABC -> CBA )

10 /MOD 10 /MOD      \ ABC -> C B A

SWAP 10 * +            \ C B A -> C BA

SWAP 100 * +            \ C BA -> CBA

;

123 I12

Ok ( 321 )

Вот так просто Форт переворачивает трехзначное число «задом наперед».

Пример 13. В трехзначном числе сотни перенести в крайнее правое положение, вместо единиц.

: I13 ( ABC -> BCA )

100 /MOD      \ ABC -> BC A

SWAP 10 * +      \ BC A -> BCA

;

123 I13

Ok ( 231 )

Пример 14. Аналогичен предыдущему. Довольно несложная задачка.

: I14 ( ABC -> CAB )

10 /MOD      \ ABC -> C AB

SWAP 100 * +      \ C AB -> CAB

;

123 I14

Ok ( 312 )

Пример 15. В трехзначном числе переставить цифры сотен и десятков местами.

: I15 ( ABC -> BAC )

10 /MOD 10 /MOD      \ ABC -> C B A

10 * SWAP 100 * + +      \ C B A -> BAC

;

123 I15

Ok ( 213 )

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

Пример 16. Поменять местами десятки и единицы в трехзначном числе.

: I16 ( ABC -> ACB )

10 /MOD 10 /MOD      \ ABC -> C B A

100 * + SWAP 10 * +      \ C B A -> ACB

;

123 I16

Ok ( 132 )

Пример 17. Довольно тривиальная задачка. В числе, большем 999, определить число сотен. Сначала отсекаем часть числа до сотен, поделив на 1000 и взяв только остаток. Затем поделив, полученное число на 100, и взяв целую часть, узнаем количество сотен.

: I17 ( A -> X )      \ X – число сотен

1000 MOD 100 /

;

123456 I17

Ok ( 4 )

Пример 18. Абсолютно идентична предыдущему примеру и также примитивна. В аналогичном числе найти число тысяч (1000 заменяется на 10000, а 100 на 1000).

: I18 ( A -> X )      \ X – число тысяч

10000 MOD 1000 /

;

123456 I18

Ok ( 3 )

Пример 19. Дано S секунд перевести в количество полных минут M.

: I19 ( S -> M )      \ M – результат в минутах

60 /

;

179 I19

Ok ( 2 )

Пример 20. Аналогична предыдущему. Переводим секунды S в число полных часов H.

: I20 ( S -> H )      \ H – результат в часах

3600 /

;

7201 I20

Ok ( 2 )