[<>]
~~TOC wide~~
====== Глава IV. Функции и подпрограммы ======
\\
Любая по-настоящему полезная классификация
содержит от трёх до шести категорий.
—//Энон//
В [[msx:basic:]] различают следующие основные типы функций:
- встроенные функции:
- встроенные числовые функции;
- встроенные функции преобразования;
- встроенные функции для работы со строками;
- функции пользователя.
Встроенные функции называют иначе //стандартными// функциями, вычисление значений которых реализуется специальными программами, постоянно находящимися в памяти компьютера. Каждая такая программа имеет уникальное //имя//, по которому и происходит обращение к ней (''SIN'', ''LOG'', ''VAL'', ''MID$'' и т.д.).
К функциям //пользователя// относятся те функции, которые пользователь определяет в программе сам.
Заметим, что встроенные //числовые// функции мы уже изучили (см. [[001#n174|раздел I.7.4.]])!
{{anchor:n41}}
===== IV.1 Встроенные функции преобразования =====
Напомним, что множество значений строковых переменных — это множество упорядоченных наборов символов. Упорядоченность означает, что строковые значения различаются не только набором, но и последовательностью символов, например, "краб" и "брак" — это разные значения. Количество символов в значении строковой переменной меняется в пределах от 0 до 255.
В дальнейшем, //строкой// ''A$'' или //словом// ''A$'' будем называть значение строковой переменной ''A$''.
Обратим особое внимание на то, что строка может не содержать символов. Такая строка называется //пустой// и обозначается %% "" %% (кавычки идут подряд). Длина этой строки равна 0. Не следует путать пустую строку со строкой, содержащей //пустой// символ (пробел). Строка, содержащая пробел, обозначается %% " " %% (между кавычками есть пробел) и имеет длину 1.
Отметим, что символ "пробел" — равноправный с остальными символ!
{{anchor:n411}} {{anchor:len}}
==== IV.1.1. LEN–функция ====
Её общий вид:
LEN(α)
,
где:
* ''LEN'' ("LENgth" — "длина") — служебное слово;
* α — строковое выражение.
Результатом функции LEN(α) является длина значения строкового выражения α (или что то же самое, количество символов в строке α), LEN(α)∈[0,255].
Напомним, что в том месте дисплейной строки, где надо обратить Ваше внимание на наличие символа пробел (" "), мы будем использовать символ "␣".
{{anchor:e0411-01}} __//Пример 1//__. \\ {{.examples:0411-01.bas|}} \\ [[+tab|wmsxbpge>0411-01.bas]]
NEW
Оk
10 X$="парк": Y$="детский парк":Z$=" ау! ":u$="":w$=" "
20 PRINT LEN(X$);LEN(Y$);LEN(Z$);len(u$);len(w$)
run
␣4␣␣12␣␣5␣␣0␣␣1
Ok
Ещё раз подчеркнём, что //пробел// — такой же символ, как, например "а"!
Эта функция, пожалуй, чаще всего используется при работе со строковыми переменными. С её помощью, например, можно определить, не является ли строка пустой до выполнения действий, которые не могут выполняться над пустой строкой (%% "" %%).
{{anchor:e0411-02}} __//Пример 2//__. \\ {{.examples:0411-02.bas|}} \\ [[+tab|wmsxbpge>0411-02.bas]]
10 IF LEN(A$)>0 THEN PRINT "Все в порядке!"
ELSE PRINT "Строка пустая!"
'Проверка, является ли строка пустой, т.е. не содержащей ни одного символа.
{{anchor:e0411-03}} __//Пример 3//__. \\ {{.examples:0411-03.bas|}} \\ [[+tab|wmsxbpge>0411-03.bas]]
NEW
Ok
10 'Выравнивание слов по правому краю.
20 FOR I=1 TO 3:READ A$
30 ?TAB(20-LEN(A$));A$:NEXTI
40 DATA "Хакер-","фанатичный","программист"
run
␣␣␣␣␣␣␣␣␣␣␣␣␣␣Хакер-
␣␣␣␣␣␣␣␣␣␣фанатичный
␣␣␣␣␣␣␣␣␣программист
Ok
Сравните функцию ''LEN()'' с функцией __длин__(А), где __длин__(А) — длина текста A, в школьном алгоритмическом языке [[bibliography#b11|[11, с.53]]]!
{{anchor:n412}} {{anchor:instr}}
==== IV.1.2. INSTR–функция ====
Общий вид функции:
INSTR([n,]α,β)
,
где: ''INSTR''("IN STRing" — "в строке") — служебное слово;
* n — арифметическое выражение, целая часть значения которого должна принадлежать [1,255] (по умолчанию n=1);
* α — строковое выражение;
* β — строковое выражение.
Эта функция исследует значение строкового выражения α слева направо, начиная с символа, номер которого задан величиной целой части значения параметра n или с его первого символа (по умолчанию). Значением функции является номер позиции в значении α, с которой значение β первый раз встречается в α. Если значение β не найдено, то результатом функции ''INSTR'' является 0 (как говорят, "функция ''INSTR'' возвращает значение 0"). Значением выражения α или β может быть пустая строка.
При работе с функцией ''INSTR'' возможны следующие ошибки.
Сообщение: "Illegal function call" \\ (//"Неправильный вызов функции"//) \\
возникает, если значение арифметического выражения n неположительно, а сообщение: "Type mismatch" \\ (//"Несоответствие типов"//), \\ если α или β нестрокового типа. Это сообщение выдаётся очень часто при работе с функциями преобразования и с функциями для работы со строками; как правило, причиной этого служит пропущенный знак "$" после имени строковой переменной или имени строковой функции.
__//Пример//__.
NEW
Оk
10 INPUT X$,Y$,N
20 PRINT INSTR(N,X$,Y$)
run
? ХАФАНАНА,НА,3
5
Ok
run
? ХАФАНАНА,НА,100
0
Ok
run
? "","",5
0
Ok
run
? KEN,"",2
2
Ok
run
? Кузьма Кузьмич,Кузьм,1.5
1
Ok
run
? Кузьма Кузмич,Кузьм,0
Illegal function call in 20
Ok
{{anchor:n413}} {{anchor:val}}
==== IV.1.3. VAL–функция ====
//Цифровая// строка — это строка, содержащая любое количество расположенных в любом месте строки пробелов, знак "плюс" или "минус", десятичные цифры и десятичную точку.
Цифровая строка также может содержать:
- запись числа в экспоненциальном представлении (в форме с плавающей точкой);
- запись числа в двоичном, восьмеричном и шестнадцатеричном представлениях; в этих случаях цифровая строка называется //двоичной, восьмеричной и шестнадцатеричной// цифровой (символьной) строкой соответственно.
В ряде случаев удобнее работать с числами, а не с их представлениями в виде цифровых строк. Цифровую строку можно преобразовать в числовое значение с помощью функции преобразования ''VAL'', общий вид которой:
VAL(α)
,
где: ''VAL''("VALue" — "значение") — служебное слово;
* α — строковое выражение.
Преобразование начинается с крайнего левого символа значения строкового выражения α и заканчивается, когда преобразован последний символ значения α, либо когда встретился нецифровой символ.
__//Примеры//__:
Ok
print VAL("12.5E-3")
.0125
Ok
Ok
print val("&b1");val("&o76");val("&hF")
␣1␣␣62␣␣15
Ok
Если строка является пустой или начинается с нецифрового символа, то функция ''VAL'' возвращает значение 0.
__//Примеры//__:
* 1) Ok
? VAL("a12");VAL("23A12:");VAL("")
␣0␣␣23␣␣0
Ok
* 2) Ok
? VAL("-1.24")
-1.24
Ok
Если одним из символов цифровой строки является восклицательный знак, то в зависимости от его расположения возможны следующие случаи.
__//Примеры//__:
* 1)Ok
? VAL("1234.56789!")
␣1234.57
Ok
результат преобразования: число одинарной точности (шесть значащих цифр!)
* 2)Ok
? VAL("34!5.1")
␣34
Ok
* 3)Ok
? VAL("3456789!.7")
␣3456790
Ok
* 4)Ok
? VAL("!1")
␣0
Ok
И наконец, парочка патологических случаев:
* 5)Ok
? VAL("&")
Syntax error
Ok
* 6) Ok
? VAL("&H")
0
Ok
Таким образом, функция ''VAL'' позволяет выделить цифры, входящие в значение строкового выражения, и образовать из них число для последующей математической обработки.
{{anchor:n414}} {{anchor:str}}
==== IV.1.4. STR$–функция ====
Часто бывает необходимо осуществить преобразование числа в цифровую строку, например, число 1234.56 преобразовать в цифровую строку %% "1234.56" %%.
Это преобразование осуществляет функция ''STR$'', общий вид которой:
STR$(α)
,
где:
* ''STR'' ("convert to STRing" — "преобразовать в строку") — служебное слово;
* α — арифметическое выражение.
Данная функция преобразует значение арифметического выражения α в цифровую строку, что позволяет выделять и обрабатывать каждый символ (цифру) полученной строки с помощью //строковых// (!) функций.
__//Примеры//__
* 1)
Ok
print STR$(-15)
-15
Ok
* 2)
Ok
print STR$(1/3)
␣.33333333333333
Ok
* 3)
Ok
? STR$(1.E-3)
␣1E-03
Ok
* 4)
Ok
? STR$(&b111);STR$(&o23);STR$(&HF1)
␣7␣19␣241
Ok
Обратите //особое// внимание на следующие примеры (первый символ в получаемых цифровых строках зарезервирован для указания знака числа):
* 5)
Оk
? LEN(STR$(20))
3
Ok
* 6) Ok
? LEN(STR$(-56.20))
5
Ok
* 7)
Оk
? LEN(STR$(-5620))
5
Ok
* 8)
Ok
? LEN(STR$(1.2E-27)
8
Ok
Ещё один пример использования функции ''STR$'': оператор
PRINT n;"X"
выводит на экран дисплея пробел между последней цифрой значения арифметического выражения n и символом "X". Для исключения этого пробела воспользуйтесь оператором
PRINT STR$(n);"X"
* 9)NEW
Ok
10 INPUT N
20 PRINT N;"i"
30 ? STR$(N);"i"
run
? 10
10 i
10i
Ok
* 10) NEW
Ok
10 INPUT N
20 PRINT "i";n
30 ?"i";MID$(STR$(N),2)
run
? 10
i 10
i10
Ok
(о строковой функции ''MID$()'' см. в [[#n421|разделе IV.2.1.]]).
Отметим, что обычно в программах функции ''VAL()'' и ''STR$()'' используются совместно.
* 11)
Ok
input N$:print STR$(VAL(N$))
? 23123.45
␣23123.45
Ok
? -4.7
-4.7
Ok
? &HFF
␣255
Ok
однако…
? 12ABBA
␣12
Ok
* 12)
Ok
input N:print VAL(STR$(N))
? 12
␣12
Ok
? -14.6E-2
-.146
Ok
? &b111
␣7
Ok
? &o21
␣17
Ok
? &h1E
␣30
Ok
* {{anchor:e0414-13}} 13) найдите натуральные числа, не превосходящие заданного N и равные сумме кубов своих цифр.
\\ {{.examples:0414-13.bas|}} \\ [[+tab|wmsxbpge>0414-13.bas]]
NEW
Ok
10 INPUT N
20 FOR I=1 TO N
30 A$=STR$(I):S=0
40 FOR J=2 TO LEN(A$)
50 S=S+VAL(MID$(A$,J,1))^3
60 NEXT J
70 IF S=I THEN PRINTI;:NEXT I ELSE NEXT I
run
? 400
␣1␣␣153␣␣370␣␣371
Ok
* {{anchor:e0414-14}} 14) определите наибольшую из цифр, используемых в десятичной записи натурального числа M.
\\ {{.examples:0414-14.bas|}} \\ [[+tab|wmsxbpge>0414-14.bas]]
NEW
Ok
10 INPUT I
20 A$=STR$(I)
30 M=VAL(MID$(A$,2,1))
40 FOR J=2 TO LEN(A$)-1
50 IFM
* {{anchor:e0414-15}} 15) найдите наибольшую из цифр, встречающуюся в десятичной записи данного натурального числа M более одного раза.
\\ {{.examples:0414-15.bas|}} \\ [[+tab|wmsxbpge>0414-15.bas]]
NEW
Ok
10 INPUT M
20 N$=STR$(M)
30 FOR I=2 TO LEN(N$)-1
40 FOR J=I+1 TO LEN(N$)
50 IF MID$(N$,I,1)=MID$(N$,J,1) THEN IF VAL(MID$(N$,I,1))>A
THEN A=VAL(MID$(N$,I,1)):NEXTI ELSE ELSE NEXTJ:NEXTI
60 PRINT A
run
? 1234245
4
Ok
run
? 1243.67E34
7
Ok
Последний результат вызывает удивление, не правда ли?
Однако…
print n$
1.24367E+37
Ok
и все становится ясным!
{{anchor:n415}} {{anchor:asc}}
==== IV.1.5. ASC–функция ====
Напомним, что при вводе в ЭВМ символы преобразуются в соответствии с кодом ASCII (см. [[001#n173|раздел I.7.3.]]).
Функция
ASC(α)
,
где:
* ''ASC''("ASC" — "American Standard Code") — служебное слово;
* α — строковое выражение,
даёт возможность установить десятичный код ASCII первого символа значения строкового выражения α. Результатом функции ''ASC'' является целое число из отрезка [0,255] (говорят, что функция ''ASC'' //возвращает// целое число из отрезка [0,255]).
__//Пример//__.
ASC
NEW
Ok
10 INPUT X$:Y=ASC(X$):PRINT X$;Y
run
? π
π 163
Ok
run
? αδβ
αδβ 160
Ok
run
? ;
; 59
Ok
run
? ","
, 44
Ok
run
? ♪ ◀─ символ,
♪ 1 имеющий
Ok двухбайтовый код
Из приведённого примера ясно, что символ "π", например, имеет десятичный код 163, а символ "α"(первый символ значения строковой константы"αδβ") — десятичный код 160.
Далее, если значением строкового выражения α является пустая строка (%% "" %%), то фиксируется ошибка "Illegal function call".
Используя предыдущий пример, попытаемся получить код символа %% " %% (кавычка).
run
? "
Illegal function call in 10
Ok
Неудача!… Попытаемся ещё разок…
run
? """
? Redo from start
? █ и т.д.
Снова неудача! Не волнуйтесь, код символа "кавычка" можно получить косвенным путём с помощью функции преобразования ''CHR$''.
{{anchor:n416}} {{anchor:chr}}
==== IV.1.6. CHR$–функция ====
Общий вид функции:
CHR$(α)
,
где:
* ''CHR''("CHaRacter" — "символ") — служебное слово;
* α — арифметическое выражение.
Вначале компьютер вычисляет //код// — целую часть значения выражения α. Код должен принадлежать отрезку [0,255], иначе на экране появится сообщение об ошибке: "Illegal function call".
Результатом функции ''CHR$'' является однобайтовая строка, содержащая символ, соответствующий полученному коду (говорят, что функция ''CHR$'' //возвращает// однобайтовую строку, содержащую символ, соответствующий
полученному коду).
__//Примеры//__:
* 1)
INPUT X:Y$=CHR$(X):PRINT X;Y$
? 34
34 "
Ok
? 187
187 √
Ok
? 163
163 π
Ok
* 2) переменной A$ присвоить значение "A$=%%"BANSAJ!"%%"
A$="A$="+CHR$(34)+"BANSAJ!"+CHR$(34)
А теперь…
print A$
A$="BANSAJ!"
Ok
* 3) вывести на экран дисплея текст: "Нажмите клавишу %%"SHIFT"+"1"%%".
Ok
PRINT "Нажмите клавишу ";CHR$(34);"SHIFT";CHR$(34);"+";CHR$(34);"1";CHR$(34)
Нажмите клавишу "SHIFT"+"1"
Ok
* {{anchor:e0416-04}} 4) инициализация строкового массива ''B$(N)'' "псевдослучайными" словами
\\ {{.examples:0416-04.bas|}} \\ [[+tab|wmsxbpge>0416-04.bas]]
Ok
20 Z=RND(-TIME)
30 INPUT "Введите наибольшее количество символов слова";G
40 INPUT"Введите количество слов";N:DIM B$(N)
60 FOR K=1 TO N:A=INT((G+1)*RND(1))
80 FOR I=1 TO A:B$(K)=B$(K)+CHR$(INT(255*RND(1)))
100 NEXT I,K
110 FOR K=1 TO N:PRINT K;"слово: ";B$(K):NEXT
run
Введите наибольшее количество символов слова? 4
Введите количество слов? 2
1 слово: ▲щΓ
2 слово: X∞p
Ok
Функции ''ASC()'' и ''CHR$()'' являются взаимообратными, то есть ''X=ASC(CHR$(X))'' и ''Y$=CHR$(ASC(Y$))'', если только значение арифметического выражения X находится в допустимых
пределах (напомним, что 0≤X≤255!), а ''LEN(Y$)=1''.
* {{anchor:e0416-05}} 5) присвоить строковой переменной Y$ значение "yes", если X≥1 и "no" — если X<1. Оператор условного перехода ''IF…THEN…ELSE…'' не применять!
\\ {{.examples:0416-05.bas|}} \\ [[+tab|wmsxbpge>0416-05.bas]]
NEW
Ok
10 INPUT X
20 Y$=CHR$(-(X>=1)*ASC("y"))+CHR$(-(X>=1)*ASC("e"))+CHR$(-(X>=1)*ASC("s"))+
CHR$(-(X<1)*ASC("n"))+CHR$(-(X<1)*ASC("o")):print Y$
run
? 5
yes
Ok
run
? -1
no
Ok
Наконец заметим, что функцию ''CHR$'' удобно использовать для работы с непечатаемыми "символами" (BS, SELECT, 'Ввод '⏎, клавиши управления курсором).
{{anchor:n417}} {{anchor:bin}}
==== IV.1.7. BIN$–функция ====
''BIN$''–функция применяется для преобразования целого числа в двоичную символьную строку. Её общий вид:
BIN$(α)
,
где:
* ''BIN'' ("BINary" — "двоичный") — служебное слово;
* α — арифметическое выражение.
Вначале вычисляется значение арифметического выражения α; результат преобразуется в целое число (возможно со знаком); если это число не попадает на отрезок [-32768,65535], то компьютер фиксирует ошибку переполнения: "Overflow".
Полученное число записывается в двоичной системе счисления, а затем преобразуется в символьную строку, соответствующую его двоичному коду.
Таким образом,
Ok
? BIN$(3),BIN$(0)
11␣␣␣␣␣␣␣␣␣␣␣␣0
Ok
Ok
? BIN$(32767)
111111111111111
Ok
Ok
? BIN$(65536)
Overflow
Ok
Максимальная длина строки результата функции ''BIN$()'' — 16.
Обозначим целую часть значения выражения α буквой N.
Заметим, что для отрицательных N компьютер вычисляет значение функции ''BIN$(N)'' по рекуррентной формуле:
BIN$(N)=BIN$(65536+N)
.
__//Примеры//__:
* 1)
BIN$(-1) возвращает "1111111111111111",
BIN$(-32768) возвращает "1000000000000000",
BIN$(32768) возвращает "1000000000000000",
BIN$(65535) возвращает "1111111111111111".
* 2)
print VAL("&B"+BIN$(15))
15
Ok
Наконец, при помощи функции ''BIN$'' можно "научить" компьютер двоичной арифметике. Посмотрите, например, как работает следующая программа:
* {{anchor:e0417-03}} 3)
\\ {{.examples:0417-03.bas|}} \\ [[+tab|wmsxbpge>0417-03.bas]]
Ok
10 PRINT"Могу найти сумму двух двоичных чисел!"
20 INPUT "Первое число, второе число";N1$,N2$
40 ANS=VAL("&B"+N1$)+VAL("&B"+N2$)
50 BN$=RIGHT$(STRING$(16,"0")+BIN$(ANS),16)'См.аналогию в п. 1.9!
60 PRINT"Ответом является:";BN$;" или";ANS;"десятичное"
run
Могу найти сумму двух двоичных чисел!
Первое число, второе число? 1111111111111111,1
Ответом является:0000000000000000 или 0 десятичное
Ok
Неизвестные вам пока строковые функции ''RIGHT$'' и ''STRING$'' рассмотрены ниже в разделах [[#n423|IV.2.3]]. и [[#n424|IV.2.4]].
* {{anchor:e0417-04}} 4) среди простых чисел, не превосходящих N, найти такое, в двоичной записи которого максимальное число единиц.
\\ {{.examples:0417-04.bas|}} \\ [[+tab|wmsxbpge>0417-04.bas]]
NEW
Ok
10 INPUT N
20 FOR I=1 TO N:FOR J=2 TO INT(SQR(I))
40 IF IMODJ=0 GOTO 100 ELSE NEXT J
50 M$=BIN$(I):PRINT I;M$:L=0
60 FOR K=1 TO LEN(M$):IF MID$(M$,K,1)="1" THEN L=L+1
80 NEXTK
90 IF L>R THEN R=L:R1=I
100 NEXT I
110 PRINT R;R1
run
? 18
␣1␣1
␣3␣11
␣5␣101
␣7␣111
␣11␣1011
␣13␣1101
␣17␣10001
␣3␣␣7
Ok
* {{anchor:e0417-05}} 5) выделить старший и младший байты двоичного числа ''A%''
\\ {{.examples:0417-05.bas|}} \\ [[+tab|wmsxbpge>0417-05.bas]]
NEW
Ok
10 INPUT"Введите число A% (не более 16 двоичных цифр)";A%
20 IF A%<0 THEN H%=A%\256-1 ELSE H%=A%\256
25 L%=A%MOD256
30 PRINT RIGHT$("00000000"+BIN$(H%),8);" ";RIGHT$("00000000"+BIN$(L%),8)
run
Введите число А% (не более 16 двоичных цифр)? &B1011110001010011
10111100 01010011 ▲
Ok │ число положительное!
run
Введите число А% (не более 16 двоичных цифр)? &B0011110001010000
00111100 01010000 ▲
Ok │ число отрицательное!
{{anchor:n418}} {{anchor:oct}}
==== IV.1.8. OCT$–функция ====
''OCT$''–функция служит для преобразования числа в восьмеричную символьную строку. Её общий вид:
OCT$(α)
,
где:
* ''OCT'' ("OCTal" — "восьмеричный") — служебное слово;
* α — арифметическое выражение.
Эта функция вычисляет значение арифметического выражения α, преобразует результат в целое (возможно со знаком); если результат не принадлежит отрезку [-32768,65535], то фиксируется ошибка: "Overflow" \\ (//"Переполнение"//).
В противном случае результат преобразуется в символьную строку, представляющую его значение в восьмеричной системе счисления.
Максимальная длина строки–результата ''OCT$()'' — 6 символов (6 байт).
__//Пример//__.
Ok
print OCT$(&HFFFF)
177777
Ok
Обозначим буквой N целую часть значения выражения α.
Если N принадлежит диапазону [0,65535], то значение функции OCT$() находится компьютером без обращения к дополнительному коду, например:
print OCT$(0)
0
Ok
print OCT$(65535)
177777
Ok
Если же N принадлежит диапазону [-32768,-1], то значение функции ''OCT$()'' находится ЭВМ с использованием дополнительного кода, т.е. по рекуррентной формуле:
OCT$(N)=OCT$(65536+N)
.
Например:
* 1)
Ok 0k
print OCT$(-1) сравните с print OCT$(65535)
177777 177777
Ok Ok
* 2)
Ok Оk
? OCT$(-32768) сравните с print OCT$(32768)
100000 100000
Ok Ok
__//Задача//__. Попытайтесь научить компьютер "восьмеричной" арифметике. Используйте для этой цели идею следующего фрагмента:
print VAL("&O"+OCT$(100))
100
Ok
И, наконец…
{{anchor:n419}} {{anchor:hex}}
==== IV.1.9. HEX$–функция ====
''HEX$''–функция служит для преобразования целого числа в шестнадцатеричную символьную строку. Её общий вид:
HEX$(α)
,
где:
* ''HEX'' ("HEXadecimal" — "шестнадцатеричный") — служебное слово;
* α — арифметическое выражение.
Вначале вычисляется N — целая часть значения арифметического выражения α. Если N не попадает на отрезок [-32768,65535], то компьютер сообщает об ошибке: "Overflow" \\ (//"Переполнение"//).
"Правильное" N преобразуется в символьную строку, соответствующую его шестнадцатеричному коду. Максимальная длина результата вычисления значения функции равна //четырём// байтам (четырём символам).
Если целое число N находится на отрезке [0,65535], то преобразование элементарно:
* ''HEX$(0)'' возвращает "0",
* ''HEX$(65535)'' возвращает "FFFF".
Однако значение функции при отрицательных N, принадлежащих [-32768,-1], вычисляется компьютером с использованием дополнительного кода, т.е. с применением рекуррентной формулы:
HEX$(N)=HEX$(65536+N);
HEX$(-1)=HEX$(65535) возвращает "FFFF";
HEX$(-32768)=HEX$(32768) возвращает "8000".
Функция ''VAL()'' в комбинации с функцией ''HEX$()'' позволяют получить обратное преобразование:
N=VAL("&h"+HEX$(N))
.
//Примеры//:
Ok
? VAL("&h"+HEX$(3))
3
Ok
Ok
? VAL("&H"+HEX$(-10))
-10
Ok
Существование функции ''HEX$()'' делает возможным "обучение" компьютера шестнадцатеричной арифметике. Единственная трудность состоит в добавлении к числу нулей, стоящих в старших разрядах (если, конечно, это необходимо!).
Подумайте!… Придумали? Если нет, то возьмите на вооружение следующий красивый приём:
X$=RIGHT$("0000"+HEX$(N),4)
или
X$=RIGHT$(STRING$(4,"0")+HEX$(N),4)
.
//Пример//.
* {{anchor:e0419-01}} 1)
\\ {{.examples:0419-01.bas|}} \\ [[+tab|wmsxbpge>0419-01.bas]]
NEW
Ok
10 PRINT"Введите два шестнадцатеричных числа"
20 INPUT A$,B$
30 SM=VAL("&H"+A$)+VAL("&H"+B$)
40 DF=VAL("&H"+A$)-VAL("&H"+B$)
50 PRINT "Сумма, разность:";RIGHT$("0000"+HEX$(SM),4);SPC(1);RIGHT$("0000"+HEX$(DF),4)
run
Введите два числа
? 1,1
Сумма, разность:0002 0000
Ok
run
Введите два числа
? F000,FFF1
Сумма, разность:EFF1 F00F
Ok
run
Введите два числа
? FFFF,FFFF
Сумма, разность:FFFE 0000
Ok
run
Введите два числа
? FFFFF,E
Overflow in 30
Ok
* {{anchor:e0419-02}} 2) выделить старший и младший байты шестнадцатеричного числа ''A%''.
\\ {{.examples:0419-02.bas|}} \\ [[+tab|wmsxbpge>0419-02.bas]]
NEW
Ok
10 INPUT "Введите число А% (не более 4 шестнадцатеричных цифр)";A%
20 IF A%<0 THEN H%=A%\256-1 ELSE H%=A%\256
25 L%=A%MOD256
30 PRINT RIGHT$("00"+HEX$(H%),2);" ";RIGHT$("00"+HEX$(L%),2)
run
Введите число А% (не более 4 шестнадцатеричных цифр)? &H67EA
67 EA ───▲──
Ok положительное число┘
run
Введите число А% (не более 4 шестнадцатеричных цифр)? &HE2A9
E2 A9 ───▲──
Ok отрицательное число┘
Теперь настало время подумать, почему встроенные функции,изученные нами в разделах [[#n411|IV.1.1.]] — IV.1.9. [[#n419|IV.1.9]], называются "функциями преобразования".
{{anchor:n42}}
===== IV.2. Встроенные строковые функции =====
\\
Пока слепо плыл сон по разбитым надеждам,\\
Космос с болью сочился над разбитой любовью,\\
Был из скрытных людей свет твой медленно изгнан,\\
И небо не спало.
—//Избранные стихотворения компьютера RCA–301, поэма 929//
В этом разделе мы рассмотрим следующие встроенные функции: ''[[#mid|MID$]]'', ''[[#left|LEFT$]]'', ''[[#right|RIGHT$]]'', ''[[#string|STRING$]]'', ''[[#space|SPACE$]]''.
{{anchor:n421}} {{anchor:mid}}
==== IV.2.1. MID$–функция ====
Общий вид ''MID$'' — функции следующий:
MID$(α,m[,n])
,
где:
* ''MID'' ("MIDdle" — "середина") — служебное слово;
* α — строковое выражение;
* m, n — арифметические выражения, целые части значений которых должны принадлежать отрезку [1,255].
Напомним, что квадратные скобки, встречающиеся при описании синтаксиса, — обозначение, указывающее, что элемент синтаксической конструкции внутри них не является обязательным.
Условимся целые части значений арифметических выражений m и n обозначать соответственно M и N.
По умолчанию N равно количеству символов значения строкового выражения α от позиции M и до конца строки включительно.
Рассмотрим два возможных случая.
- Функция ''MID$'' //слева// от знака равенства в операторе присваивания ''LET''.
[LET] MID$(α,m[,n]) = β
Здесь β — строковое выражение.
Пусть L — длина значения строкового выражения β. При выполнении оператора ''LET'' символы значения строкового выражения α, начиная с позиции M, последовательно замещаются на первые ''min(L,N)''
символов строкового выражения β. Остальные символы значения строкового выражения α остаются без изменений (см. [[#pict_04_01|рис.1]]).
{{anchor:pict_04_01}}
1–й 2–й М–й (М+1)–й (M+N-1)–й LEN(α)–й
символ символ символ символ символ символ
│ │ │ │ │ │
┌───▼───┬───▼───┬───┬───▼───┬───▼───┬───┬───▼───┬───┬───▼───┐
│ │ │ … │███████│███████│ … │███████│ … │ │ α
└───────┴───────┴───┴───────┴───────┴───┴───────┴───┴───────┘
│◀─────── N символов ──────▶│
▲ ▲
│ │
┌───────────────────────────┐
│ MID$(α,M,N) = β │
▲───────────────────────────▲
│ │
┌────────────────────────────────────────┐
L > N │███████│███████│ … │███████│ … │███████│ β
└────────────────────────────────────────┘
│◀──────────────── L символов ──────────▶│
или ▲ ▲
│ │
┌───────────────────┐─ ─ ─ ─┐
L < N │███████│ … │███████│ │β
└───────────────────┘─ ─▲─ ─┘
│◀─── L символов───▶│ │
символы из α
__//Рис. 1//__
{{anchor:e0421-01}} __//Пример//__.
\\ {{.examples:0421-01.bas|}} \\ [[+tab|wmsxbpge>0421-01.bas]]
10 X$="12345678910":INPUT Y$,M,N:MID$(X$,M,N)=Y$:PRINT X$
run ┌────────────────┐
? стол,8,4 │X$="12345678910"│
1234567стол │ └──┘ │
Ok │ Y$="стол"│
└────────────────┘
run ┌────────────────┐
? сто,8,3 │X$="12345678910"│
1234567сто0 │ └─┘ │
Ok │ Y$="сто" │
└────────────────┘
run ┌────────────────┐
? стул,1,4 │X$="12345678910"│
стул5678910 │ └──┘ │
Ок │Y$="стул" │
└────────────────┘
run ┌────────────────┐
? казак,8,5 │X$="12345678910"│
1234567каза │ └──┘ │
Ok │ Y$="каза"│
└────────────────┘
И наконец, вместо оператора присваивания вида:
А$ = строковое выражение
,
используйте оператор: ''MID$(А$,1)'' = строковое выражение, чтобы избежать использования дополнительной памяти. Но учтите, что строковая переменная A$ уже должна существовать и ''LEN(A$)≥LEN'' (строковое выражение)!
- Функция ''MID$'' //справа// от знака равенства в операторе присваивания ''LET''.
[LET] γ = MID$(α,m[,n])
Здесь γ — строковая переменная.
При выполнении указанной конструкции N последовательных символов значения α, начиная с позиции M, становятся значением строковой переменной γ. Предварительное определение γ не обязательно (см. [[#pict_04_02|рис.2]]).
{{anchor:pict_04_02}}
1–й 2–й М–й (М+1)–й (М+N-1)–й LEN(α)–й
символ символ символ символ символ символ
│ │ │ │ │ │
┌───▼───┬───▼───┬───┬───▼───┬───▼───┬───┬───▼───┬───┬───▼───┐
│ │ │ … │███████│███████│ … │███████│ … │ │ α
└───────┴───────┴───┴───────┴───────┴───┴───────┴───┴───────┘
│ ┌─────────────────┐ │
│ │ γ = MID$(α,M,N) │ │
│ └─────────────────┘ │
▼───────┬───────┬───┬───────▼
│███████│███████│ … │███████│ γ
└───────┴───────┴───┴───────┘
│◀────── N символов────────▶│
__//Рис. 2//__
__//Примеры//__:
* 1)
Ok
? MID$("КЛАВИАТУРА",3,4)
АВИА └──┘
Ok
* 2)
Ok
? MID$("капрал"+"литр",4,5)
ралли └──────┘
Ok
* 3)
Ok
? MID$("молоко"+"брат",5,5)
кобра └──────┘
Ok
* 4)
Ok
? MID$("СОР У НОР",3,5)
Р У Н └───┘
Ok
* 5)
Ok
10 input I,J:? MID$("234",I,J)
run run run run
? 1,1 ? 2,90 ? 10,1 ? 1,0
2 34
Ok Ok Ok Ok
run или run
? -1,1 ? 0,0
Illegal function call in 10
Ok
run или run
? 2,-1 ? 0,1
Illegal function call in 10
Ok
* {{anchor:e0421-06}} 6)
{{.examples:0421-06.bas|}} \\ [[+tab|wmsxbpge>0421-06.bas]]
NEW
Ok
1 Z$="123"
2 INPUT X$,M
3 Z$=MID$(X$,M)
4 Y$=MID$(X$,M+4)
5 PRINT Z$
6 PRINT Y$
run
? Баба-Яга-костяная нога,6
Яга-костяная нога
костяная нога
Ok
* {{anchor:e0421-07}} 7) определить третий символ в слове X$.
Ok
input x$:z$=mid$(x$,3,1):print z$
? интеграл
т
Ok
Если требуется определить K–й символ в слове X$, то программа будет иметь следующий вид (разумеется, ''1≤K≤LEN(X$)''):
\\ {{.examples:0421-07.bas|}} \\ [[+tab|wmsxbpge>0421-07.bas]]
NEW
Ok
10 INPUT X$,K
20 IF K<1 OR K>LEN(X$) THEN
PRINT"Буквы с указанным номером в слове нет":END
ELSE Z$=MID$(X$,K,1):PRINT Z$:END
run
? информатика,3
ф
Оk
run
? информатика,12
Буквы с указанным номером в слове нет
Ok
__//Пример 8//__. Для получения числа, состоящего из двух младших цифр целого числа N, воспользуйтесь строкой:
10 X%=VAL(MID$(STR$(N),LEN(STR$(N))-1,2))
Заметим, что Вы можете выполнить то же самое, используя функцию ''INT()'':
10 INPUT N:PRINT N-INT(N/100)*100
{{anchor:e0421-09}} __//Пример 9//__. Написать программу, определяющую, сколько раз слово Z$ встречается в слове X$.
\\ {{.examples:0421-09.bas|}} \\ [[+tab|wmsxbpge>0421-09.bas]]
NEW
Оk
10 INPUT X$,Z$:J=0
20 FOR I=1 TO LEN(X$)-LEN(Z$)+1
30 IF Z$<>MID$(X$,I,LEN(Z$)) THEN NEXTI ELSE J=J+1:NEXTI
40 PRINT J:END
run
? БАОБАБ,БА
2
Ok
{{anchor:e0421-10}} __//Пример 10//__. Написать программу, заменяющую в слове X$ слово Z$ на слово Y$ той же длины.
\\ {{.examples:0421-10.bas|}} \\ [[+tab|wmsxbpge>0421-10.bas]]
NEW
Оk
10 INPUT X$,Z$,Y$:IF LEN(Z$)>MID$(X$,J,LEN(Z$)) THEN 50
40 MID$(X$,J,LEN(Y$))=Y$
50 NEXT J:PRINT X$:END
run
? ДОМ КИРПИЧНЫЙ
?? КИРПИЧНЫЙ
?? БАНАНОВЫЙ
ДОМ БАНАНОВЫЙ
Оk
\\
Я разуму уму заря,\\
Я иду с мечом судия.
—//Г.Державин//
\\
Мечтатель! Летать чем?
—//С.Кирсанов//
{{anchor:e0421-10}} __//Пример 11//__. Слова и фразы, переходящие в себя при "акустическом отображении", получили название //палиндромов//. Это слово греческого происхождения и означает "движущийся обратно", "обратимый". Найти палиндром практически невозможно, но его нетрудно построить. В качестве "строительных блоков" простейших палиндромов следует выбирать слова, не изменяющиеся при чтении от конца к началу. Выдерживают отражение в "акустическом зеркале" такие слова, как боб, дед, кок, мим, иди, топот, потоп, колок, ротор, кабак, моном и выражения, такие, например,как "Лёша на полке клопа нашёл".
\\ {{.examples:0421-11.bas|}} \\ [[+tab|wmsxbpge>0421-11.bas]]
Ok
10 INPUT X$:Y$=""
20 FOR I=1 TO LEN(X$):Y$=MID$(X$,I,1)+Y$:NEXT
30 IF X$=Y$ THEN PRINT"Палиндром!" ELSE PRINT"Не палиндром!"
run
? косолетижежителосок
Палиндром!
Ok
run
? мечтателлетатчем
Палиндром!
Ok
run
? я разуму уму заря
Не палиндром!
Ok
Сравните результат действия функции ''MID$(A$,I,J)'', стоящей справа от знака равенства в операторе присваивания, с результатом операции //вырезки//:
A[i:i+j-1]
,
где:
* А — текст;
* i — номер первого символа фрагмента, вырезаемого из текста A;
* j — количество символов в фрагменте, вырезаемом из текста A,
в школьном алгоритмическом языке.
Рассмотрите интересный часто встречающийся частный случай j=1, т.е. проведите аналогию между ''MID$(A$,I,1)'' и ''A[i:i]''.
И, наконец, сопоставьте результат действия функции ''MID$'', стоящей слева от знака равенства в операторе присваивания, с результатом работы команды частичного изменения значения литерной величины (команды присваивания вырезке)
A[i:j]=B
,
позволяющей заменить часть слова А, начинающуюся с i–той буквы и оканчивающуюся j–той буквой, на слово B, в школьном алгоритмическом языке.
{{anchor:n422}} {{anchor:left}}
==== IV.2.2. LEFT$–функция ====
Общий вид функции:
LEFT$(α,n)
,
где:
* ''LEFT'' ("left" — "левый") — служебное слово;
* α — строковое выражение;
* n — арифметическое выражение, целая часть значения которого должна принадлежать отрезку [0,255].
Функция ''LEFT$'' позволяет выделить самые //левые// n символов значения строкового выражения α.
''LEFT$'' — функция является "частным случаем" ''MID$''–функции и через неё может быть определена так:
LEFT$(α,n) = MID$(α,1,n)
.
__//Примеры//__:
* {{anchor:e0422-01}} 1)
\\ {{.examples:0422-01.bas|}} \\ [[+tab|wmsxbpge>0422-01.bas]]
Ok
10 INPUT X$,N
20 U$=MID$(X$,1,N)
30 V$=LEFT$(X$,N)
40 PRINT U$;V$
run
? 1234567890,6
123456123456
Ok
* 2)
Ok
? LEFT$("деньги",0)
Illegal function call
Ok
* 3)
Ok
? LEFT$("деньги",4)
день └──┘
Ok
* 4)
Ok
? LEFT$("abc",2);LEFT$(LEFT$("def",2),1)
abd
Ok
* 5)
Ok
? LEFT$("wap",-1)
Illegal function call
Ok
{{anchor:n423}} {{anchor:right}}
==== IV.2.3. RIGHT$–функция ====
Общий вид функции:
RIGHT$(α,n)
,
где:
* ''RIGHT'' ("right" — "правый") — служебное слово;
* α — строковое выражение;
* n — арифметическое выражение, целая часть значения которого должна принадлежать отрезку [0,255].
Функция ''RIGHT$'' позволяет выделить самые //правые// n символов значения строкового выражения α (в этом смысле функция ''RIGHT$()'' симметрична функции ''LEFT$()''!).
''RIGHT$'' — функция является частным случаем ''MID$''–функции и через неё может быть определена так:
RIGHT$(α,n) = MID$(α,L-n+1,n)
,
где L — длина значения строкового выражения α.
__//Примеры//__:
* 1)
Ok
? RIGHT$("abcdgoldfish",8)
goldfish └──────┘
Ok
* 2)
Ok
? RIGHT$("гамбит",3)
бит └─┘
Ok
* {{anchor:e0423-03}} 3)
\\ {{.examples:0423-03.bas|}} \\ [[+tab|wmsxbpge>0423-03.bas]]
Ok
10 INPUT X$,N
20 U$=MID$(X$,LEN(X$)-N+1)
30 V$=RIGHT$(X$,N):? U$;V$
run
? 1234567890,4
78907890
Ok
* 4)
Ok
? RIGHT$(LEFT$("рубaйте!",6),4)
байт
Ok
* 5)
Ok
? MID$("рубайте!",3,4)
байт └──┘
Ok
Заметим, что хотя функции ''LEFT$'' и ''RIGHT$'' являются "частными случаями" функции ''MID$'', их нельзя использовать //слева// от символа присваивания в операторе ''LET''!
Посмотрите…
NEW
Ok
1 A$="Барсик":MID$(A$,1,4)="Корт":PRINT A$
2 B$="Комик":LEFT$(B$,3)="Том":PRINT B$
run
Кортик
Syntax error in 2
Ok
{{anchor:e0423-06}} __//Пример 6//__. Образовать слово Y$, состоящее из первых N и последних К символов данного слова Х$.
\\ {{.examples:0423-061.bas|}} \\ [[+tab|wmsxbpge>0423-061.bas]]
Первоначальный вариант программы выглядит так:
NEW
Ok
10 INPUT X$,N,K
20 Y$=LEFT$(X$,N)+RIGHT$(X$,K)
30 PRINT "Результат:";Y$
40 END
run
? аллигатор,3,6
Результат:аллигатор
Ok
run
? аллигатор,0,6
Результат:игатор
Ok
run
? аллигатор,0,0
Результат:
Ok
однако…
run
? аллигатор,-4,3
Illegal function call in 20
Ok
Поэтому дополним программу строкой:
\\ {{.examples:0423-062.bas|}} \\ [[+tab|wmsxbpge>0423-062.bas]]
15 IF N<0 OR N>LEN(X$) OR K<0 OR K>LEN(X$) THEN PRINT"Не балуйтесь! Повторите!":GOTO 10
Выполним полученную программу:
run
? аллигатор,9,10
Не балуйтесь! Повторите!
? аллигатор,6,0
Результат:аллига
Оk
run
? аллигатор,-1,5
Не балуйтесь! Повторите!
? аллигатор,9,10
Не балуйтесь! Повторите!
?
Строка 15 обеспечивает повторный запрос на ввод исходных данных в том случае, когда K<0 или К> длины слова Х$, или N<0, или N>длины слова X$ (своеобразная "защита" программы от непредусмотренной исходной информации).
{{anchor:e0423-07}} __//Пример 7//__. Написать программу перевода чисел, записанных римскими цифрами, в числа, записанные арабскими цифрами.
\\ {{.examples:0423-07.bas|}} \\ [[+tab|wmsxbpge>0423-07.bas]]
NEW
Ok
1 DATA 1000,M,900,CM,500,D,400,CD,100,C,90,XC,50,L,40,XL,10,X,9,IX,5,V,4,IV,1,I:INPUT"N$";N$
2 READ A,A$
3 IF A$=LEFT$(N$,LEN(A$)) THEN N$=RIGHT$(N$,LEN(N$)-LEN(A$)):N=N+A:GOTO 3
4 IF N$>" "GOTO 2
5 PRINT N
run
N$? IV
4
Ok
run
N$? CMIV
904
Ok
run
N$? MMMDXCLLI
3691
Ok
run
N$? MCMXVII
1917
Ok
run
N$? MDCCCXII
1812
Ok
А как насчёт программы обратного перевода? Пожалуйста!
{{anchor:e0423-08}} __//Пример 8//__. Написать программу перевода чисел, записанных арабскими цифрами, в числа, записанные римскими цифрами.
\\ {{.examples:0423-08.bas|}} \\ [[+tab|wmsxbpge>0423-08.bas]]
NEW
Ok
1 DATA 1000,M,900,CM,500,D,400,CD,100,C,90,XC,50,L,40,XL,10,X,9,IX,5,V,4,IV,1,I:INPUT"N";N
2 READ A,A$
3 IF A<=N THEN PRINT A$;:N=N-A:GOTO 3
4 IF N>0 GOTO 2
run
N? 654
DCLIV
Ok
run
N? 25
XXV
Ok
run
N? 11111
MMMMMMMMMMMCXI
Ok
run
N?3691
MMMDCXCI
Ok
{{anchor:n424}} {{anchor:string}}
==== IV.2.4. STRING$–функция ====
Общий вид STRING$ — функции следующий:
STRING$(n,α)
,
или
STRING$(n,m)
,
где:
* ''STRING'' ("string" — "строка") — служебное слово;
* α — строковое выражение;
* n,m — арифметические выражения, целые части значений которых должны принадлежать отрезку [0,255].
Обозначим N—значение арифметического выражения n. Тогда, в случае формата ''STRING$(n,α)'' функция возвращает строку, состоящую из N одинаковых символов, равных первому символу значения строкового выражения α. А в случае формата ''STRING$(n,m)'' значением функции является последовательность N одинаковых символов, код ASCII которых одинаков (ведь символы одинаковы!) и равен целой части значения арифметического выражения m.
Поэтому, если Вы хотите инициализировать строковую переменную A$, N //пробелами// (наиболее часто встречающаяся операция!), то два приведённых ниже оператора выполняют идентичное действие:
A$=STRING$(N,32)
,
A$=STRING$(N," ")
(ведь ''CHR$(32)=%%" "%%'' !).
Таким образом, с помощью ''STRING$''–функции можно назначать длину и проводить инициализацию строковых переменных пробелами. Приведём несколько тривиальных примеров:
* 1)
Ok
Y$=STRING$(10,"*"):? LEN(Y$):? "#"+Y$+"FILE"
10
#**********FILE
Ok
* 2)
Ok
print STRING$(10,52);STRING$(10,CHR$(52))
44444444444444444444
Ok
* 3)
Ok
A$=STRING$(5," "):MID$(A$,3,3)="Все!":? A$
␣␣Все
Ok
Если n=0, то функция ''STRING$'' возвращает пустую строку (%%""%%), независимо от того, какие α или m Вы задали.
В тех случаях, когда целые части значений арифметических выражений n и m находятся вне отрезка [0,255], на экране дисплея появляется сообщение об ошибке:
"Illegal function call" \\ (//"Недопустимый вызов функции"//).
{{anchor:n425}} {{anchor:space}}
==== IV.2.5. SPACE$–функция ====
Общий вид функции:
SPACE$(n)
,
где:
* ''SPACE'' ("space" — "пространство") — служебное слово;
* n — арифметическое выражение, целая часть значения которого должна принадлежать отрезку [0,255].
С помощью ''SPACE$''–функции назначается длина и проводится начальная инициализация пробелами строковых переменных. Например, если Вы хотите инициализировать новую строковую переменную X$ N пробелами, то используйте оператор присваивания ''X$=SPACE$(N)''
''SPACE$''–функция является частным случаем ''STRING$''–функции и через неё может быть определена так:
SPACE$(n)=STRING$(n," ")
{{anchor:n426}}
==== IV.2.6. Примеры ====
* {{anchor:e0426-01}} 1) выделить из текста, являющегося значением строковой переменной T$, отдельные слова (они отделены друг от друга одним пробелом) и записать их в строковый массив с именем W$. (Психологи утверждают, что из минуты, затраченной на чтение, мы 58 секунд считываем промежутки между символами!)
\\ {{.examples:0426-01.bas|}} \\ [[+tab|wmsxbpge>0426-01.bas]]
NEW
Ok
10 LINEINPUT T$:T$=T$+" ":K=0'K-количество слов в T$
20 FOR I=1 TO LEN(T$):IF MID$(T$,I,1)=" " THEN K=K+1
35 NEXT I 'Количество слов в Т$ найдено!
40 DIM W$(K):DIM P(K)'Вот теперь можно описать массивы!
45 FOR J=1 TO K:W$(J)=SPACE$(5):NEXTJ 'Начальная инициализация пробела ми элементов массива W$(K)
47 N=1:FOR J=1 TO K:FOR I=N TO LEN(T$)
70 IF MID$(T$,I,1)=" " GOTO 85
80 NEXT I
85 P(J)=I:W$(J-1)=MID$(T$,N,P(J)-P(J-1)-1):N=P(J)+1:PRINT W$(J-1);SPC(5)
90 NEXT J
run
123 4 567
123␣␣␣␣␣4␣␣␣␣␣567
Ok
\\
Елечвок енмяет ослог.
—//А.Вознесенский. Человек//
* {{anchor:e0426-02}} 2) даны два слова X$ и Y$. Проверить, можно ли из символов, входящих в слово X$, составить слово Y$. Символы можно переставлять, и каждый символ можно использовать несколько раз!
\\ {{.examples:0426-02.bas|}} \\ [[+tab|wmsxbpge>0426-02.bas]]
NEW
Ok
10 INPUT X$,Y$
30 FOR I=1 TO LEN(Y$):FOR J=1 TO LEN(X$)
50 IF MID$(Y$,I,1)=MID$(X$,J,1) THEN A$="да":NEXT I ELSE A$="нет":NEXT J
80 PRINT A$:END
run run
? школаа,алокша ? AB,BB
да да
Ok Ok
* {{anchor:e0426-03}} 3) даны два натуральных числа M и N. Проверьте,можно ли из цифр числа M составить число N. Цифры можно переставлять и использовать:
* a) более одного раза
\\ {{.examples:0426-031.bas|}} \\ [[+tab|wmsxbpge>0426-031.bas]]
NEW
Ok
10 INPUT M,N:M$=STR$(M):N$=STR$(N)
30 FOR I=2 TO LEN(N$):FOR J=2 TO LEN(M$)
40 IF MID$(N$,I,1)<>MID$(M$,J,1) THEN NEXT J:?"Нельзя!"ELSE NEXTI:?"Можно!"
run run run
? 6785,58758 ? 6785,58358 ? 6785,87
Можно! Нельзя! Можно!
Ok Ok Ok
* b) не более одного раза
\\ {{.examples:0426-032.bas|}} \\ [[+tab|wmsxbpge>0426-032.bas]]
b)
NEW
Ok
10 INPUT M,N:X$=" ":M$=STR$(M):N$=STR$(N)
40 FOR I=2 TO LEN(N$):FOR J=2 TO LEN(M$)
50 IF MID$(N$,I,1)=MID$(M$,J,1) THEN MID$(N$,I,1)=X$:
MID$(M$,J,1)=X$:NEXTI:?"Можно!" ELSE NEXTJ:?"Нельзя!"
run run
? 12345432,1236 ? 1234554320,1022
Нельзя! Можно!
Ok Ok
* {{anchor:e0426-04}} 4) число 41 обладает следующим свойством: 41²=1681,√16=4 и √81=9, т.е. числа 16 и 81 — точные квадраты; найти все натуральные числа, не превосходящие N, и такие, что первые две и последние две цифры квадрата числа являются точными квадратами.
\\ {{.examples:0426-04.bas|}} \\ [[+tab|wmsxbpge>0426-04.bas]]
NEW
Ok
10 INPUT"Сколько чисел необходимо проверить";N:PRINT"Интересующие Вас числа:";
20 FOR I=10 TO N:B=I^2
40 B$=MID$(STR$(B),2):IF VAL(RIGHT$(B$,2))<>0 THEN 50 ELSE NEXT I: END
50 IF SQR(VAL(LEFT$(B$,2)))=FIX(SQR(VAL(LEFT$(B$,2)))) AND
SQR(VAL(RIGHT$(B$,2)))=FIX(SQR(VAL(RIGHT$(B$,2))))THENPRINT I;:NEXTI ELSE NEXTI
run
Сколько чисел необходимо проверить? 100
Интересующие Вас числа: 41
Ok
* {{anchor:e0426-05}} 5) написать программу, которая заменяет часть слова Z$ текста T$ на текст Y$ с сохранением одного пробела между словами.
\\ {{.examples:0426-05.bas|}} \\ [[+tab|wmsxbpge>0426-05.bas]]
NEW
Ok
5 PRINTSPC(8);"Вводите текст !":LINEINPUT T$:T$=T$+" ":K=0
15 INPUT "Какое слово менять";Z$
16 INPUT "На какое слово менять";Y$
20 FOR I=1 TO LEN(T$):IF MID$(T$,I,1)=" " THEN K=K+1:NEXTI ELSE NEXTI
'K-количество слов в тексте T$
40 DIM W$(K):DIM P(K)'Вот теперь можно описать массивы!
45 FOR J=1 TO K:W$(J)=SPACE$(5):NEXT J
47 N=1'N-номер первой буквы текущего слова в тексте
50 FOR J=1 TO K: FOR I=N TO LEN(T$)
70 IF MID$(T$,I,1)=" " GOTO 85
80 NEXT I
84 'P()-массив,содержащий номера позиций пробелов в тексте
85 P(J)=I:W$(J-1)=MID$(T$,N,P(J)-P(J-1)-1):N=P(J)+1
90 NEXT J
95 A$="Образец не найден! Ваш текст:"
100 FOR J=0 TO K-1
105 IF W$(J)=Z$ THEN W$(J)=Y$:A$="":GOTO 120
106 FOR I=1 TO LEN(W$(J))-LEN(Z$)+1
107 IF Z$<>MID$(W$(J),I,LEN(Z$))THEN115
108 W$(J)=LEFT$(W$(J),I-1)+Y$+RIGHT$(W$(J),LEN(W$(J))-I-LEN(Z$)+1):A$=""
115 NEXTI
120 NEXT J
130 FOR J=0 TO K-1:B$=B$+W$(J)+" ":NEXT
145 PRINT A$
150 PRINT B$:END
run
␣␣␣␣␣␣␣␣Вводите текст !
Мама мыла Эмму
Какое слово менять? мы
На какое слово менять? очень люби
␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣␣
Мама очень любила Эмму
Ok
* {{anchor:e0426-06}} 6) составьте программу, в результате работы которой выяснялось бы, можно ли переставить цифры десятичной записи числа K так, чтобы они образовывали арифметическую или геометрическую прогрессию.
\\ {{.examples:0426-06.bas|}} \\ [[+tab|wmsxbpge>0426-06.bas]]
NEW
Ok
10 CLS:DIM K,M%,D%,Q,F$:INPUT"Введите натуральное число";K
30 F$=STR$(K):M%=LEN(F$):DIM A(M%)
40 FOR I=1 TO M%:A(I)=VAL(MID$(F$,I,1)):NEXT
50 FOR I=2 TO M%-1:FOR J=I+1 TO M%
60 IF A(I)>A(J)THEN SWAP A(I),A(J)
70 NEXT J,I
80 D%=A(3)-A(2)
90 IF M%<3 THEN PRINT"Число должно содержать не менее 2 цифр":GOTO 20
100 IF M%=3 THEN PRINT"Арифм.прогрессия есть!":GOTO 150
110 FOR I=4TO M%
120 IF A(I)-A(I-1)<>D%THEN ?"Арифм.прогрессии нет":GOTO160
130 NEXT I
140 PRINT"Арифм.прогрессия есть!"
150 IF M%=3 AND A(3)=0 THEN PRINT"Геом.прогрессии нет":END
160 FOR I=2 TO M%:IF A(I)=0 THEN?"Геом.прогрессии нет":END
170 NEXT I
180 Q=A(3)/A(2)
190 IF M%=3 THEN ?"Геом.прогрессия есть!":END
200 FOR I=4 TO M%
210 IF A(I)/A(I-1)<>Q THEN PRINT"Геом.прогрессии нет":END
220 NEXT I
230 PRINT"Геом.прогрессия есть!":END
run run
Введите нат. число? 7132546 Введите нат. число? 42
Арифм.прогрессия есть! Арифм.прогрессия есть!
Геом.прогрессии нет Геом.прогрессия есть!
Ok Ok
* {{anchor:e0426-07}} 7) составить программу, которая m–кратно повторяет каждый символ в слове X$.
\\ {{.examples:0426-07.bas|}} \\ [[+tab|wmsxbpge>0426-07.bas]]
NEW
Ok
10 INPUT"Введите слово";X$
20 INPUT"Укажите число повторений символов";M%
30 IF M%=0 OR M%*LEN(X$)>255 THEN ?"Повторите ввод":GOTO10
40 N$="":FOR I=1 TO LEN(X$):FOR J=1 TO M%
70 N$=N$+MID$(X$,I,1):NEXTJ,I
90 PRINT "Вы этого хотели? ";N$
run
Введите слово? керпкпр
Укажите число повторений символов? 2
Вы этого хотели? ккееррппккппрр
Ok
* {{anchor:e0426-08}} 8) составить программу вывода всех натуральных чисел, меньших N, сумма квадратов цифр которых равна M.
\\ {{.examples:0426-08.bas|}} \\ [[+tab|wmsxbpge>0426-08.bas]]
NEW
Ok
5 DEFINT I,J,N,M,Y:INPUT N,M
10 FOR I=1 TO N:Y=0:A$=STR$(I)
20 FOR J=2 TO LEN(A$):Y=Y+VAL(MID$(A$,J,1))^2:NEXTJ
30 IF Y=M THEN PRINTI;:NEXTI ELSE NEXTI
40 END
run
? 123,65
␣18␣␣47␣␣74␣␣81␣␣108
Ok
* {{anchor:e0426-09}} 9) по данному натуральному N найдите наименьшее из чисел, имеющих столько же и таких же цифр, что и N, если известно, что каждую цифру числа N можно использовать при записи один раз.
\\ {{.examples:0426-09.bas|}} \\ [[+tab|wmsxbpge>0426-09.bas]]
NEW
Ok
10 DEFINT I,J: DEFSTR A
20 INPUT "В каком числе хотите перетрясти цифры";N
25 IF LEN(STR$(N))>14 THEN PRINT "Не озоруй!":GOTO 20
30 FOR I=0 TO9:FOR J=2 TO LEN(STR$(N))
40 IF I=VAL(MID$(STR$(N),J,1))THEN A=A+STR$(I)
50 NEXT J,I
60 PRINT VAL(A):END
run
В каком числе хотите перетрясти цифры? 87655543
␣34555678
Ok
run
В каком числе хотите перетрясти цифры? 112235799435213
Не озоруй!
В каком числе хотите перетрясти цифры? …
* {{anchor:e0426-10}} 10) написать программу, устанавливающую, какие из натуральных чисел, небольшие заданного натурального M, делятся на каждую свою цифру.
\\ {{.examples:0426-10.bas|}} \\ [[+tab|wmsxbpge>0426-10.bas]]
10 DEFINT I,J,M:DEFSTR A:INPUT M
30 FOR I=1 TO M:A=STR$(I):FOR J=2 TO LEN(A)
55 IF VAL(MID$(A,J,1))=0 THEN NEXT J:NEXT I:END ELSE::::
IF I MOD VAL(MID$(A,J,1))=0 THEN NEXTJ:PRINTI;:NEXTI ELSE NEXTI
run
? 40
␣1␣␣2␣␣3␣␣4␣␣5␣␣6␣␣7␣␣8␣␣9␣␣11␣␣12␣␣15␣␣22␣␣24␣␣33␣␣36
Ok
* {{anchor:e0426-11}} 11) определите, сколько цифр используется в записи натурального числа X только по одному разу.
\\ {{.examples:0426-111.bas|}} \\ [[+tab|wmsxbpge>0426-111.bas]]
\\ Первый способ.
10 INPUT X:X$=STR$(X)
20 K=0
30 FOR J=2 TO LEN(X$)
40 P=0
50 FOR I=2 TO LEN(X$)
60 IF MID$(X$,J,1)=MID$(X$,I,1) THEN P=P+1
70 NEXTI
80 IF P=1 THEN K=K+1
90 NEXTJ
100 PRINT "K=";K
run
? 45687345976
K=3
Ok
\\ {{.examples:0426-112.bas|}} \\ [[+tab|wmsxbpge>0426-112.bas]]
\\ Второй способ
10 INPUT"Введите число";X$
20 L=LEN(X$):P=L
30 FOR I=1 TO L:FOR J=1TO L
40 IFMID$(X$,I,1)=MID$(X$,J,1)ANDI<>J THEN P=P-1:NEXTI ELSE NEXT J,I
50 PRINT"Цифр,используемых 1 раз:";P
run
Введите число? 12341524162
Цифр,используемых 1 раз: 3
Ok
* {{anchor:e0426-12}} 12) из каждого слова массива C$(N) слов вычеркнуть те символы, которые употребляются при написании каждого из слов массива.
\\ {{.examples:0426-12.bas|}} \\ [[+tab|wmsxbpge>0426-12.bas]]
NEW
Ok
10 CLEAR 1500
20 INPUT"Введите количество слов";N:PRINT"Вводите слова!"
27 'В строках 30-60 идентификация исходного массива C$(N).
30 DIM C$(N):FOR I=1 TO N:INPUTC$(I):NEXT:K$=""
75 'В строках 80-130 находятся "общие" символы.
Значение переменной К$ состоит из "общих" символов.
80 FOR I=1 TO N:FOR J=1 TO LEN(C$(I)):FOR R=1 TO N
110 FOR K=1 TO LEN(C$(R))
120 IF MID$(C$(I),J,1)=MID$(C$(R),K,1) THEN NEXT R:K$=K$+MID$(C$(I),J,1):NEXT J ELSE NEXT K,J
130 NEXT I
135 'В строках 140-180 происходит"вычеркивание"из слов массива "общих" символов.
140 FOR I=1 TO N:FOR J=1 TO LEN(C$(I)):FOR K=1 TO LEN(K$)
170 IF MID$(C$(I),J,1)=MID$(K$,K,1)THENMID$(C$(I),J,1)=" "
180 NEXT K,J,I
185 'Строки 190-230 выполняют "сжатие" слов массива C$(N),т.к.
при "вычеркивании" "общих" символов образуются пробелы.
190 DIM N$(N)
200 FOR I=1 TO N:FOR J=1 TO LEN(C$(I))
220 IF MID$(C$(I),J,1)<>" "THENN$(I)=N$(I)+MID$(C$(I),J,1)
230 NEXT J,I
240 FOR I=1TO N:PRINT"Вот";I;"слово:" ;N$(I);" - не правда ли?":NEXT
run
Введите количество слов? 4
Вводите слова!
? апро
? ажип
? сонп
? портфель
Вот 1 слово:аро - не правда ли?
Вот 2 слово:ажи - не правда ли?
Вот 3 слово:сон - не правда ли?
Вот 4 слово:ортфель - не правда ли?
Ok
* {{anchor:e0426-13}} 13) определить количество //различных// букв, встречающихся в слове A$ более одного раза.
\\ {{.examples:0426-13.bas|}} \\ [[+tab|wmsxbpge>0426-13.bas]]
NEW
Ok
10 INPUT A$:S=0
20 FOR I=1 TO LEN(A$)
30 P=0
40 FOR J=1 TO LEN(A$)
50 IF MID$(A$,I,1)=MID$(A$,J,1) THEN P=P+1
60 NEXT J
70 IF P>1 THEN S=S+1/P
80 NEXT I
90 PRINT FIX(S+.5):END
run
aaaaaaaa
1
Ok
run
? бегемот
1
Ok
run
? Кинг-Конг
3
Ok
run
? мяучело
0
Ok
{{anchor:n43}} {{anchor:deffn}}
===== IV.3. Функции пользователя. Оператор DEF FN =====
Язык [[msx:basic:]] предоставляет возможность программисту (пользователю) //определять// в составляемой им программе одну или несколько "собственных" функций с помощью специального оператора ''DEF FN'', имеющего следующий синтаксис:
DEF FN α[(аргумент [, аргумент] …)] = β
,
где:
* ''DEF FN'' ("FuNction DEFinition" — "определение функции") — служебные слова;
* α — имя переменной, для которой выделяется память;
* аргумент — имя переменной, для которой память не выделяется (функции пользователя могут иметь не более 9 аргументов);
* β — выражение, имеющее тот же тип, что и α.
Итак, оператор, определяющий функцию пользователя, начинается со служебного слова ''DEF''. В идентификаторе определяемой функции два первых символа (буквы ''FN'') являются обязательными, а остальные (α) выбираются автором программы по его желанию. //Аргументы// перечисляются через запятую, и вся совокупность //аргументов// заключается в круглые скобки. Отметим, что функция пользователя может вообще не иметь аргументов.
Каждый //аргумент// является //формальным// параметром: это означает, что он на самом деле "не существует" (память для него не выделяется!) и поэтому не совпадает с переменными, имеющими такое же имя в программе.
Далее, после символа присваивания "=" в записи оператора, определяющего функцию пользователя, следует выражение β, задающее алгоритм вычисления значения функции пользователя. Это есть "образец", по которому программа каждый раз вычисляет значение функции, если, конечно, это необходимо. В выражении допустим вызов любой встроенной функции и вызовы других функций пользователя.
Приведём примеры //описания// функций пользователя:
* 1)
Формальные параметры
│ │ │ │
▼ ▼ ▼ ▼
10 DEF FN C(X,Y)=SQR(X+SIN(Y))
* 2)
Параметры (аргументы), не являющиеся формальными
│ │
▼ ▼
30 DEF FN BUS(Z)=A*Z+B*LOG(ABS(Z))+5467.453*FN C(4,0.7)
▲ ▲ ▲ ▲
│ │ │ │
Формальный параметр Функция пользователя
* 3)
Параметр (аргумент), не являющийся формальным
│
▼
100 DEF FN QU$(QU,ST)=MID$(OL$(QU),ST)
▲ ▲ ▲ ▲
│ │ │ │
Формальные параметры
Отметим, что //рекурсия запрещена// (и прямая, и косвенная!). Кстати, о понятии //рекурсии// см. в [[#n441|разделе IV.4]].
Посмотрите…
NEW
Ok
10 DEF FN Y(X)=(X<1)*FN Y(X)
20 PRINT FN Y(3)
run
Out of memory in 20
Ok
Напомним, что //исполняемым// оператором мы называем оператор программы, определяющий конкретные действия, которые должны быть выполнены.
Оператор ''DEF FN'' может стоять в любом месте программной строки. Однако, он, в отличие от оператора ''DATA'', — //исполняемый// оператор, т.е. функция пользователя должна быть определена до обращения к ней. Если же обращение к функции пользователя произведено раньше её определения оператором ''DEF FN'', то на экране дисплея появляется сообщение об ошибке: "Undefined user function in …" \\ ("Функция пользователя не определена в строке …").
Поэтому, как правило, в начале программы для операторов ''DEF FN'' специально резервируется несколько программных строк.
Если в ходе выполнения программы, необходимо использовать значение функции пользователя, определённой в этой программе, при заданных значениях её аргументов, то необходимо //вызвать// её, т.е. записать идентификатор функции в соответствующее выражение, заменив формальные параметры нужными значениями аргументов — //фактическими// параметрами (константами или выражениями).
Приведём примеры //вызова// функции пользователя:
* a)
50 R=FN C(3.07,5.)
▲ ▲
│ │
фактические параметры 3.07 и 5.
* b)
90 A=10:B=2:D=3.3:C=FN BUS(5*D-7)
▲
│
фактический параметр 5*D-7
Если выражение β содержит программные объекты (имена переменных, элементов массивов, функции), описанные в качестве формальных параметров в операторе ''DEF FN'', то их значения определяются из списка фактических параметров, указываемых при вызове функции пользователя.
Если выражение β содержит программные объекты, не входящие в список формальных параметров, то берутся их "текущие" значения из программы (на момент вызова функции пользователя).
В каждом из таких случаев интерпретатор обратится к описанию (определению) соответствующей функции пользователя, "подставит" вместо //формальных// параметров необходимые значения //фактических// параметров и произведёт требуемые вычисления, после чего результат вычисления значения функции возвращается в точку вызова функции.
Другими словами, компьютер выполнит над значениями фактических аргументов (параметров) все те действия, которые оператор ''DEF FN α'' выполняет над своими формальными аргументами.
__//Примеры//__:
* {{anchor:e043-01}} 1)
\\ {{.examples:043-01.bas|}} \\ [[+tab|wmsxbpge>043-01.bas]]
Ok
10 DEF FN P(X)=X^2+X+1:S=0
▲ ▲ ▲
│ │ │
Формальный параметр X
20 FOR K=1 TO 7
30 S=S+FN P(K)
▲
│
Фактический параметр K
40 NEXT K
50 PRINT S:END
run
175
Ok
* {{anchor:e043-02}} 2)
\\ {{.examples:043-02.bas|}} \\ [[+tab|wmsxbpge>043-02.bas]]
Формальный параметр X
NEW │ │
Ok ▼ ▼
10 DEF FN A(X)=X*7
20 INPUT"Сколько лет Вашей собаке";D
30 PRINT"Если бы собака была человеком,ей было бы";FN A(D);"лет!"
▲
│
Фактический параметр D
run
Сколько лет Вашей собаке? 4
Если бы собака была человеком,ей было бы 28 лет!
Ok
* {{anchor:e043-03}} 3)
\\ {{.examples:043-03.bas|}} \\ [[+tab|wmsxbpge>043-03.bas]]
Аргумент, не являющийся формальным параметром
NEW │
Ok ▼
10 DEF FN Z(A,B)=A^2+B^2+C
▲ ▲ ▲ ▲
│ │ │ │
Формальные параметры A и B
20 INPUT X,Y,C
30 PRINT FN Z(X,Y):END
▲ ▲
│ │
Фактические параметры X и Y
run
? 2,-3,5
18
Ok
* {{anchor:e043-04}} 4)
\\ {{.examples:043-04.bas|}} \\ [[+tab|wmsxbpge>043-04.bas]]
NEW
Ok
10 DEF FN S$(X$,K)=MID$(X$,K,1)
15 DEF FN Q$(X$)=FN S$(X$,K)+"$"
20 INPUT X$,K:PRINT FN Q$(X$):END
run
? APR,2
P$
Ok
run
? betta,4
t$
Ok
* {{anchor:e043-05}} 5)
\\ {{.examples:043-05.bas|}} \\ [[+tab|wmsxbpge>043-05.bas]]
NEW
Ok
10 INPUT X:IF X>1 THEN DEF FN Y(X)=X^2 ELSE DEF FN Y(X)=X^3
20 ? FN Y(X):END
run
? 2
4
Ok
run
? -12
-1728
Ok
* {{anchor:e043-06}} 6)
\\ {{.examples:043-06.bas|}} \\ [[+tab|wmsxbpge>043-06.bas]]
10 'Функция FN ODD$(N) помогает проверить,является ли целое число N нечетным:
если значением функции является -1, то число N — нечетное,
если же значением функции является 0, то число N — четное.
20 DEF FN ODD(N)=RIGHT$(BIN$(N),1)="1"
Отметим, что можно определить 4 различные функции пользователя, имеющие одинаковое имя, но различающиеся по типам.
* {{anchor:e043-07}} 7)
\\ {{.examples:043-07.bas|}} \\ [[+tab|wmsxbpge>043-07.bas]]
NEW
Ok
10 DEF FN Q%=1:DEF FN Q!=2:DEF FN Q=3:DEF FN Q$="4"
20 DEFINTQ:PRINT FN Q;
30 DEFSNGQ:PRINT FN Q;
40 DEFDBLQ:PRINT FN Q:PRINT
50 PRINT FN Q%;FN Q!;FN Q;FN Q$
run
␣1␣␣2␣␣3
␣1␣␣2␣␣3␣4
Ok
Этот факт приводит к удивительным последствиям: например, программа может модифицировать сама себя с помощью динамического изменения вызова функции:
|text1|text1=несоответствие типов формальных и \\ фактических параметров \\ при обращении к функции пользователя
||!@4|
|ошибка| ──▶
||!@4|
|обработка прерывания по ошибке ("ловушка" ошибки)| ──▶
||!@4|
|изменение типа функции пользователя|
* {{anchor:e043-08}} 8)
\\ {{.examples:043-08.bas|}} \\ [[+tab|wmsxbpge>043-08.bas]]
NEW
Ok
5 ON ERROR GOTO 100
10 DEF FN Y(X)=X^3
15 DEF FN Y$(X$)=X$+"(Козьма Прутков)"
18 DEFSTR X
20 LINEINPUT X:PRINT FN Y(X):END
100 IFERR=13 THEN DEFSTR Y:RESUME0'Код 13 имеет ошибка"Type mismatch"!
run
Смотри в корень!
Смотри в корень!(Козьма Прутков)
Ok
(см. раздел [[008#n834|VIII.3.4.]]).
Поскольку выражение β, содержащее алгоритм вычисления значения функции пользователя, хранится в памяти компьютера как //часть программы//, то ''DEF FN'' — один из нескольких операторов, которые нельзя использовать в режиме прямого выполнения команд (это пример //оператора//, который не является //командой// !).
* 9)
Ok
DEF FN R(X)=X^5:PRINT FN R(2)
Illegal direct
Ok
Сообщение об ошибке "Illegal direct" означает, что в качестве команды непосредственного выполнения встречается оператор, недопустимый в этом режиме.
Учтите, что если Вы допустили ошибку во время определения функции, эта ошибка будет обнаружена только тогда, когда функция встретится в выражении. Компьютер будет указывать как на источник ошибки на ту строку текста, где использована неверно заданная функция, а не на ту строку, где эта функция была определена!
{{anchor:n44}}
===== IV.4. Подпрограммы =====
Прежде всего отметим, что //подпрограммы// — это специальным образом оформленные группы программных строк. Подпрограммы бывают двух типов: //стандартные// и //нестандартные//.
Первые из них входят в состав математического обеспечения компьютера. Нам часто приходится неявным образом обращаться к ним, когда, например, вводим данные в память, печатаем результаты счета, вычисляем значения встроенных функций и т.п.
//Нестандартные// подпрограммы составляются самим пользователем и являются фактически фрагментами его программ.
В виде подпрограмм целесообразно оформлять логически завершённые части алгоритма, имеющие самостоятельную ценность; кроме того, использование подпрограмм позволяет экономить время и место в том случае, когда нужно в одной и той же программе несколько раз выполнить какую–либо последовательность операторов.Вместо того, чтобы многократно переписывать эту последовательность, напишите единственный программный //модуль//, к которому Вы сможете обращаться всякий раз при необходимости.
Подпрограмма, следовательно, играет в какой–то степени ту же роль, что в математике "теорема" или "лемма": когда нужно доказать результат в некоторой специальной области, например, в дифференциальной геометрии, вовсе не обращаются каждый раз к базовым аксиомам теории множеств, а опираются на теоремы, которые косвенно через несколько уровней абстракции основываются на этих аксиомах.
{{anchor:gosub}}
Общая форма записи оператора обращения к подпрограмме (оператора вызова подпрограммы) следующая:
GOSUB n
,
где:
* ''GOSUB'' ("GO to SUBroutine" — "идти к подпрограмме") — служебное слово;
* n — номер первой строки подпрограммы, 0≤n≤65529.
Разумеется, нежелательно, чтобы строка с номером n находилась внутри цикла!
Использование несуществующего номера программной строки n вызывает по явление на экране дисплея сообщения об ошибке вида: "Undefined line number" \\ (//"Не определён номер строки"//).
{{anchor:return}}
Подпрограмма, как правило (!), завершается оператором
RETURN [m]
,
где
* ''RETURN'' ("return" — "возврат") — служебное слово.
* m — номер существующей программной строки, которой должно быть передано управление после выполнения подпрограммы.
Оператор ''GOSUB n'' используется для вызова подпрограммы — группы операторов, начинающихся программной строкой с номером n и заканчивающихся оператором ''RETURN''. Подпрограмма может начинаться с комментария и находиться в любом месте Вашей программы.
Главной особенностью оператора ''GOSUB'' является то, что оператор ''RETURN'' (без параметра m) возвращает управление оператору, стоящему за последним выполненным оператором ''GOSUB''. Заметим,что оператор, которому возвращается управление, может находиться на той же программной строке, что и ''GOSUB'' !
Приведём пример:
{{anchor:e044-01}} {{.examples:044-01.bas|}} \\ [[+tab|wmsxbpge>044-01.bas]]
Ok
100 X=2
110 GOSUB 200:X=3
120 GOSUB 200
125 X=4
140 END
200 PRINT X^X
210 RETURN
run
4
27
Ok
Ok ┌──────────────────┐
100 X=2 ▼ │
┌── 110 GOSUB 200:X=3 │
│ ┌ 120 GOSUB 200 │
│ │ 125 X=4 ◀─ ─ ─ ─ ─ ─ ─ ─ ┐ │
│ │ 140 END │ │
│ │ │ │
│ └─ ─ ─ ─ ─ ─ ─ ┐ │ │
│ ▼ │ │
└─────────────▶ ┌───────────────┐ │ │
│ 200 PRINT X^X │ │ │
│ 210 RETURN │─┘─┘
└───────────────┘
Оператор ''GOSUB n'' может показаться очень похожим на оператор ''GOTO n''. Существенная разница между ними в том, что оператор ''GOSUB'' как бы "посылает в командировку" — после выполнения операторов подпрограммы происходит возвращение назад.
Основная программа │ Подпрограмма
┌───────────────────┐ ▼─────────▶┌────────────┐
│ GOSUB │ │ ││ │
│ … │ │ ││ │
│ GOSUB │ ▼ ││ │
│ … │ │ ││ │
│ END │ │ ││ RETURN │
└───────────────────┘ ▼ ▼└────────────┘
//Все// переменные в подпрограмме являются //глобальными//. Это означает, что "доступ" к их значениям возможен из //любого// места Вашей основной программы.
Глобальная переменная — переменная, областью существования которой является вся программа. Локальная переменная — переменная с ограниченной областью существования в программе.
Образно говоря, переменные, имеющие одно и то же имя в основной (вызывающей) программе и подпрограмме не "однофамильцы", а одно и то же лицо!
При вызове подпрограммы значения переменных в основной программе не "замораживается", а могут быть изменены операторами подпрограммы! Без "замораживания" то и дело происходят пренеприятнейшие вещи.
Представьте себе, что Вы подарили своему товарищу очень ценную для него подпрограмму и объяснили, как ею пользоваться. Разумеется, Вашему товарищу нет никакого дела до того, какие имена переменных Вы использовали в Вашей подпрограмме. Поэтому может случиться, что и в его основной программе будут использоваться переменные с теми же именами, что и в подаренной Вами подпрограмме. Ясно, что эти переменные "неразличимы" для интерпретатора. Дальнейшее ужасно!
Как говорят программисты, в [[msx:basic:]] постоянно присутствует "побочный эффект подпрограммы"!
Следовательно, важно выделить конкретные имена переменных, используемых //только// в данной подпрограмме и применить их для передачи значений аргументов из основной программы и возвращения результатов работы подпрограммы в основную программу.
Впрочем, оператор ''RESTORE'' (см. [[002#n245|раздел II.4.5.]]) предоставляет возможность каждой подпрограмме иметь свои собственные (//локальные//) переменные. Необходимо просто при //каждом// обращении к ней выполнять оператор ''RESTORE'' для установки указателя на начало данных подпрограммы.
{{anchor:e044-02}} __//Пример//__. \\ {{.examples:044-02.bas|}} \\ [[+tab|wmsxbpge>044-02.bas]]
NEW
Ok
10 DATA 5,2:'Данные основной программы
20 DATA 7,.5:'Данные подпрограммы
30 READ A,B:?A+B;:GOSUB 40:?A+B:END
40 RESTORE 20:READ A,B:?A+B;:RESTORE 10:READA,B:RETURN'A и B - локальные переменные подпрограммы!
run
␣7␣␣7.5␣␣7
Ok
Наиболее распространённой ошибкой при использовании подпрограмм является их неполное отделение от основной программы. Основными способами защиты от указанной ошибки являются:
- использование оператора ''END''; например, если //пакет// подпрограмм (совокупность нескольких подпрограмм) начинается с программной строки 100, то строка основной программы
99 END
предотвратит вход в подпрограмму, начинающуюся со строки 100;
- использование в основной программе перед первой строкой подпрограммы оператора ''GOTO k'', где k — номер программной строки, расположенной за строкой, в которой расположен оператор ''RETURN'' данной подпрограммы (см. пример из [[#n441|раздела IV.4.1.]]).
Поэтому сформулируем важный практический вывод.
**Помещайте подпрограммы или в //самом конце// или в //самом начале// основной программы !**
При случайном(!) попадании в подпрограмму без использования оператора ''GOSUB'' она выполнится нормально, но компьютер выдаст сообщение об ошибке "RETURN without GOSUB" \\ ("RETURN //без// GOSUB").
Однако, если Вы случайно забыли поставить оператор ''RETURN'', то интерпретатор может иногда не заметить ошибку! Он будет тщетно искать "забытый" оператор ''RETURN'' до конца программы, и, не найдя, прекратит все вычисления (т.к. программа уже будет выполнена полностью). Сообщения об ошибке не будет! Берегитесь!
Учтите, что операторы, стоящие за оператором ''RETURN'' в той же программной строке, //никогда// не выполняются, поэтому за оператором ''RETURN'' целесообразно помещать только комментарии.
Например,
50 RETURN '──▶
или
50 RETURN ──▶
.
Поговорим теперь о совместном использовании операторов ''FOR…NEXT'' и ''GOSUB n''.
Внутри цикла использовать оператор ''GOSUB'', //можно//, но оператор ''NEXT'' должен быть выполнен только //после// выполнения оператора ''RETURN''.
В противном случае последует сообщение: "NEXT without FOR" \\ ("NEXT //без// FOR").
{{anchor:e044-03}} __//Пример//__. \\ {{.examples:044-03.bas|}} \\ [[+tab|wmsxbpge>044-03.bas]]
NEW
Ok
5 INPUT K,N:FOR I=1 TO N:GOSUB 100:PRINT I;:NEXTI:END
100 IF K>I THEN NEXTI:RETURN ELSE RETURN
run
? 2,6
NEXT without FOR in 100
Ok
run
? 0,6
␣1␣␣2␣␣3␣␣4␣␣5␣␣6
Ok
С другой стороны, если цикл ''FOR…NEXT'' используется в подпрограмме, то оператор ''RETURN'', находящийся в цикле ''FOR…NEXT'', позволяет выйти из цикла и вернуться в основную программу. Это наводит на мысль, что наиболее эффективно размещать процедуру поиска элемента массива, обладающего требуемыми свойствами, //внутри// подпрограммы. Как только искомый элемент будет найден, выполнится выход из подпрограммы.
Однако учтите, что в момент выполнения оператора ''RETURN'' любой незаконченный цикл ''FOR…NEXT'' в подпрограмме заканчивается и часть стекового пространства, которое он (цикл) занимал, освобождается!
{{anchor:e044-04}} __//Пример//__. \\ {{.examples:044-04.bas|}} \\ [[+tab|wmsxbpge>044-04.bas]]
Вывести на экран дисплея первое нечётное число, встретившееся в целочисленном массиве A(K).
NEW
Ok
10 DEFINT A:INPUT K:DIM A(K):DATA 2,1,4,7,8
30 FOR I=1 TO K:READ A(I):NEXTI:GOSUB 100:END
100 FOR J=1 TO K:IF VAL(RIGHT$(STR$(A(J)),1))MOD2=1THEN?A(J):RETURN ELSE NEXTJ
120 PRINT "нечетных элементов в массиве нет":RETURN
run
? 3
1
Ok
run
? 5
1
Ok
При многократном выполнении выхода из подпрограмм с помощью операторов ''GOTO'', ''ON GOTO'' или ''IF…THEN…ELSE'' (вместо оператора ''RETURN'') в конце концов на экране может появиться сообщение об ошибке: "Out of memory" \\ (//"Не хватает памяти"//).
Причина возникающей ошибки довольно своеобразна: дело в том, что интерпретатор отводит сравнительно небольшой участок динамической памяти для хранения списка адресов возврата из подпрограмм. Такой список организуется в виде стека и называется //рабочим стеком//. Каждый раз при вызове подпрограммы в стек заносится соответствующий этому вызову //адрес возврата//, занимающий 7 байт. При выполнении оператора ''RETURN'' из стека извлекается адрес возврата, записанный последним, и происходит передача управления по этому адресу, после чего он удаляется из стека. Операторы, подобные ''GOTO'' или ''IF…THEN…ELSE'', осуществляют выход из подпрограммы, не затрагивая стека! Если такой выход из подпрограмм происходит часто, то адреса из стека не удаляются, а лишь добавляются при каждом новом вызове подпрограммы, так что в результате размер стека может превысить величину отведённого ему участка памяти. В итоге программа прекращает работу, и выдаётся сообщение об ошибке.
{{anchor:e044-05}} __//Пример//__. \\ {{.examples:044-05.bas|}} \\ [[+tab|wmsxbpge>044-05.bas]]
10 GOSUB 20:END
20 FOR I=1 TO 1:K=K+1:GOTO 10:NEXTI:RETURN
run
Out of memory in 20
Ok
а теперь… print K
␣894
Ok
В программе может быть несколько операторов ''RETURN'', относящихся к одному оператору ''GOSUB n''. Отметим, что наличие оператора ''RETURN'' в некотором месте подпрограммы ещё не означает фактического окончания подпрограммы в данном месте.
{{anchor:e044-06}} __//Пример//__. Написать программу, вычисляющую значение функции y=│x+x²│ в точке x=A (не применяя функции ''ABS()''). \\ {{.examples:044-06.bas|}} \\ [[+tab|wmsxbpge>044-06.bas]]
NEW
Ok
10 INPUT A:U=A+A^2:GOSUB 100:PRINT Z:END
100 'Подпрограмма вычисления │x│: аргумент U, результат Z
110 IF U>=0 THEN Z=U:RETURN ELSE Z=-U:RETURN
run
? -4
12
Ok
Подпрограмма может, в свою очередь, вызывать другую подпрограмму, та — следующую и так далее. Глубина вложения (степень вложения) подпрограмм ограничивается лишь размерами стека.
Наглядно это можно представить себе следующим образом. Пусть Вас из Куйбышева послали в Москву на повышение квалификации, а оттуда ещё в Ленинград на курсы. По окончании этих курсов Вы возвращаетесь в Москву, заканчиваете учёбу там и только после этого возвращаетесь домой.
Вложенные подпрограммы являются довольно мощным средством разработки и отладки больших программ. Используя принцип вложений, можно не только разбивать сложную программу на отдельные модули и для каждого из них писать свою подпрограмму, но и //разделять// на //модули// любые подпрограммы. Чем меньше будет каждый выделенный программный модуль, тем легче будет его программировать и отлаживать и тем больше вероятность его многократного использования в других программах!
"Трудности, обусловленные бессистемным написанием (без использования модульной структуры) большой и сложной программы, можно сравнить с трудностями, возникающими при попытке съесть сразу //целиком// весь батон колбасы; в то же время, если разрезать колбасу на ломтики, то съесть её не представит никакого труда" (Л.Пул).
{{anchor:e044-07}} Приведём пример, иллюстрирующий вложение подпрограмм:
\\ {{.examples:044-07.bas|}} \\ [[+tab|wmsxbpge>044-07.bas]]
NEW
Ok
1' Программа вычисления суммы вида
2' k k k
3' S = x + x +...+ x ,
4' 1 2 n
5' где x - корни линейного алгебраического уравнения n-ой степени
6' i
7'Алгоритм решения поставленной задачи основан на формулах Ньютона,
приведенных в книге: А.П.Мишина, И.В.Проскуряков "Высшая алгебра".
М.:ГИФМЛ, 1962, гл.III, 3, с.245.
11 INPUT "Укажите степень многочлена";N:INPUT"Укажите k";K
15 DIM A(N) 'Описан массив коэффициентов уравнения!
18 PRINT"Вводите коэффициенты уравнения"
20 FOR I=0 TO N:INPUT A(I):NEXT 'Итак, массив А(N) введен!
40 DIM SIG(N):FOR I=1 TO N:SIG(I)=(-1)^I*A(I)/A(0):NEXTI
80 IF N>=K THEN DIM S(N):A1=N:A2=K:GOSUB 200 ELSE DIM S(K):A1=K:A2=N:GOSUB 300
110 PRINT S(K):END
200 '¤¤¤¤¤ Начало подпрограммы 1 ¤¤¤¤¤
205 S(1)=SIG(1):FOR J=2 TO A2:S(J)=(-1)^(J+1)*J*SIG(J)
230 FOR I=1 TO J-1:S(J)=S(J)+(-1)^(J-I+1)*S(I)*SIG(J-I)
250 NEXTI:NEXTJ:RETURN '──▶
300 '¤¤¤¤¤ Начало подпрограммы 2 ¤¤¤¤¤
305 GOSUB 200 'Вот оно, в л о ж е н и е подпрограмм!
360 FOR J=N+1 TO K:S(J)=0
380 FOR I=1 TO N:S(J)=S(J)-(-1)^S(J-I)*SIG(I)
400 NEXT I,J:RETURN '──▶
run
Укажите степень многочлена? 2
Укажите k? 2
Введите коэффициенты уравнения
? 1
? 0
? -1
2
Ok
{{anchor:n441}}
==== IV.4.1. Примеры ====
{{anchor:e0441-01}} __//Пример 1//__. Написать программу, вычисления числа сочетаний из m по n используя формулу \\ C matrix{2}{1}{n {m}}={m!}/{n!(m-n)!}, (m>=n).
Целесообразно разработать подпрограмму вычисления факториала и затем трижды (почему?) обратиться к ней из основной программы. Итак,
\\ {{.examples:0441-01.bas|}} \\ [[+tab|wmsxbpge>0441-01.bas]]
NEW
Ok
40 INPUT "Введите значения m,n";M,N:GOTO 100' ───┐
50 'Подпрограмма вычисления факториала F! │
60 F=1:FOR J=1 TO K:F=F*J:NEXTJ' │
90 RETURN 'Конец подпрограммы! │
100 K=M:GOSUB 60:M1=F' ◀──────────────────────┘
110 K=N:GOSUB 60:N1=F
120 K=M-N:GOSUB 60:R1=F
130 CMN=M1/N1/R1
140 ? "Число сочетаний из М=";M;"по N=";N;"равно";CMN:END
run
Введите значения m,n? 6,3
Число сочетаний из M= 6 по N= 3 равно 20
Ok
Заметим, что оператор ''GOSUB'' может оказаться полезным в качестве команды прямого режима. Если Ваша программа состоит из подпрограмм, которые Вы хотите проверить, то, используя команду ''GOSUB'' в прямом режиме, можно войти в любую подпрограмму. После выполнения оператора ''RETURN'' система [[msx:basic:]]
снова вернётся в режим прямого выполнения команд.
В примере 1, например, можно проверить работоспособность подпрограммы следующим образом (находясь в прямом режиме!):
F=1:k=4:GOSUB 60
Ok
print F
24
Ok
\\
Ибо это недостойно совершенства человеческого,
подобно рабам тратить часы на вычисления.
—//Г.Лейбниц//
{{anchor:e0441-02}} __//Пример 2//__. Составить программу, которая позволяет методом половинного деления (дихотомии) на отрезке [A,B] с точностью E определить какой–либо (!) вещественный корень уравнения F(x)=0, где F(x) непрерывна на [A,B] и на концах отрезка принимает значения разных знаков (F(A)×F(B)<0).
Пусть, например,
F(x)=cos e^x-sin pi^x, A=1, B=3, E=0.00001.
\\ {{.examples:0441-02.bas|}} \\ [[+tab|wmsxbpge>0441-02.bas]]
NEW
Ok
10 INPUT A,B,E
20 X=A:GOSUB 200:F1=Y
30 X=(A+B)/2
40 GOSUB 200:F2=Y:IF Y=0 THEN 90
50 IFSGN(F1)*SGN(F2)>0 THEN 70
60 B=X:GOTO 80
70 A=X:F1=F2
80 IF B-A>E THEN 30
90 PRINTUSING "Корень####.##### точность##^^^^";X,E
100 END
200 Y=COS(EXP(X))-SIN(EXP(X*LOG(3.14159)))
210 RETURN
run
? 1,3,.00001
Корень␣␣␣1.27163␣точность␣1Е-05
Оk
{{anchor:e0441-03}} __//Пример 3//__. Если у Вас есть знакомый художник, попросите его разделить произвольный отрезок на две неравные части. Возможно он сделает это в золотом отношении, если его чувство меры воспитано на классических образцах.
Такое чувство меры целесообразно "внушить" компьютеру, которому часто приходится отыскивать единственную точку минимума некоторой функции y=f(x) на отрезке [a,b]. Основа алгоритма — последовательное приближение к минимуму до достижения заданной точности E.
\\ {{.examples:0441-03.bas|}} \\ [[+tab|wmsxbpge>0441-03.bas]]
NEW
Ok
10 'Поиск минимума функции y=f(x) на отрезке [a,b] методом золотого сечения
20 DEFFN Y(X)=-X^2+5: 'Вид функции
30 INPUT"A,B,точность";A,B,E
40 GOSUB 110:GOSUB120
50 IF ABS(B-A)Y2 THEN 70
65 B=X2:X2=X1:Y2=Y1:GOSUB 110:GOTO 80
70 A=X1:X1=X2:Y1=Y2:GOSUB120 '──▶
80 GOTO 50
90 X=(A+B)/2:PRINT"Y мин=";FNY(X);"при x=";X
100 END
110 X1=.618*A+.382*B:Y1=FNY(X1):RETURN '──▶
120 X2=.382*A+.618*B:Y2=FNY(X2):RETURN '──▶
run
A,B,точность? -1,4
?? 0.00001
Y мин=-10.999971821564 при x= 3.999996477694
Ok
{{anchor:e0441-04}} __//Пример 4.//__
Вычислить \\ {1/pi}*int{0}{pi}{delim{[}{ {sin(10*x)}/{sin(x)} }{]}^{6}}dx по формуле парабол (формуле Симпсона). Внимание! Интеграл несобственный!
\\ {{.examples:0441-04.bas|}} \\ [[+tab|wmsxbpge>0441-04.bas]]
NEW
Ok
20 INPUT "Нижний предел";A
30 INPUT "Верхний предел";B
40 INPUT "Количество точек";M
70 S=0:H=(B-A)/M
80 S1=0:FOR X=A+H/2 TO B-H/2 STEP H
90 GOSUB 180:S1=S1+Y:NEXT:S1=S1*4
100 S2=0:FOR X=A+H TO B-H STEP H
110 GOSUB 180:S2=S2+Y:NEXT:S2=S2*2
120 X=A:GOSUB 180:S=S+Y
130 X=B:GOSUB 180:S=S+Y
140 S=(S+S1+S2)*H/6
160 PRINT"Интеграл = ";S:END
180 Y=(SIN(10*X)/SIN(X))^6/(4*ATN(1)):RETURN ──▶
run
Нижний предел? 0.00000001
Верхний предел? 3.14159265
Количество точек? 250
Интеграл = 55251.99567425
Ok
run
Нижний предел? 0.00000001
Верхний предел? 3.14159265
Количество точек? 500
Интеграл = 55251.995674258
Ok
{{anchor:e0441-05}} __//Пример 5//__. Написать программу, осуществляющую лексикографическое упорядочение (расположение в алфавитном порядке) массива фамилий на русском языке (после фамилий можно указывать инициалы, например: Бобров А.В.).
\\ {{.examples:0441-05.bas|}} \\ [[+tab|wmsxbpge>0441-05.bas]]
NEW
Ok
10 DATA " ",".","А","Б","В","Г","Д","Е","Ж","З","И","Й","К","Л","М","Н",
"О","П","Р","С","Т","У","Ф","Х","Ц","Ч","Ш","Щ","Ы","Ь","Э","Ю","Я"
15 DATA " ",".","-","а","б","в","г","д","е","ж","з","и","й","к","л","м","н",
"о","п","р","с","т","у","ф","х","ц","ч","ш","щ","ы","ь","э","ю","я"
20 INPUT"Введите количество фамилий";S:DIM C$(S)
30 FOR I=1TOS:INPUT"Введите очередную фамилию";C$(I):NEXTI
40 GOSUB 190
50 DIM N(L1,S)
56 FOR I=1 TO S:RESTORE 10:FOR J=1 TO 33:READB$
57 IF B$=MID$(C$(I),1,1) THEN N(1,I)=J
58 NEXTJ
70 FOR K=2 TO LEN(C$(I))-4
71 IF MID$(C$(I),K-1,1)="-"THEN RESTORE 10 ELSE RESTORE 15
80 FOR J=1 TO 34
90 READ B$:IF B$=MID$(C$(I),K,1) THEN N(K,I)=J
100 NEXT J,K
101 FOR K=LEN(C$(I))-3 TO L1:RESTORE 10
103 FOR J=1 TO 33
105 READ B$:IF B$=MID$(C$(I),K,1) THEN N(K,I)=J
107 NEXT J,K
108 NEXT I
110 FOR K=1 TO L1
120 FOR I=1 TO S-1:FORJ=I+1 TO S
130 IF MID$(C$(I),1,K)=MID$(C$(J),1,K) THEN GOTO 140 ELSE
IF(N(K,I)>N(K,J))AND(MID$(C$(I),1,K-1)=MID$(C$(J),1,K-1)) THEN
SWAP C$(I),C$(J):FOR L=1 TO L1:SWAP N(L,I),N(L,J):NEXT L
140 NEXT J,I,K
150 CLS 'Очистим экран дисплея!
160 PRINT SPC(10);"Итоговый список"
170 FOR I=1 TO S:PRINT SPC(10);STR$(I);". ";C$(I):NEXTI
180 END
190 L1=0:FOR P=1 TO S:IF LEN(C$(P))>L1THEN L1=LEN(C$(P))
200 NEXT:RETURN
run
Введите количество фамилий? 4
Введите очередную фамилию? Ухин И.Н.
Введите очередную фамилию? Ми-Ка М.У.
Введите очередную фамилию? Ми-Ша А.А.
Введите очередную фамилию? Лим А.
Итоговый список
1. Лим А.
2. Ми-Ка М.У.
3. Ми-Ша А.А.
4. Ухин И.Н.
Ok
{{anchor:e0441-06}} __//Пример 6//__. Абсолютно простым числом назовём натуральное число, обладающее следующим свойством: при любой перестановке цифр данного числа образуется также простое число. Найдите все абсолютно простые числа, принадлежащие [C,E].
\\ {{.examples:0441-06.bas|}} \\ [[+tab|wmsxbpge>0441-06.bas]]
NEW
Ok
1 CLS 'Вначале очистим экран!
2 INPUT "Введите два натуральных числа через запятую, причём второе число
должно быть больше первого (эти числа задают диапазон,
в котором ищутся абсолютно простые числа)";C,E
3 PRINT"Вы хотите узнать всю правду об абсолютно простых числах, лежащих на
отрезке [";C;",";E;"] ? Пожалуйста..."
4 FOR A=C TO E:IF A<10AND(A=1ORA=2ORA=3ORA=5ORA=7)THEN PRINTA;:
NEXTA ELSE IF A<10AND(A=4ORA=6ORA=8ORA=9)THEN NEXTA ELSE A$=MID$(STR$(A),2)
5 N=LEN(A$):DIM A(N)
10 FOR L=1TO N:A(L)=VAL(MID$(A$,L,1)):NEXT L:GOSUB 200
20 I=N
30 GOTO 110
40 GOSUB 300:J=N-1
50 IF A(J)>=A(J+1)ANDJ>0 THEN J=J-1:GOTO 50
60 I=J
70 IF I>0 THEN J=N:GOTO 80 ELSE GOTO 110
80 IF A(I)>=A(J) THEN J=J-1:GOTO 80
90 SWAP A(I),A(J):P1=I+1:P2=N
100 IF P10 THEN GOTO 40
120 ERASEA:IF JJ<>1 THEN PRINT A;:NEXT AELSE JJ=0:NEXTA
125 PRINT:PRINT"Надеюсь, Вы остались довольны?!"
130 END
190 'Подпрограмма, производящая сортировку массива A(N),
состоящего из цифр числа А.
200 FOR M=1 TO N-1:FOR S=M+1 TO N
210 IFA(M)<=A(S)THEN NEXT:NEXT ELSE SWAP A(M),A(S):NEXT:NEXT
220 RETURN
290 'Подпрограмма, позволяющая определить, является ли число V,
составленное из цифр числа A, простым.
300 V=0:FOR P=1TON:V=V+A(P)*10^(N-P):NEXT
310 FOR Q=2 TO INT(SQR(V))
320 IF VMODQ=0 THEN JJ=1:RETURN ELSE NEXT Q:RETURN
run
Введите два натуральных числа через запятую, причём второе число
должно быть больше первого (эти числа задают диапазон,
в котором ищутся абсолютно простые числа)? 1,100
Вы хотите узнать всю правду об абсолютно простых числах,лежащих на отрезке [1, 100] ? Пожалуйста…
␣1␣␣2␣␣3␣␣5␣␣7␣␣11␣␣13␣␣17␣␣31␣␣37␣␣71␣␣73␣␣79␣␣97
Надеюсь, Вы остались довольны?
Ok
{{anchor:e0441-07}} __//Пример 7//__.
\\ {{.examples:0441-07.bas|}} \\ [[+tab|wmsxbpge>0441-07.bas]]
10'****************************************************
30'* Программа LONGMULT *
50'* позволяет найти точное значение произведения *
70'* не более чем 210-значных чисел *
81'* Мах. время работы - 1.5 мин. *
83'****************************************************
90 CLEAR 1000:DIMA(30),C(31,61),D(62),D$(62),B(30),S(62):CC=10000000!
92 DEFFND$(X)=RIGHT$("0000000"+RIGHT$(STR$(X),LEN(STR$(X))-1),7):CLS
100 PRINT"Введите 1-й множитель":PRINT"a=";:GOSUB 150
110 FOR I=1TO N:A(I)=S(1):NEXT:NA=N
120 ?:PRINT"Введите 2-й множитель":PRINT"b=";:GOSUB 150
130 FOR I=1 TO N:B(I)=S(I):NEXT:NB=N:GOSUB 190
135 IF D(1)=0 THEN S=2 ELSE S=1
140 PRINT:PRINT"a*b=";:FOR I=S TO NA+NB:PRINT FND$(D(I));:NEXT
145 PRINT :PRINT:END
150 VV$=""
160 S$=INKEY$:IF S$=""THEN160 ELSE IF S$=CHR$(13) THEN PRINT:
GOTO170 ELSE IF ASC (S$)<48 OR ASC(S$)>57 THEN160 ELSE PRINT S$;:VV$=VV$+S$:GOTO160
170 N=LEN(VV$):IFNMOD7=0THEN180 ELSE VV$="0"+VV$: GOTO170
180 N=N/7:FOR I=1 TO N:S(I)=VAL(MID$(VV$,1+(I-1)*7,7)):NEXT:RETURN
190 '∗∗∗ Собственно длинное умножение ∗∗∗
200 FOR I=NB TO 1 STEP-1:S=0
210 FOR J=NA TO 0 STEP-1
220 C(I,J+I)=S+B(I)*A(J)-INT((S+B(I)*A(J))/CC)*CC:S=INT((S+B(I)*A(J))/CC):NEXT:NEXT
230 S=0:FORJ=NA+NBTO1 STEP-1:FORI=1TONB:D(J)=D(J)+C(I,J):NEXT:D(J)=D(J)+S:
S=INT((D(J)+S)/CC):D(J)=D(J)-INT(D(J)/CC)*CC:NEXT:RETURN
run
Введите 1-й множитель
a=11111111111111111111111111111111111111111111111111111
Введите 2-й множитель
b=10000000000000000000000000000000000000000000000000001
a*b=11110001111000111100011110001111000
111100011110001112111000111100011110001
1110001111000111100011110001111
Ok
\\
У попа была собака, он её любил.
\\ Она съела кусок мяса — он её убил!
\\ Убил и закопал, и надпись написал:
\\ "У попа была собака, он её любил.
\\ Она съела кусок мяса — …"
—//Из русского народного фольклора//
Допустимо использование рекурсивных подпрограмм.
//Рекурсией// называется обращение подпрограммы (функции) к самой себе непосредственно или посредством других подпрограмм. Такие программы (функции) называются //рекурсивными//. Само слово "рекурсия" означает "возвращение" (лат. "recursio").
В программировании различают два типа рекурсии — рекурсивное определение или //прямую// рекурсию и рекурсивное использование или //косвенную// рекурсию.
Рекурсивное использование — это способ описания процесса через подпроцессы, идентичные основному процессу, а рекурсивное определение — способ описания процесса через самого себя.
Рекурсия получается в программах, реализующих алгоритмы, которые содержат //рекуррентные// формулы, то есть формулы, в которых значение функции от некоторого аргумента X выражается через значение этой же функции, но от другого аргумента Y, который в каком–то смысле "предшествует" X.
Рекуррентной является, например,формула вычисления факториала:
⎧ 1, если k=1,
F(k)= ⎨
⎩ F(k-1)×k, если k>1.
{{anchor:e0441-11}} __//Пример 1//__. Составим программу вычисления факториала натурального числа К, содержащую рекурсивную подпрограмму.
\\ {{.examples:0441-11.bas|}} \\ [[+tab|wmsxbpge>0441-11.bas]]
NEW
Ok
10 INPUT K:F=1:GOSUB 100 '──▶
30 PRINT F:END
90 'Внимание! Рекурсивная подпрограмма!
100 IF K>1 THEN F=F*K:K=K-1:GOSUB 100 '──▶
110 RETURN '──▶
run
? 4
24
Ok
run
? 6
720
Ok
run
? 48
1.2413915592536E+61
Ok
run
? 49
Overflow in 100
Ok
Конечно, любая рекурсивная подпрограмма может быть написана как циклическая программа. Например, последнюю программу можно переписать следующим образом:
10 INPUT K:F=1
20 IF K>1 THEN F=F*K:K=K-1:GOTO 20
30 PRINT F:END
Но многим программистам больше нравится использование рекурсивных подпрограмм, несмотря на то, что коварство рекурсии проявляется в том, что бывает довольно трудно заметить дефект программы или алгоритма, а в циклическом процессе все на виду!
{{anchor:e0441-12}} __//Пример 2//__. Пользуясь рекуррентной формулой
\\ C matrix{2}{1}{0 n} = 1; C matrix{2}{1}{m n}={{n-m+1}/m} * C matrix{2}{1}{{m-1} n},(∗)
\\ вычислить число сочетаний из n по m при m, n=1, k, k>=1, m<=n.
Приведём два варианта программы.
* a) {{.examples:0441-121.bas|}} \\ [[+tab|wmsxbpge>0441-121.bas]]
10 INPUT K
20 FOR N=0 TO K:C=1:FOR M=1 TO N:IF M>N THEN NEXTN
ELSE C=(N-M+1)/M*C:PRINT N;M;FIX(C+.5):NEXT:NEXT
run
? 3
␣1␣␣1␣␣1
␣2␣␣1␣␣2
␣2␣␣2␣␣1
␣3␣␣1␣␣3
␣3␣␣2␣␣3
␣3␣␣3␣␣1
Ok
* b) {{.examples:0441-122.bas|}} \\ [[+tab|wmsxbpge>0441-122.bas]]
NEW
Ok
5 'Программа находит число сочетаний из N по M по формуле (∗)
с применением рекурсивной подпрограммы.
10 INPUT N,M:C=N:GOSUB100:PRINT FIX(C+.5):END
100 IF M>1THEN C=C*(N-M+1)/M:M=M-1:GOSUB100:RETURN ELSE RETURN
run
? 232,231
232
Ok
run
? 233,232
0 ◀── ?!
Ok
run
? 4080,4079
Out of memory in 100
Ok
далее…
Ok
print m
2
Ok
{{anchor:e0441-13}} __//Пример 3//__ [[bibliography#b6|[6]]].
\\ {{.examples:0441-131.bas|}} \\ [[+tab|wmsxbpge>0441-131.bas]]
NEW
Ok
100 INPUT "Строка для сжатия:";FS$
109 'Удаление пробелов из FS$.
110 S$=FS$:RMV$=" ":GOSUB 200
130 PRINT S$:END
140 '∗∗∗∗ Рекурсивная подпрограмма сжатия строк. ∗∗∗∗
150 'В RMV$ - удаляемые символы
160 'В S$ - исходная строка и результат
200 IF INSTR(1,S$,RMV$)<>0 THEN S$=LEFT$(S$,INSTR(1,S$,RMV$)-1)+
RIGHT$(S$,LEN(S$)-INSTR(1,S$,RMV$)):GOSUB 200
210 RETURN
Сравните:
\\ {{.examples:0441-132.bas|}} \\ [[+tab|wmsxbpge>0441-132.bas]]
NEW
Ok
10 INPUT"Строка для сжатия:";A$
20 FOR I=1 TO LEN(A$):C$=MID$(A$,I,1)
30 IF C$<>" " THEN B$=B$+C$
40 NEXT:PRINT B$
Существует ограничение на число обращений подпрограммы к себе самой. Оно зависит от нескольких факторов, в частности от количества других вложенных одна в другую подпрограмм, выполняемых в этот же момент времени, и от размеров рабочего стека.
Отметим, что понятие рекурсии охватывает также и весьма интересную ситуацию, при которой первая подпрограмма вызывает вторую, а та в свою очередь вызывает первую!
{{anchor:n45}} {{anchor:ongosub}}
===== IV.5. Оператор ON GOSUB =====
Кроме оператора перехода на подпрограмму в [[msx:basic:]] есть оператор выбора подпрограмм (оператор—"переключатель").
Его общий вид:
ON β GOSUB n1,n2,…nk
,
где:
* ''ON''("на"), ''GOSUB''("to GO to SUBroutine" — "идти к подпрограмме") — служебные слова;
* β — арифметическое выражение;
* n1,n2,…,nk — номера начальных строк подпрограмм.
Выполнение оператора ''ON…GOSUB'' начинается с вычисления целой части значения арифметического выражения β, которую мы обозначим p.
Далее, если p ∈ {1,2,…,k}, где k — целое положительное число, то управление переходит к строке программы.
Если p=0 или p>k, то выполняется оператор, следующий за ''ON''. После выполнения соответствующей подпрограммы управление передаётся оператору, расположенному за ''ON GOSUB'' .
Если p<0, то компьютер выдаёт сообщение об ошибке: "Illegal function call".
{{anchor:e045-01}} __//Пример 1//__.
\\ {{.examples:045-01.bas|}} \\ [[+tab|wmsxbpge>045-01.bas]]
NEW
Ok
20 INPUT "Ваш выбор (1-3)";CH
27 ON CH GOSUB 31,32,33:END
31 PRINT "1":RETURN' Попадаем сюда, если CH=1
32 PRINT "2"'RETURN' Попадаем сюда, если CH=2
33 PRINT "3"'RETURN' Попадаем сюда, если CH=3
run
Ваш выбор (1-3)? 2
2
Ok
{{anchor:e045-02}} __//Пример 2//__.
\\ {{.examples:045-02.bas|}} \\ [[+tab|wmsxbpge>045-02.bas]]
NEW
Ok
10 PRINT "Draw,Print,Change,Quit:";:I$=INPUT$(1)
20 ON INSTR(2," DdPpCcQq",I$)/2 GOSUB 100,200,300,400
30 PRINT:GOTO 10
100 PRINT "D":RETURN '──▶
200 PRINT "P":RETURN '──▶
300 PRINT "C":RETURN '──▶
400 PRINT "Q":RETURN '──▶
run
Draw,Print,Change,Quit:█
Draw,Print,Change,Quit: ◀── нажата клавиша "а"
Draw,Print,Change,Quit:Q ◀── нажата клавиша "q"
Draw,Print,Change,Quit:P ◀── нажата клавиша "p"
…
Обратите внимание на примеры из раздела [[003#III.3. Оператор ON GOTO|III.3.]]!
__//Выводы //__ [[bibliography#b8|[8]]]. При применении подпрограмм BASIC придерживайтесь следующих правил:
- Чётко обозначайте начало и конец каждой подпрограммы. Если это возможно, выделяйте их путём отступа операторов вправо.
- Никогда не пользуйтесь оператором ''GOTO'' для входа в подпрограмму и для выхода из неё. Каждую подпрограмму надо рассматривать как независимый логически завершённый //модуль//.
- Имейте под рукой список глобальных входных переменных и выходных результатов каждой подпрограммы. Лучше всего оформить его комментариями к программе с помощью операторов ''REM''.
- Обязательно убедитесь, что в подпрограмме не изменяются значения таких внешних переменных, как счётчики циклов. Если это возможно, в каждой подпрограмме вводите свой принцип именования внутренних переменных.
- В отличии от функций, подпрограммы не появляются в программе до того, как ими будут пользоваться, поэтому полезно группировать подпрограммы вместе после оператора ''END'', указанного в конце основной программы.
{{anchor:n46}}
===== IV.6. Дополнение 1 [77] =====
[[bibliography#b77|[77]]]
{{anchor:e046-01}}
Программа позволяет умножить два числа, причём каждое из них может иметь "//любое//" количество цифр при следующих ограничениях: в произведении должно содержаться не более 765 цифр и длина каждого из чисел должна быть ограничена возможной длиной строки.
В заключение отметим, что приведённая программа работает неправильно либо когда одно из введённых чисел меньше 10, либо когда их произведение меньше 1000. Безусловно имеет смысл сделать программу "защищённой" в этом отношении!
\\ {{.examples:046-01.bas|}} \\ [[+tab|wmsxbpge>046-01.bas]]
5 CLEAR 1000
10 DIM A(255),B(255),C(255)
20 T=1000:Z$="000"
30 PRINT"Точное произведение целых чисел"
40 INPUT"Введите первое число: ";A$
50 LA=LEN(A$):NA=INT((LA-1)/3)
60 FOR I=0 TO NA-1
70 A(I)=VAL(MID$(A$,LA-2-3*I,3))
80 NEXT I
90 A(NA)=VAL(LEFT$(A$,LA-3*NA))
100 INPUT"Введите второе число: ";B$
110 LB=LEN(B$):NB=INT((LB-1)/3)
120 FOR I=0 TO NB-1
130 B(I)=VAL(MID$(B$,LB-2-3*I,3))
140 NEXT I
150 B(NB)=VAL(LEFT$(B$,LB-3*NB))
160 NC=NA+NB+1
170 FOR I=0 TO NC:C(I)=0:NEXT I
180 FOR I=0 TO NA
190 FOR J=0 TO NB
200 K=I+J:X=A(I)*B(J)+C(K)
210 Y=INT(X/T):C(K)=X-T*Y
220 IF Y>0 THEN K=K+1:X=Y+C(K):GOTO 210
230 NEXT J
240 NEXT I
250 PRINT "Произведение = ";
260 IF C(NC)=0 THEN NC=NC-1
270 PRINT C(NC)
280 FOR I=NC-1 TO 0 STEP -1
290 PRINT",";RIGHT$(Z$+STR$(C(I)),3);
300 NEXT I
320 END
run
Точное произведение целых чисел
Введите первое число: 12345678
Введите второе число: 987654321
Произведение = 12,193,262,222,374,638
Оk
print 12345678*987654321
1.2193262222375E+16
Ok
run
Точное произведение целых чисел
Введите первое число: 11111111111111111111
Введите второе число: 6666666666666666666666666666666666666666
Произведение = 74, 74, 74, 74, 74, 74, 73,333,333,333,333,333,333,325,925,925,925,925,925,926
Ok
print 11111111111111111111*6666666666666666666666666666666666666666
7.4074074074074E+58
Ok
{{anchor:n47}}
===== IV.7. Дополнение 2 [90] =====
[[bibliography#b90|[90]]]
{{anchor:e047-01}}
Нахождение факториалов "огромных" чисел ( >1).
\\ {{.examples:047-01.bas|}} \\ [[+tab|wmsxbpge>047-01.bas]]
10 DIM A(100):PRINT"Введите число N":INPUT N:A(1)=1:J=1
60 FOR I=2 TO N
80 P=0
90 FOR K=1 TO J
100 C=A(K)*I+P:P=INT(C/10):A(K)=C-P*10
130 NEXT K
140 IF P=0 GOTO 200
150 J=J+1
160 IF J>100 THEN PRINT"Не хватает места в массиве":STOP
170 A(J)=P-INT(P/10)*10:P=INT(P/10)
190 GOTO 140
200 NEXT I
210 PRINT STR$(N);"!=";
220 FOR K=J TO 1 STEP -1:PRINT RIGHT$(STR$(A(K)),1);:NEXT K
run
Введите число N
? 23
23!=25852016738884976640000
Ok
run
Введите число N
? 50
50!=30414093201713378043612608166064768844377641568960512000000000000
Ok
{{anchor:examples4}}
====== Диск с примерами ======
{{.examples:examples04.dsk|Загрузить образ диска}}
[[+tab|wmsxbpged>examples04.dsk|Открыть диск в WebMSX]]
----
[<>]
{{tag>MSX msxbdpl}}