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 )
О проекте
О подписке