[an error occurred while processing this directive]
─PL63 ─TM0 ─HM2 ─DH//-|P-// ─LM10 ─PE │..................................................................< 3.6 Процедуры и функции Процедуры представляют собой наиболее мощное средство структурирования программ рассматриваемой версии языка BASIC. Можно сказать, что данное средство приближает BASIC по предоставляемым возможностям к такому мощному языку структурного программирования, как PASCAL, сохраняя при этом его простоту и наглядность. Считается, что данная версия языка BASIC является практически первой в мире, которая предоставляет механизм процедур и процедур-функций. Что же такое процедура? Это произвольная последовательность операторов языка BASIC, которая может быть вызвана по имени в любом месте программы. Обычно совокупность операторов, оформленная в виде процедуры выполняет некоторую самостоятельную логическую функцию (решает подзадачу) в рамках определенной программы (задачи). В рамках данного пособия у нас нет возможности подробно остановиться на принципах модульности и методах структурного программирования, базирующихся на понятии процедуры или подпрограммы (применительно, например, к языку FORTRAN). Желающих ознакомиться более детально с этими вопросами, отсылаем к фундаментальной учебной литературе. Мы же сейчас остановимся подробнее на общей организации программы и методических основах использования процедур и процедур-функций с точки зрения языка BASIC. Применительно к понятиям процедур и прцедур-функций существуют две языковые категории это определение (или описание ) и обращение (или вызов). В целом, программа использующая процедуры и (или) процедуры-функции делится на две секции: 1 - выполняемая часть (головная программа) заканчивается операторам END; 2 - описание процедур и процедур-функций. Желательно, чтобы первая секция в исходном тексте программы предшествовала второй. Отметим, что все инструкции языка BASIC, относящиеся к первой секции, выполняются как обычно, и среди них могут содержаться обращения по именам к процедурам и (или) процедурам- функциям. Эти обращения не вызывают переходов в программе из первой секции во вторую (в отличие от операторов GOSUB, ON... GOSUB) с возвратом. Правильнее будет считать, что такие обращения вызывают выполнение другой программы (процедуры или процедуры-функций), исходный текст которой содержится во второй секции. При этом такая самостоятельная программа использует три вида переменных (допускаются все типы данных): переменные вызывающей программы (глобальные переменные); собственные переменные, определенные только внутри процедуры или процедуры-функции (локальные); переменные, значения которых передаются из вызывающей программы при вызове. Для описания процедуры или процедуры-функции используется специальный оператор DEF (от слова define - определять). Он является первым оператором описания и имеет формат DEF <имя>(список параметров), где имя - идентификотор процедуры, начинающийся с корня PROC или функции, начинающийся с корня FN; список параметров - список произвольных имен переменных, рассматриваемых как формальные параметры. Оператор DEF имеет смысл заголовка описания процедуры или процедуры-функции, его действие заключается: во-первых, в присвоении имени (начинающегося соответственно с PROC или FN) следующей за ним последовательности инструкций языка BASIC, выполнение которых будет осуществляться при вызове. Во-вторых, в заголовке описания указывается список формальных параметров. Имена переменных в этом списке служат лишь для обозначения тех переменных с которыми будут действительно выполняться нижеследующие инструкции при обращении к процедуре или прцедуре-функции. Имена формальных параметров никак не связаны с именами фактических, так как они относятся к разным программам (мы отмечали, что процедура или процедура-функция являются по существу самостоятельными программами). Примеры имен процедур: PROCBOX, PROCDiana, PROCexample1 и т.д. Примеры имен процедур-функций: FNSUM, FNMean, FNexample1 и т.д. Примеры заголовков: DEF PROCNUM (X,Y,Z) DEF FNSwitch (NAME¤, Length%) Последовательность инструкций языка BASIC, следующая за заголовком процедуры, носит название тела процедуры. Последним оператором тела должен являтся специальный оператор ENDPROC - конец описания. В теле могут использоваться не только переменные, передаваемые через параметры, но и другие переменные, которые подразделяются на локальные и глобальные. Локальные это те переменные, которые существуют (известны) только в процедуре или процедуре-функции, их значения являются недоступными для вызывающей программы. Имена таких переменных присваиваются независимо от имен вызывающей программы и других процедур или процедур-функций (в часности, могут совпадать). В языке BASIC имеется оператор LOCAL, который используется для обьявления переменных локальными внутри процедуры или процедуры-функции. Оператор LOCAL является чисто описательным (невыполняемым) оператором и должен следовать непосредстаенно за заголовком. Формат оператора LOCAL список имен переменных не требует комментария. В одной процедуре допускается наличие нескольких операторов LOCAL. Переменные процедуры или процедуры-функции, не обьявленные локальными с помощью оператора LOCAL и не являющиеся формальными параметрами, относятся к глобальным переменным, т.е. доступны вызывающей программе. Это обстоятельство требует особой осторожности при работе с глобальными переменными, так как их значения могут модифицироваться внутри процедуры, что, в свою очередь, даст "побочный эффект" при дальнейшем выполнении вызывающей программы. Отметим также, что фактические параметры, заменяющие формальные, при вызове процедур и процедур-функций передаются в тело процедуры своими значениями, но не именами. Иначе говоря, значения фактичских параметров поступают на вход и не модифицируются в вызывающей программе. По завершению выполнения процедуры или процедуры-функции значения переменных, являющихся фактическими параметрами, останутся такими же как и при вызове. В этом смысле фактические параметры можно считать локальными переменными процедур и процедур-функций. Здесь также наблюдается принципиальная разница между процедурой и подпрограммой в языке FORTRAN. Процедура, в отличие от подпрограммы, не может иметь выходных параметров, а только входные. Модификация значений переменных вызывающей программы осуществляется только через механизм глобальных переменных. Из этого следует, что процедура представляет собой существенно менее универсальное средство, чем подпрограмма в Фортране, так как имеет связь (привязку) с вызывающей программой. Наконец отметим, что независимо от порядка в котором следует описание процедур и процедур-функций во второй секции программы (после оператора END) допускается взаимный, сколь угодно раз вложенный вызов. Кроме того допускается вызов процедурами и функциями самих себя , т.е. рекурсия. Поскольку процедуры и процедуры-функции имеют кроме изложенного общего также и существенные отличия, дальнейшее их рассмотрение проведем раздельно. Процедуры. Итак,описание процедур содержится во второй секции программы (после оператора END), начинается с заголовка описания (оператор DEF), далее могут следовать определения локальных переменных (оператор LOCAL), затем собственно тело процедуры и, наконец, завершается описание оператором ENDPROC. Обращение к процедуре (вызов) осуществляется в вызывающей программе оператором, представляющим собой имя процедуры, за которым в круглых скобках следует список фактических параметров (если таковые имеются). Рассмотрим пример фрагмента программы, использующего процедуру для вывода на экран горизонтальной линии в виде последовательности символов подчеркивания в заданном диапазоне позиций строки с N по M. 5 MODE 0 10 INPUT "Введите через запятую номера левой и правой позиции строки" lev, prav 20 PROCline (lev, prav) 30 END 40 DEF PROCline (N,M) 50 LOCAL I 60 PRINT TAB (N-1); 70 FOR I=1 TO M-N+1 80 PRINT "_"; 90 NEXT I 100 PRINT 110 ENDPROC. В данном примере наличие формальных параметров N,M делает процедуру достаточно универсальным средством, удобным, например, при формировании таблиц на экране. Вторая секция программы (строки 40-110) является примером описания процедуры. Переменная I является управляющей переменной цикла и объявлена локальной (строка 50). Это полезно сделать, так как в принципе, обращение к процедуре может встречаться в различных местах большой программы, в которых переменная I также используется. Наличие оператора LOCAL как бы защищает внешние по отношению к PROCLine переменные I от модификации внутри процедуры. Процедуры-функции. Если процедуры предназначены для именования и выполнения при обращении некоторой "логически замкнутой" последовательности операторов языка Бейсик в самом общем смысле, то процедуры-функции решают частную задачу - сформировать выходное значение - как функцию задаваемых значений аргументов (фактических параметров) по алгоритму, записанному в виде тела процедуры с помощью формальных параметров. По принципу обращения (вызова) процедуры-функции полностью аналогичны встроенным функциям языка Бейсик т.е. используются также как переменные вызывающей программы. Формируемое значение функции присваивается непосредственно ее имени в последнем операторе описания процедуры-функции. Он имеет вид оператора присваивания без левой части =выражение, где выражение - произвольное арифметическое, логическое или строковое выражение. Результат выражения присваивается имени функции. Простейший пример фрагмента программы для нахождения суммы произвольных трех чисел с использованием процедуры-функции 5 MODE 7 10 INPUT "Ввидите через запятую три числа "А,В,С 20 PRINT "Сумма равна"; FNSUM(A,B,C) 30 END 40 DEF FNSUM(X,Y,Z) 50 = X+Y+Z Обращение к процедуре-функции делается в операторе PRINT (строка 20 головной программы), как к обычной переменной, содержащей в качестве значения искомую сумму. Отметим еще раз, что описание (тело) процедуры-функции заканчивается не специальным оператором (типа ENDPROC, как в случае процедуры), а обычным оператором присваивания, только без левой части. Именно этот оператор определяет значение процедуры-функции, присваиваемое ее имени. Процедуры-функции позволяют неограниченно расширять набор встроенных функций языка Бейсик в пользовательских программах. ─PE 3.7 Работа с файлами 3.7.1 Организация файлов Будем исходить из того,что читатель знаком с понятием "файл", поэтому не приводим здесь одно из многочисленных существующих определений. Отметим, что средства для работы с файлами языка Бейсик предоставляют возможности ввода данных в программу с магнитных носителей (операция чтения из файла), а также вывода информации на устройства внешней памяти (операция записи в файл). Мы будем рассматривать файлы, соостоящие из записей, которые в свою очередь содержат данные, т.е. числа и (или) символьные строки. В качестве магнитных носителей информации для организации ввода-вывода данных в программах на Бейсике могут выступать-кассета с магнитной лентой, гибкий диск (или флоппи), диск типа "винчестер". Каждому из перечисленных носителей соответствуют различные устройства записи-чтения информации, а значит различные системы доступа к файлам и содержащимся в них записям. Однако, нижерассматриваемые средства языка Бейсик не зависят от типа устройства внешней памяти, поэтому программы, использующие эти средства, не должны модифицироваться в зависимости от используемого носителя информации. ─PE10 3.7.2 Открытие файлов Операция открытия файла предшествует непосредственно обмену информацией между оперативной и внешней памятью ПЭВМ. Суть этой операции можно условно рассматривать, как установление некоторого канала связи, по которому будет осуществляться собственно передача информации. Для работы с каждым файлом в программе должен быть открыт отдельный канал связи. В языке Бейсик имеются три оператора открытия файла-OPENIN, OPENOUT, OPENUP. Все они имеют одинаковый синтаксис числ. пер.=оператор ("имя ф."), где числ.пер. - произвольная числовая (целочисленная или вещественная) переменная; оператор - один из трех указанных операторов открытия файла; имя файла- имя открываемого файла (см.п.6). Например, X=OPENIN ("FILENAME"). Числовая переменная в левой части получает в качестве значения номер канала связи для обмена информацией с указанным в правой части файлом данных. Наличие трех различных операторов открытия файла позволяет конкретизировать операции ввода-вывода, которые в дальнейшем будут производиться, или наделить открываемый канал связи определенными логическими функциями соответственно особенности каждого из операторов открытия файла. OPENIN. Открывает файл только для чтения. Оператор предназначен для открытия существующего файла данных, из которого предусматривается ввод информации в программу. Запись (вывод информации) в файл, открытый оператором OPENIN, запрещена. Таким образом, оператор OPENIN обеспечивает защиту информации в открываемом файле от модификации или дополнительной записи. OPENOUT. Открывает новый (несуществующий) на момент открытия файл только для записи (вывода информации). Ввод данных в программу из файла, открытого оператором OPENOUT, невозможен. Если в поле оператора указано имя некоторого существующего файла, то то он будет перезаписан, т.е. создается новый файл вместо существующего. OPENUP. Открывает обязательно существующий файл с указанным именем для выполнения операций как чтения (ввод информации в программу), так и записи (вывода информации). В целом, оператор OPENUP предназначен для модификации некоторого файла данных. ─PE10 3.7.3 Операторы ввода-вывода Аналогично операторам ввода-вывода на терминал - INPUT и PRINT, в Бейсике имеются соответствующие операторы для обмена информацией с файлом на устройстве внешней памяти. Это соотвественно, операторы INPUT# и PRINT#. Еще раз подчеркнем, что в программах на Бейсике использованию операторов обмена обязательно должны предшествовать операторы открытия некоторых файлов. Формат оператора обмена следующий. INPUT#N, список переменных. PRINT#N, список переменных и констант, где N -произвольная числовая переменная (как правило та же, что и в предшествующем операторе открытия файла), определяющая установленный ПЭВМ номер канала связи с файлом. Список переменных представляет собой перечисление через запятую идентификаторов переменных программы, которым присвоены значения при чтении из файла (оператор INPUT#) и значения которых будут записываться в файл (оператор PRINT#). В списке могут, естественно, содержаться переменные всех допустимых типов данных, а в операторе PRINT# в список могут входить и константы всех типов, которые будут непосредственно записываться в файл. Каждый оператор PRINT# формирует в файле отдельную запись. Примеры операторов: INPUT#channel, date, name¤, address¤ PRINT#file, X,Y,Z,A¤,"Monday",33. ─PE10 3.7.4 Закрытие файла По завершении всех операций обмена информацией с файлами необходимо выполнить еще одну специальную процедуру, называемую закрытием файла. Смысл ее состоит в следующем: во-первых, ПЭВМ ставится в известность, что закрепленный канал связи освободился, и может быть использован для установления связи, например, с другим файлом; во-вторых, физически завершаюся все операции обмена между оперативной памятью и внешним устройством; в-третьих, система управления файлами получает все необходимые данные, обеспечивающие доступ к записям закрываемого файла. Закрытие файла осуществляется оператором CLOSE#, формат которого CLOSE# числ.пер., где числ.пер.-полностью соответствует (совпадает) с параметром одного из используемых ранее операторов открытия файла. Заметим, что своевременное использование оператора закрытия CLOSE# также необходимо, как и предварительное открытие файлов. В противном случае файл может оказаться "запорченным", т.е. доступ к содержащимся в нем записям будет потерян. Прежде, чем перейти к дальнейшему описанию команд для работы с файлами рассмотрим простейшие примеры. Предположим, что мы хотим создать файл с именем GROUP, в котором будем хранить список студентов некоторой группы. Фрагмент программы для занесения в файл имеет вид: 10 INPUT "Число студентов?" N 20 C=OPENOUT ("GROUP") 30 FOR I=1 TO N 40 INPUT "Фамилия студента?" FAM¤ 50 PRINT#C, FAM¤ 60 NEXT I 70 CLOSE#C Оператор 20 создает и открывает для вывода файл GROUP, оператор 50 осуществляет вывод очередной записи, содержащей фамилию студента (переменной FAM¤) в этот файл, наконец опертор 70 осуществляет закрытие сформированного файла. Следующий фрагмент программы соответственно распечатывает на экране список студентов, содержащийся в файле GROUP: 10 INPUT "Число студентов?" N 20 C=OPENIN ("GROUP") 30 FOR I=1 TO N 40 INPUT#C, FAM¤ 50 PRINT I, FAM¤ 60 NEXT I 70 CLOSE#C Заметим,что если в первом фрагменте ввод числа студентов N представляется достаточно естественным (при создании списка ), то во втором он выглядит вынужденным, так как пользователь программы не обязательно должен его знать. Другими словами, часто при работе с файлами возникает задача прочитать весь файл, не зная общее количество содержащихся в нем записей. В Бейсике существует специальная функция EOF# - (конец файла - End of File), которая проверяет есть ли в файле еще записи, или уже достигнут конец файла. Формат EOF#(числ.пер.), где числ.пер.-переменная, содержащая номер канала связи с открытым файлом. Функция EOF# принимает логическое значение "истина", когда в результате очередного ввода информациии будет прочитана последняя запись (конец файла). Таким образом EOF# можно использовать для проверки достижения конца файла. Так предыдущий фрагмент распечатки списка может быть модифицирован с учетом отсутствия необходимости указания числа студентов следующим образом: 10 C=OPENIN ("GROUP") 20 I=1 30 REPEAT 40 INPUT C#,FAM¤ 50 PRINT I, FAM¤ 60 I=I+1 70 UNTIL EOF#(C) 80 CLOSE#C Условием окончания цикла (строки 20-70) является логическое значение "истина", возвращенное функцией EOF#(C), т.е. считывание _оследней записи файла "GROUP". ─PE10 3.7.5. Организация файлов прямого доступа Выражение PTR# (Pointer) - указывает текущее место (или точку) в файле при выполнении очередной операции ввода-вывода, которое определяется в байтах от начала файла. Формат числ.пер.1=PTR#числ.пер.2 (1) или PTR#числ.пер2=числ.пер.1. (2) где числ.пер.1-соответственно получаемое или присваемое значение указателя места в файле; числ.пер.2-определяет канал связи для открытого файла. Выражение PTR# позволяет в случае формата (1) определить из какого места файла осуществляется очередная операция чтения-записи, а в случае формата (2) указать конкретно место в файле для выполнения соответствующих операций. Последнее позволяет практически работать с файлами прямого доступа. Рассмотрим предыдущий пример, в котором сделаем для простоты изменения, организовав файл с записями фиксированной длины. Следующие два фрагмента иллюстрируют соответственно создание файла с длинной записи в 20 байт для занесения фамилий студентов и выборочную распечатку на экране произвольных записей по указываемому номеру. Первый фрагмент (создание файла с записями фиксированной длины): 10 C=OPENOUT ("GROUP") 20 I=0 30 REPEAT 40 INPUT "Фамилия студента?" FAM¤ 50 IF FAM¤="" THEN GO TO 100 60 PTR#C=20*I 70 PRINT#C, FAM¤ 80 I=I+1 90 UNTIL FALSE 100 CLOSE#C Очевидно, что выражение PTR#C (сторока 70) управляет базовым адресом заносимых записей в строке 80 относительно начала файла, каждый раз увеличивая его на 20. Теперь фрагмент вывода на экран записей с конкретным порядковым номером: 10 C=OPENIN ("GROUP") 20 INPUT "Номер студента в списке"N 30 PTR#C=20*(N-1) 40 INPUT#C, FAM¤ 50 PRINT N, FAM¤ 60 INPUT "Будете еще делать запрос? (да/нет)?"К¤ 70 IF К¤="Да" THEN GO TO 20 80 CLOSE#C В целом, использование выражение PTR# позволяет обеспечить прямой доступ к произвольным записям файла с целью их извлечения, модификации и т.д. Для модификации файла с записями фиксированной длины бывает полезно получить информацию об общем количестве записей в некотором текущем состоянии. В общем случае часто необходимым оказывается знать общий размер файла данных. Выражение EXT#(EXTend)-преназначено для определения общего размера открытого файла в байтах. Формат числ.пер.1=EXT#(числ.пер.2), где числ.пер. 1, числ.пер.2-имеют тот же смюсл, что и в выражении PTR#. В часности, для файла с записями фиксированной длины, общее количество записей определяется оператором N=EXT#(C)/L, где N-общее количество записей; С-номер канала связи с открытым файлом, L-длина записи. ─PE10 3.7.6 Побайтные операции ввода-вывода с файлами данных. Операторы BGET# и BPUT# Для некоторых специальных приложений в языке Бейсик имеются средства записи в файл (чтения из файла) кодов отдельных символов (байтов). Оператор BGET# (get a byte from file) предназназначен для чтения из файла, а BPUT# (put a byte to life)-записи в файл. Формат оператора BPUT#: BPUT# числ.пер.1,числ.пер.2, где числ.пер.1-номер канала связи с файлом, числ.пер.2-переменная, число или выражение лежащие в диапазоне 0-255, т.е. размещаемые в одном байте опера- тивной памяти. Например, BPUT#N, 32 или BPUT#A, Х MOD 256. Формат оператора BGET#: числ.пер.2=BGET#числ.пер.1. Байт, читаемый из файла с номером канала связи "числ.пер.1" присваивается числовой переменной программы "числ.пер.2." памяти.[an error occurred while processing this directive]