Языки программирования: Мега-Бейсик, Бета-Бейсик, Бета-Бейсик, Лазер-Бейсик, ZX-Форт, Паскаль HP4TM 1993 г.

BETA-BASIC - процедуры.


3. ПРОЦЕДУРЫ

Список используемых ключевых слов: DEF PROC, END PROC, LOCAL,
DEFAULT, REF, READ LINE, LIST PROC и функция ITEM().

В этой главе мы кратко рассмотрим вопросы, связанные с при-
менением процедур в БЕТА-БЕЙСИКе. Более подробную информацию Вы
сможете получить далее, прочитав разделы относящиеся к каждому из
указанных ключевых слов.

Использование процедур - весьма желанный прием для тех, кто
программирует на БЕЙСИКе. Современные версии БЕЙСИКа в той или иной
мере используют эту возможность и программисты встречают их с
большим энтузиазмом. И, если Вы ими не пользуетесь, то наверное
стоит пересмотреть свои взгляды, причем вовсе не потому, что это
модно или потому, что это все рекомендуют, а просто так удобнее и
легче писать и отлаживать программы.

БЕТА-БЕЙСИК 3.0 имеет одну из самых совершенных систем исполь-
зования процедур среди других языков для домашних персональных
компьютеров. Именно здесь Вы получаете наибольшую гибкость и
эффективность программирования.

Основное преимущество использования процедур состоит в струк-
турном программировании. Эта концепция предполагает, что Вы можете
создать некоторый программный модуль, способный выполнять опре-
деленную работу и не оказывать никаких нежелательных побочных
эффектов на остальную часть программы. Он не должен, например,
изменять никакие переменные в программе, кроме тех, которые по-
ложено. После того, как этот блок программы Вами написан и тщатель-
но отлажен, Вы можете забыть о том, как он работает и из чего
состоит. Когда Вы пишете новую программу и он Вам нужен, Вы просто
цришьете его к тексту командой MERGE "", а используете - вызвав его
по имени. Каждая процедура должна быть достаточно простой, чтобы
быть вполне понятной. Если она у Вас получается громоздкой, сложной
и непонятной, то стоит подумать о том, чтобы разделить ее на
несколько логических частей и выразить каждую из этих частей своей
процедурой. Процедура - это часть программы, имеющая свое имя и
начинающаяся с оператора DEF PROC, после которого задается ее имя и
имена тех переменных, с которыми она должна работать, а

заканчивающаяся оператором END PROC.

Давайте рассмотрим простой, хотя и вполне бесполезный пример:
100 DEF PROC greet
110 PRINT "Hello"
120 END PROC

Попробуйте дать команду RUN и Вы увидите, что ничего не прои-
зойдет. Хоть процедура и часть программы, но работать в таком виде
она не будет. Здесь мы записали только определение процедуры, то
есть указали, что она должна делать. Выполнение процедуры
происходит только после вызова ее по имени. Наша процедура имеет имя
"greet". Попробуйте добавить строку: 10 greet.

Теперь, если Вы дадите команду RUN, в строке 10 будет запуще-
на эта процедура и, в соответствии с ее определением, будет на-
печатано на экране слово 'Hello'.

Все происходит точно так же, как в стандартном БЕЙСИКЕ с
функциями. Там ведь тоже DEF FN игнорируется до тех пор, пока
программа не встретит вызов функции оператором FN.

Вы можете столкнуться с проблемой того, как набрать слово greet
в строке 10, ведь курсор находится в режиме "К". Выше мы об этом
упоминали:

Вы можете нажать пробел и далее набирать greet по буквам;
Вы можете дать команду KEYWORDS 4 и перейти в режим ввода
всех ключевых слов по буквам;

Вы можете использовать ключевое слово PROC и записать стро-
ку в виде:

10 PROC greet
Это совершенно то же самое, что и просто
10 greet

Использование ключевого слова PROC в данном случае просто дань
привычке тех программистов, которые привыкли к такой записи по более
ранним версиям БЕТА-БЕЙСИКа, хотя реально в нем больше нет никакой
необходимости.

Далее мы будем говорить об использовании имени процедуры, как о
вызове процедуры. Имя процедуры обязательно должно начинаться с
буквы. Заканчивается имя процедуры пробелом, двоеточием, нажатием
ENTER, операторами REF или DATA. Остальные символы, как правило,
могут быть использованы в имени процедуры, но желательно, чтобы Вы
привыкли использовать только буквы, цифры и символы подчеркивания
"_". Не имеет значения, какие буквы применяются строчные или
прописные.

Определение процедуры начинается с DEF PROC и может быть рас-
положено где угодно в программе. Его вполне можно располагать и до и
после первого вызова процедуры. Важно только, чтобы оператор DEF
PROC был первым оператором в строке. Определение процедуры может
иметь сколько угодно строк и должно заканчиваться оператором END
PROC.

Если Вы используете с одним DEF PROC несколько END PROC, то
компьютер будет не в состоянии правильно "перепрыгнуть" через
определение процедуры во время работы программы. В этом случае Вам
придется самостоятельно позаботиться, чтобы он смог это сделать или
размещать все лишние END PROC после оператора STOP. Изменим
приведенный выше пример:

100 DEF PROC greet times
110 FOR n=l TO times
120 PRINT "HELLO"

130 NEXT n
140 END PROC

В данном случае times - это параметр процедуры. Он называется
формальным параметром, поскольку не имеет пока никакого числового

значения, а только указывает, как он используется при расчете
процедуры. Раз он указан в определении процедуры, то ,он должен быть
задан и при вызове процедуры. Теперь, когда Вам надо будет исполнить
процедуру greet, Вы зададите параметр times и заданное Вами значение
называется фактическим параметром - это число, которое фактически
будет подставлено на место формального times при расчете процедуры.

Итак, формальные параметры - это просто имена, а фактические
параметры - это числа, выражения или переменные (кроме тех случа-
ев, когда используется REF, о чем см. ниже).

Теперь, если Вы обратитесь к процедуре по имени, без задания
параметра, например 10 greet , то получите сообщение об ошибке
"Variable not found" (переменная не найдена).
Если же Вы обратитесь к процедуре:
10 greet 5

Вы увидите, что слово Hello будет пять раз напечатано на экране,
т.к. фактический параметр "5" встал на место формального times.

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

Дело в том, что, как мы говорили, процедуры должны исполнять
то, что записано в их определении и не должны оказывать нежелатель-
ных побочных воздействий на остальную часть программы. Рассмотрим
нашу процедуру. Она манипулирует с двумя переменными - times и п.
Давайте теперь проверим, чему они равны после того, как процедура
отработала. Дайте прямые команды:
PRINT times
PRINT n

Вы обнаружите, что times - не существует, an - существует и
имеет числовое значение.

Почему не существует times? Дело в том, что эта переменная
введена нами в строке DEF PR0C, что автоматически сделало ее ло-
кальной, то есть определенной только внутри процедуры. Когда про-
цедура кончила работать, она удаляется из памяти и ее больше нет. И
это хорошо, ведь если у Вас где-то еще в программе используется
какая-нибудь переменная times, то ее значение могло бы быть изменено
(испорчено), а теперь Вам можно об этом не думать.

А если у Вас до вызова процедуры уже была какая-то переменная
times, то она тоже будет удалена? Нет, в момент вызова она будет
запомнена, как глобальная переменная, а в процедуре будет создана
локальная times. После работы процедуры локальная будет удалена, а
глобальная - восстановлена.

Хуже обстоит дело с переменной п, - она осталась существовать
после работы процедуры и является глобальной, ведь она не входила в
оператор DEF PR0C. Ее значение может незаметно для Вас быть
использовано где-то еще и привести к неприятным ошибкам. Для того,
чтобы это не происходило, ее можно объявить локальной переменной
внутри процедуры в принудительном порядке. Для этого служи? оператор
LOCAL. Добавьте к нашей программе строку:
105 LOCAL п ,

а теперь добавьте к программе следующие строки, которые позволят Вам
убедиться, что после работы процедуры переменные times и п остались
ненарушены:

10 LET п=12 34
20 LET times=5678
30 greet 10
40 PRINT n,times

Процедура "greet" имела скорее учебное, чем практическое назна-
чение, а вот более полезная процедура:

100 DEF PROC box x,у,width,height
130 PLOT x,y: DRAW width,0
140 DRAW 0,-height: DRAW -width,0
150 DRAW 0,height
160 END PROC

Процедура "box" предназначена для рисования прямоугольника и
имеет четыре параметра: х,у - координаты левого верхнего угла,
width - желаемая ширина, a height - высота. Так, команда

box 100,100,10,40
изобразит вблизи центра экрана прямоугольник, вытянутый по вер-
тикали .

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

box 100,100,50

Получим сообщение об ошибке, т.к. процедура имеет четыре фор-
мальных параметра, а мы им на смену подставили только три фак-
тических. Это, однако, тоже можно предусмотреть и предотвратить, для
чего служит оператор DEFAULT (по английски default - принятый "по
умолчанию"). Запишем строку:
120 DEFAULT height=width

Эта строка буквально означает следующее: "Если параметр height
не задан, то считать, что он равен параметру width".

Ну, а если Вы уж совсем ленивы, то можете не указывать и ши-
рину квадрата, введя строку:

110 DEFAULT with = 20
и у Вас и ширина и высота будут равны 20 пикселам.

Используя запятые, можно опускать не только последние
параметры, но и вообще любые,
box ,100,40,10

В этом вызове опущен параметр х. Разумеется, где-то в описании
процедуры должен присутствовать оператор

DEFAULT х = ... .

ПЕРЕДАЧА ПАРАМЕТРОВ В ВИДЕ ССЫЛКИ

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

Но у нас может возникнуть необходимость не только передавать
что-то в процедуру, но и, например, получать что-то от нее. Можно,
конечно, написать процедуру, которая будет что-то рассчитывать, а
результат расчета присваивать глобальной переменной х, из которой
можно этот результат узнать после окончания работы процедуры и
возврата в главную программу. Но это нарушит нашу договоренность о
том, что процедура должна быть независимым модулем и не должна
оказывать нежелательного влияния на другие участки программы (и на
другие процедуры тоже). А если мы поступим таким образом с
переменной х, то теперь должны будем все время помнить о том, что
эту букву уже нигде нельзя использовать для иных целей, т.к. можно
потерять то, что в этой переменной содержится. Желательно было бы
уже при вызове процедуры и указании фактических параметров задать
какую-то переменную, в которую надо поместить результат работы
процедуры. Данная версия БЕЙСИКа, в отличие от многих аналогов,
позволяет делать и это.

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

Это называется передачей параметра, как ссылки, и делается
добавлением оператора REF перед именем этой переменной в операторе
DEF PROC. REF - это сокращение от английского слова reference
(ссылка). Таким образом, переменная, которую Вы ставите при вызове
процедуры в качестве фактического параметра, переименовывается в то
имя, перед которым стоит REF, вычисляется в процедуре и возвращается
под первоначальным именем в вызывающую программу или процедуру. В
качестве демонстрации приведем процедуру SWOP, которая обменивает
между собой содержимое двух строковых переменных.
200 DEF PROC swop REF а$, REF b$
210 LOCAL *t$
220 LET t$=a$: LETa$=b$:

LET b$=t$
2 30 END PROC
А вызывается она, например, так:

10 LET х$= "hi": LET y$="goodbye"
20 SWOP x$,y$
30 PRINT x$,y$

Без указания REF в определении функции и а$ и b$ тоже будут об-
мениваться своим содержимым в процедуре SWOP, но только именно
внутри нее; глобального эффекта не будет, т.к. локальные а$ и Ь$ при
выходе из процедуры будут утрачены. С использованием же REF эти
переменные являются как бы временными именами, на которые ссылаются
глобальные х$ и у$, поэтому изменения, которым подверглись а$ и Ь$ в
теле процедуры, отражаются и на х$ и на у$.

Языки программирования для микрокомпьютеров, использующие
концепции процедур, в большинстве случаев не позволяют использовать
массивы в качестве параметров. БЕТА-БЕЙСИК разрешает это, но правда,
требует, чтобы они передавались только как ссылки. Они
переименовываются вместо того, чтобы просто копироваться в область
локальных переменных процедуры. Этим достигается экономия памяти,
ведь компьютеру не надо одновременно хранить один массив два раза.
Если же Вам на самом деле нужна локальная копия Вашего массива для
каких-то временных манипуляций с ним, Вы можете внутри процедуры
создать параллельный массив, объявив его как LOCAL, а затем
скопировать в него содержание Вашего исходного массива,
воспользовавшись для этого командой БЕТА-БЕЙСИКа COPY, о чем мы еще
скажем ниже, когда будем ее рассматривать вместе с командой JOIN.
Вот демонстрационный пример, в котором показано, как можно найти
сумму элементов массива.

300 DEF PROC total REF а(),

REF sum
310 LOCAL n
320 LET sum=0

330 FOR n=l TO LENGTH (l,"a()")
340 LET sum = sum + a()

350 NEXT n
360 END PROC

За именем массива должны идти скобки, чтобы интерпретатор отли-
чал имена массивов от обычных переменных с тем же именем. Перед
"sum" стоит оператор REF, так что по окончании работы содержимое
"sum" будет передано глобальной переменной. Функция LENGTH (), о
которой мы еще будем говорить, определяет размер массива для того,
чтобы процедура могла работать с массивами любой длины.

Теперь зададим сам массив, чтобы убедиться, что наша проце-
nvoa юаботает нормально:

130 NEXT n
и добавим вызов нашей процедуры:
140 total t(),answer
150 PRINT answer
в итоге получим 55.

ПЕРЕДАЧА ПАРАМЕТРОВ СПИСКОМ

Возможны варианты, когда Вам вместо того, чтобы определять
комбинацию параметров для процедуры, удобнее иметь дело со списком
этих параметров, причем список может быть неопределенной длины.
Чтобы это было возможным, в БЕТА-БЕЙСИКе 3.0 есть специальные
средства.

Если в операторе DEF PROC использовать оператор DATA вместо
обычного перечисления формальных параметров, то соответствующий ему
оператор READ примет список фактических параметров, стоящих в вызо-
ве Вашей процедуры. Чтобы эта возможность была по настоящему удоб-
ной, необходимо, чтобы можно было определить в процедуре есть ли
еще параметры в списке, которые она не приняла. Для этого существу-
ет функция ITEM (). Она возвращает 0, если список исчерпан пол-
ностью, 1 - если в списке есть непереданные параметры и следующий
параметр - число, 2 - если следующий параметр - строковая перемен-
ная.

Приведем пример, который показывает, как можно организовать
процедуру, которая просуммирует несколько заданных Вами чисел.
100 DEF PROC sum DATA
110 s=0

120 DO UNTIL ITEM()=0

130 READ a

140 s=s+a

150 LOOP

160 PRINT sum

170 END PROC

Примененные в строках 120, 150 операторы DO UNTIL и LOOP - это
удобная форма организации цикла (см. далее). В принципе вместо DO
UNTIL можно было бы применить и DO WHILE (см. ниже).

Вызов этой процедуры можно выполнить, например так:
sum 1,2,3,4
или с другим количеством параметров:

sum 1,2,х,у,z,1256
Если Ваш список параметров состоит из строковых переменных, то
в строке 130 надо было бы применять READ а$, а не READ а. А как
быть, если список смешанный и содержит и числа и строки? В этом
случае перед READ надо проверить очередной параметр с помощью 1ТЕМ()
и использовать либо тот вариант, либо другой. В строковых пе-
ременных при этом можно избежать использования кавычек, если вме-
сто READ использовать READ LINE (см. далее).

Обратите внимание на то, что список фактических параметров,
передаваемых через DATA и READ, исключает возможность им быть
локальными, если Вы специально это не зададите.

РЕКУРСИЯ.

Процедура, которая вызывает саму себя, называется рекурсив-
ной. Есть анекдот, что в одном из компьютерных словарей-справоч-
ников написали: РЕКУРСИЯ - см. РЕКУРСИЯ.

Рекурсия часто помогает очень элегантно избегать сложных прог-
раммистских проблем. С другой стороны, это не самая быстро ра-

ботающая структура, к тому же возможны большие расходы памяти, ведь
при каждом вызове процедурой самой себя формируется новый вре-
менный список локальных переменных и для них выдэляется память.
Кроме того, несмотря на внешнюю простоту такой структуры, ее тща-
тельный разбор может вызвать у программиста легкое головокружение. В
качестве примера мы приведем процедуру, которая рисует бриллиант, а
затем еще один такой же, но поменьше, внутри первого и еще один
внутри второго и так далее. На минимальный размер наложено
ограничение, иначе процесс мог бы продолжаться бесконечно.
100 DEF PROC diamond х,у,size,diff
DEFAULT diff=15
PLOT x,y,-size
DRAW -size,size
DRAW size,size
DRAW size,-size
DRAW -size,-size
110 IF size >4 THEN

diamond x,y+size,size-diff
diamond x,y-size,size-diff
diamond x-size,y,size-diff
diamond x+size,y,size-diff
130 END PROC
Вызвать эту процедуру можно, например, так:
diamond 128,88,40

ОШИБКИ

Если- Вы попробуете вызвать процедуру, которую забыли задать,
Вам дадут сообщение об ошибке W: "Missing DEF PROC". Если же Вы
забудете закрыть процедуру с помощью END PROC - сообщение X: "No END
PROC". Программа будет стараться во время работы "перепрыгнуть"
через блок, в котором задается процедура и не сможет этого сделать.

Если при вызове задано больше фактических параметров, чем их
есть в наличии в описании процедуры, - сообщение "Parameter error."
Если тип формальных параметров не совпадает с типом реально
установленных фактических параметров - "Nonsense in BASIC".
Оператор END PROC может генерировать сообщение "Variable not found",
если оказывается, что переменная, которую процедура должна передать
в качестве выходного параметра, не существует.




СОДЕРЖАНИЕ:


  Оставте Ваш отзыв:

  НИК/ИМЯ
  ПОЧТА (шифруется)
  КОД



Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
AyHits - Max и музыка :)
Impulse longa, vita - кaкaя тяжёлaя жизнь, кaк нaдoелo зaнимaться paскpaшивaнием и сбopкoй Импульсa.
Проект года - потрясающая новелла к игре "Навигатор".
И снова party 1 - обзор графики и музыки.
Part 1 - Editorial and news.

В этот день...   21 ноября