III.3. ОПЕРАТОР ON GOTO Общая форма записи оператора-переключателя (оператора выбора по целому значению) следующая: ON β GOTO N1,N2,...,Nk где: ON ("на"), GOTO ("идти к") - служебные слова; β - арифметическое выражение, целая часть значения которого должна принадлежать отрезку [0,255]; 3) N1,N2,...,Nk - номера программных строк; оператор ON GOTO воспри- нимает столько номеров строк в списке,сколько Вы сможете записать в одной логической строке (помните, что она ограничена 255 символами!). Выполнение оператора ON начинается с вычисления целой части значения арифметического выражения β, которое мы обозначим p. Далее, если p∈{1,2,...,k}, k - натуральное число, то управление перехо- дит к строке Np. Если p=0 или p>k, то выполняется оператор, следующий за ON. Если же p<0, то последует сообщение об ошибке "Illegal function call" ("Н е п р а в и л ь н ы й в ы з о в ф у н к ц и и"). П р и м е р ы: ───────────── 1) NEW Ok 10 INPUT X 20 ON X GOTO 200,300,400:? X:GOTO 10 200 PRINT X+200:END 300 PRINT X+300:END 400 PRINT X+400:END run run ? 0 ? 2 0 302 ? 3 Ok 403 run Ok ? 1 run 201 ? -1 Ok Illegal function call in 20 Ok Оператор ON GOTO удобен, когда выполнение одной или нескольких различ- ных ветвей алгоритма определяется значением переменной или выражения (так называемый выбор п о з н а ч е н и ю). Если эти ветви к о р о т к и е, то смело используйте оператор ON GOTO.Если же ветви имеют достаточно слож- ную логику и могут быть оформлены как п о д п р о г р а м м ы ,то для ре- ализации выбора по значению используйте оператор ON GOSUB (см. раздел IV.5). 2) NEW Ok 5 'Анализ символов, вводимых с клавиатуры 10 ON ASC(INPUT$(1))-26 GOTO 101,102,103 101 GOTO 10 102 PRINT "Нажата клавиша ──▶":END 103 PRINT "Нажата клавиша ◀──":END run ┌─────┐ Нажата клавиша ──▶ Нажмите клавишу │ ──▶ │ ! Ok └─────┘ run ┌─────┐ Нажата клавиша ◀── Нажмите клавишу │ ◀── │ ! Ok └─────┘ run ┌─────┐ Illegal function call in 10 Нажмите клавишу │ TAB │ ! Ok └─────┘ О строковой функции INPUT$() см. в разделе VII.1.1 . 3) Оператор ON A GOTO 110,110,110 позволяет совершить переход к программной строке 110, если значение пере- менной А находится в полуинтервале [1,4). 4) Пусть в программе требуется выполнить три различные операции в зави- симости от значения переменной K, которое может быть меньше, больше или равно нулю. Приведем фрагмент программы: 110 ON SGN(K)+1 GOTO 170,210 120 'Выполнение операции при K<0 · · · 170 'Выполнение операции при K=0 · · · 210 'Выполнение операции при K>0 · · · 5) Пусть в программе требуется совершить переход к различным строкам, если значение переменной A находится в следующих диапазонах: 0≤A< 800 - переход к строке 900; 800≤A<1600 - переход к строке 800; 1600≤A<2400 - переход к строке 700. Если же A≥2400 , то должен выполняться следующий оператор. В этом слу- чае программная строка с оператором ON будет иметь вид: 50 ON A/800+1 GOTO 900,800,700 Заметим, что оператор ON GOTO потенциально опасен, особенно для начи- нающих, так как его неаккуратное использование может привести к запутан- ной, трудной для понимания программе. В заключение проведем аналогию между оператором ON GOTO и командой в ы- б о р в сокращенном варианте выбор ───── │ при условие 1: серия 1 │ ─── │ при условие 2: серия 2 │ ─── │ ... │ при условие N: серия N │ ─── все ─── в школьном алгоритмическом языке. Говорят, что команда выбора реализует "выбор п о у с л о в и ю". Выбор по условию введен в школьный алгоритми- ческий язык из языка программирования Рапира. В большинстве языков высоко- го уровня (в том числе и в MSX-BASIC!) этой конструкции в чистом виде нет, но ее можно моделировать следующей последовательностью вложенных команд ветвления: если условие 1 ──── │ то серия 1 │ ── │ иначе если условие 2 │ ───── ──── │ │ то серия 2 │ │ ── │ │ иначе ... │ │ ───── │ │ если условие N │ │ ──── │ │ │ то серия N │ │ │ ── │ │ все │ │ ─── │ │ ... │ все │ ─── все ─── Зато в версии MSX-BASIC есть оператор ON GOTO - другая форма выбора (по з н а ч е н и ю выражения). Ясно, что, вообще говоря, механизмы работы выбора по условию и по зна- чению совпадают. Отличие лишь в форме условия: в выборе по условию оно мо- жет быть любым,а в выборе по значению условие определяется значением ариф- метического выражения. III.4. ПРОГРАММИРОВАНИЕ ЦИКЛОВ Три месяца каждую вторую среду он ходил смотреть фильм "Девушка моей мечты", но на связь с ним никто не вышел. В.Кожевников Понятие цикла является одним из основополагающих понятий информатики. Считается,что если бы в программах не было возможности организовывать цик- лы, то применять ЭВМ для решения многих задач не имело бы никакого смысла. Ц и к л - это многократное повторение одной и той же группы операторов, возможно, с новыми значениями переменных, входящих в эту группу. Типичный пример цикла мы видим в "Сказке о рыбаке и рыбке" А.С.Пушкина. Действия слабохарактерного старика повторялись ц и к л и ч е с к и : разговор с золотой рыбкой ──▶ путь от моря к старухе ──▶ получение нового приказания от старухи и снова ──▶ к морю, к золотой рыбке. Параметром цикла (в терминах языка BASIC) здесь выступает алчность ста- рухи, которая увеличивается при каждом новом прохождении циклического участка. Программы, содержащие циклы, называются ц и к л и ч е с к и м и . Цикл, не содержащий внутри себя других циклов,называется п р о с т ы м. В противном случае его называют к р а т н ы м или в л о ж е н н ы м. В школьном алгоритмическом языке для записи циклических алгоритмов при- меняются две конструкции: цикл "для" и цикл "пока". ─── ──── Рассмотрим, как на языке программирования MSX-BASIC программируется ц и к л "д л я". ───── Для этого существуют два специальных оператора: оператор начала цикла (оператор FOR),оператор конца цикла (оператор NEXT). Операторы FOR и NEXT, как правило(!), используются совместно. Оператор начала цикла (его называют еще з а г о л о в к о м цикла) имеет вид: FOR i = α TO β [ STEP γ ] Здесь: FOR ("для"), TO ("до"), STEP ("шаг"), NEXT ("следующий") - служеб- ные слова; i - имя числовой переменной (без индекса!); α,β,γ - арифметические выражения. Вслед за оператором н а ч а л а цикла располагают операторы программы, которые должны выполняться циклически(эти операторы образуют т е л о цик- ла). За последним оператором тела цикла размещается оператор к о н ц а цикла, который имеет следующий синтаксис: NEXT [i] Обратите внимание на то, что переменная, указываемая после NEXT,должна быть той же переменной,которая упоминается в операторе начала цикла после служебного слова FOR (эту переменную называют п а р а м е т р о м цикла). Значения арифметических переменных α и β задают соответственно началь- ное и конечное значения параметру цикла. Значение арифметического выраже- ния γ - это ш а г ,на величину которого изменяется параметр цикла после каждого повторения тела цикла. Шаг может быть как положительным, так и от- рицательным. Если шаг цикла равен +1, указание STEP γ в операторе начала цикла мо- жет опускаться (говорят, что "по умолчанию шаг цикла равен 1"). Итак, в программе цикл "для" записывается следующим образом: ─── ┌───────────────────────────────────────-┐ │ FOR i = α TO β [ STEP γ ] │ │ т е л о ц и к л а │ │ NEXT i │ └───────────────────────────────────────-┘ Выполнение этого фрагмента программы происходит следующим образом: па- раметру цикла i присваивается его начальное значение (значение арифметиче- ского выражения α), и один раз выполняется тело цикла (т.е. группа опера- торов, размещенных между заголовком цикла и соответствующим оператором NEXT). Вслед за этим оператор NEXT изменяет значение параметра цикла i на величину шага (значение арифметического выражения γ) и проверяет, не выш- ло ли новое значение параметра цикла из рабочего интервала (если шаг поло- жителен,то не стало ли оно больше конечного значения параметра цикла (зна- чения арифметического выражения β); если шаг отрицателен - не стало ли ме- ньше конечного значения параметра цикла). Если этого не происходит, то осуществляется повторное выполнение тела цикла, в противном случае - в ы х о д из цикла, то есть переход к опера- тору, следующему за NEXT. Заметим, что при л ю б ы х значениях α,β,γ тело цикла будет выполне- но х о т я бы о д и н раз (в отличие от школьного алгоритмического языка и других языков программирования!)! П р и м е р: NEW ─────────── Ok 10 FOR A=1 TO 10 STEP-1 20 PRINT A 30 NEXT A run 1 Ok Эта программа выведет на экран только начальное значение параметра цик- ла A=1. При первой же встрече с оператором NEXT A будет обнаружено,что ко- нечное значение параметра цикла (A=10) уже "позади"! Еще раз обращаем Ваше внимание на то, что: проверка на окончание цикла производится п о с л е выполнения тела цикла; изменение параметра цикла происходит п е р е д проверкой на оконча- ние цикла. Таким образом, следующие фрагменты эквивалентны: ··· ··· 10 FOR K=1 TO 4 STEP 2 10 K=1 ··· тело цикла ··· 20 'Пустой оператор 100 NEXT K ··· тело цикла ··· ··· 100 K=K+2 110 IF K<=4 GOTO 20 ··· Отметим, что оператор цикла FOR...NEXT можно располагать на одной про- граммной строке. Это целесообразно делать, если тело цикла состоит из од- ного-двух операторов. Например: 10 FOR L=1 TO 10:PRINT L;L^3:NEXT L Выполнив эту строку, компьютер выведет на экран дисплея таблицу кубов чисел 1,2,3,...,10. Кстати, сравните конструкцию FOR...NEXT с циклом "для" в школьном ал- горитмическом языке: ─── для x от x до x шаг x ─── ── min ── max ─── шаг нц │ ── серия команд │ кц ── Напомним, что в том месте дисплейной строки,где надо обратить Ваше вни- мание на наличие символа пробел (" "), мы будем использовать в тексте сим- вол "·". В а ж н о е з а м е ч а н и е ! Значения переменных α,β,γ можно изменять операторами, находящимися в теле цикла! Следующие две программы идентичны: NEW NEW Ok Ok 10 A=7:B=9:H=1 5 A=7:B=9:H=1:A1=A:B1=B:H1=H 20 FOR I=A TO B STEP H 10 FOR I=A1 TO B1 STEP H1 30 A=4:B=5:H=2 20 A=4:B=5:H=2 40 ? A;B;H:NEXT 25 ? A;B;H:NEXT run run ·4··5··2 ·4··5··2 ·4··5··2 ·4··5··2 ·4··5··2 ·4··5··2 Ok Ok Таким образом, текущие значения переменных α,β,γ(в теле цикла) могут не совпадать с начальным, конечным значениями параметра цикла и значением шага соответственно! В то же время, з н а ч е н и я α, β и γ,указанные в заголовке цикла, "недоступны" для операторов тела цикла при условии, что в теле цикла нет обращения к заголовку цикла. NEW Ok 10 INPUT A,B,H 20 FOR I=A TO B STEP H:?"I=";I 30 A=A+2:IF A<6 GOTO 20 ELSE ?A 40 NEXT:?I run ? 2,3,1 I= 2 I= 4 6 5 Ok Следовательно, изменить начальное и конечное значения параметра цикла, а также значение шага можно только(!)с помощью обращения к заголовку цикла. Таким образом, операторы, находящиеся в теле цикла могут управлять чис- лом повторений цикла! Значение параметра цикла i также м о ж н о изменять операторами, на- ходящимися в теле цикла! По окончании работы цикла значение параметра цикла i равно первому его значению, для которого выполнилось неравенство sign([β]-i)·[γ]<0, (∗) где обозначено: 1) [β] и [γ] - значения арифметических выражений β и γ соответственно; 2) функция sign(x)("signum"-"знак") определяется формулой: 1, если x>0 sgn(x)= { 0, если x=0 -1, если x<0. Условие (∗) принято вместо условия ([β]-i)·[γ]<0 потому, что при малых ([β]-i) и [γ] произведение ([β]-i)·[γ] может дать "машинный нуль",хотя будет выполнено неравенство (∗)! Кстати,число повторений цикла "без фокусов" можно вычислить по формуле: K=INT(ABS(([β]-[α])/[γ]))+1 Отметим, что во многих других языках программирования (ALGOL, FORTRAN) по окончании цикла значение параметра i становится н е о п р е д е л е н- н ы м . В этом случае им можно "пользоваться" только после присвоения но- вого значения. П р и м е р ы: ───────────── 1) NEW 2) NEW Ok Ok 10 INPUT A,B,H 10 INPUT A,B,H 20 FOR I=A TO B STEP H 20 FOR I=A TO B STEP H 40 ?I;:I=I+1:?I 30 H=H+1:I=I+H:?H;I 50 NEXT 40 NEXT run run ? 1,3,1 ? 1,4,1 ·1··2 ·2··3 ·3··4 ·3··7 Ok Ok 3) NEW 4) NEW Ok Ok 10 INPUT A,B,H 10 INPUT A,B,H 20 FOR I=A TO B STEP H 20 FOR I=A TO B STEP H 30 NEXT I:PRINT I 30 I=I^2:NEXT:?I run run ? 4,5,2 ? 5,1,1 6 26 Ok Ok Тело цикла по отношению к остальной части программы замкнуто, то есть н е л ь з я "входить" в него, минуя заголовок цикла. Возможен досрочный выход из цикла не через его окончание, а с использо- ванием операторов IF...THEN...ELSE... и GOTO (в этом случае говорят, что цикл выполняется "n+1/2" раз!). При этом значение параметра цикла о п р е- д е л е н о и равно тому значению, при котором произошел выход из цикла. П р и м е р. Найти индекс первого положительного элемента одномерного ─────────── числового массива А(N). NEW Ok 10 DEFINT N,I:INPUT "Введите N";N 20 DIM A(N)'Описание массива А 30 FOR I=1 TO N:INPUT A(I):NEXTI'Ввод массива A 40 FOR I=1 TO N 50 IF A(I)>0 THEN ?"Искомый индекс:";I:GOTO 60 ELSE NEXTI 60 PRINT"We are out of the loop!" 'Мы вышли из цикла! run Введите N? 4 ? -1 ? -3 ? 5 ? -4 Искомый индекс: 3 We are out of the loop! Ok Oтметим, что оператор GOTO 60 в 50 строке можно заменить на последова- тельность операторов: I=N:NEXT I (мы пользуемся тем, что параметр цикла I можно менять внутри цикла!), ко- торая обеспечит в случае A(I)>0 выход из цикла. Обратите внимание на то, что можно даже выйти из тела цикла в "окружающую" его часть программы, а затем вернуться снова в тело цикла, м и н у я заголовок! П р и м е р. NEW ─────────── Ok 10 FOR I=1 TO 5 20 IF I<=2 THEN GOTO 100 30 M=I^2:?M 40 NEXT I:END 100 I=I+3:GOTO 30 'Возвращение в тело цикла! run 16 25 Ok Приведенный пример показывает, как, используя оператор GOTO,можно "рас- ширить" тело цикла, включив в него произвольные части программы. П р и м е р ы п р о с т ы х ц и к л о в 5% текста программы занимают 90% времени ее выполнения. Аксиома программирования 1) 10 FOR I=1 TO 1000:NEXT 'Задержка ≈ 2.35 секунды Применение оператора цикла с "пустым" телом позволяет получить эффект з а д е р ж к и по в р е м е н и. Запомните этот трюк! 2) Ok 10 'Признание в любви! 20 INPUT"Введите целое число N";N% 30 FOR I%=1 TO N% 40 PRINT"I love YOU!" 50 NEXT I% Думаем, что результат работы этой N-"любвиобильной" программы Вас пора- дует. Заметим, что программа идентична алгоритму с использованием команды повторения n раз в школьном алгоритмическом языке [13]: ─── нц число повторений раз ── серия команд ─── │ кц ── так как тело цикла не зависит от параметра цикла. 3) Ok 10 DEFINT N,I:INPUT "Введите N";N 20 DIM A(N) 'Массив А описан! 30 FOR I=0 TO N:INPUT A(I):NEXT I 40'Данный фрагмент позволяет осуществить ввод элементов одномерн ого массива А (в массиве N+1 элемент!) Приведем еще один способ решения указанной проблемы: Ok 10 DIM A(5) 'Массив A содержит 6 элементов! 20 DATA 1.,2.,3.,4.,5.,6. 30 FOR I=0 TO 5:READ A(I):NEXTI 4) и н и ц и а л и з а ц и я массива (присвоение начальных значений) целыми псевдослучайными числами, принадлежащими отрезку [A,B]: NEW Ok 10 DEFINT N,I,C:INPUT A,B,N:DIM C(N) 20 X=RND(-TIME) 'начальная установка генератора псевдослучайных чисел 30 FOR I=1 TO N 40 C(I)=INT((B-A+1)*RND(1)+A):?C(I);NEXTI run ? -5,3,12 -1·-3··0·-1··0·-1·-3··3··2·-5··0··1 Ok З а м е ч а н и е. Как можно чаще применяйте указанный способ инициали- зации массива псевдослучайными числами при т е с т и р о в а н и и (про- цессе обнаружении ошибок) Ваших программ! 5) NEW Ok 10 'Табулирование функции y=exp(sin(x)) на [A,B] с шагом H. 100 INPUT A,B,H 110 FOR X=A TO B STEP H 120 ?USING"#.### #.###";X;EXP(SIN(X)) 130 NEXT run ? .0,1.,.2 0.000 1.000 0.600 1.759 0.200 1.220 0.800 2.049 0.400 1.476 1.000 2.320 Ok 6) составить программу табулирования функции y=sinx на отрезке [a,b], причем на первой половине отрезка - с шагом h,a на второй половине отрез- ка - с шагом h/2. Приведенные ниже программы эквивалентны и дают полное представление о работе оператора FOR...NEXT . a) NEW b) NEW Ok Ok 10 INPUT A,B,H:P=0 10 INPUT A,B,H:P=0'Р-флажок! 20 FOR X=A TO B STEP H 20 A1=A:B1=B:H1=H 30 IF X>=(A+B)/2ANDP=0 25 X=A1 THEN P=1:A=(A+B)/2:H=H 30 IF X>=(A+B)/2ANDP=0 THEN /2:GOTO 20 P=1:A=(A+B)/2:H=H/2:GOTO 20 35 PRINT X;SIN(X) 35 PRINT X;SIN(X) 40 NEXT X 40 X=X+H1:IF X<=B1 GOTO 30 run c) NEW ? 0,1,.1 Ok 0 0 10 INPUT A,B,H:P=0:X=A .1 .099833416646831 20 IF X>=(A+B)/2 THEN FOR X=(A .2 .19866933079507 +B)/2+H/2 TO B STEP H/2:P_1 EL ··· ··· SE FOR X=A TO (A+B)/2 STEP H .95 .81341550478941 30 PRINT X;SIN(X) 1 .84147098480792 40 NEXT:IF P=0 THEN GOTO 20 Ok 7) пусть требуется вычислить значение функции y=x² для x=0, 0.1, 0.2, ..., 1., кроме x=0.3. Предостережем от н е в е р н о г о (в принципе!) решения: 10 FOR X=0 TO 1 STEP 0.1 20 IF X><0.3 THEN ?X;X^2:NEXT X ELSE NEXT X Значение шага цикла (0.1) есть десятичная константа с фиксированной то- чкой. Поэтому, вследствие накопления погрешности округления, мало надежды (хотя она и есть!) на то, что на четвертом цикле значение X будет точно равно 0.3. Да и на одиннадцатом цикле значение X может оказаться "чуточку" большим (меньшим) единицы! Поэтому верное решение будет, например, таким: 10 FOR Y%=0 TO 10 20 IF Y%><3 THEN X=Y%/10:?X;X^2:NEXT X ELSE NEXT X 8) NEW Ok 5 'Вычисление значения многочлена A(0)+A(1)·x+A(2)·x²+···+A(n)· xⁿ в точке x=X0 по схеме Горнера 10 INPUT"Введите степень многочлена n=";N:INPUT "X0=";X 20 FOR I=0 TO N:PRINT"A("I")=";:INPUT A(I):NEXTI 36 C=A(N) 40 FOR I=N-1 TO 0 STEP -1:C=C*X+A(I):NEXTI:PRINT "c=";C 9) последовательность чисел Фибоначчи F(1), F(2),..., F(n),... определяется по следующим рекуррентным формулам: F(1)=1, F(2)=1, F(n+2)=F(n)+F(n+1), n=1,2,... . Напишите программу, вычисляющую F(N) по заданному N. NEW Ok 10 DEFINT N,I:INPUT N:A=1:B=1 20 IF N=1ORN=2 THEN C=1:? C:END 30 FOR I=1 TO N-2:C=A+B:A=B:B=C:NEXT 35 PRINT C:END run run run ? 5 ? 10 ? 100 5 55 3.5422484817927E+20 Ok Ok Ok Известно, что для членов последовательности Фибоначчи имеет место кра- сивая формула: 1┌ 1+√5 ⁿ 1+√5 ⁿ┐ F(n)= ──│(─────) ─ (─────) │ , n=1,2,... √5└ 2 2 ┘ Используем ее для написания второго варианта предыдущей программы: NEW Ok 10 FOR N=1 TO 63 20 A=FIX((((1+SQR(5))/2)^N-((1-SQR(5))/2)^N)/SQR(5)+0.5) 30 PRINT"n=";N;"A=";A 40 NEXT run n= 1 A= 1 ··· n= 63 A= 6557470319842 Ok Как уже говорилось, тело цикла может содержать второй цикл - в н у т- р е н н и й по отношению к первому, в н е ш н е м у циклу ("бумажные стаканчики"). Внутренний цикл должен целиком содержаться в теле внешнего цикла. Таким образом, оператор NEXT для внутреннего цикла должен стоять перед оператором NEXT внешнего цикла. Нарушение этого правила приводит к появлению на экране сообщения об ошибке: "NEXT without FOR in ..." ("О п е р а т о р NEXT б е з о п е р а т о р а FOR в с т р о к е ..."). П р и м е р. NEW ─────────── Ok 10 FOR I=1 TO 3:FOR J=2 TO 4 20 IF J>1 THEN NEXT I 30 NEXT J run NEXT without FOR in 30 Ok Тело внутреннего цикла в свою очередь может содержать еще один цикл, внутренний по отношению к нему и так далее. Интерпретатор MSX-BASICa уста- навливает максимальную "глубину" (число) вложений циклов, однако она на- столько велика, что превысить ее практически невозможно! Учтите, что каждый оператор FOR...NEXT занимает при работе 25 байтов с т е к а , причем стек освобождается только при завершении цикла, после последнего выполнения оператора NEXT! Тем не менее, если программа использует слишком много цикловодновре- менно, может возникнуть сообщение об ошибке: "Out of memory" ("В ы х о д з а п р е д е л ы п а м я т и"). Если требуется, чтобы несколько вложенных операторов FOR заканчива- лись в одном месте, можно использовать один оператор NEXT,содержащий спи- сок параметров циклов, разделенных запятыми: первое имя в списке соответ- ствует самому внутреннему, последнее - самому внешнему циклу.