Подпрограммы, функции и процедуры
----------------------------------------------------------------
Стуктура программы в ТУРБО БЕЙСИКе может быть упрощена использо-
ванием подпрограмм, процедур и функций. Подпрограмма - это поме-
ченный набор инструкций,  исполняемых при достижении GOSUB. Про-
цедура - это как бы минипрограмма, (называемая также подпрограм-
мой), выполняющая некоторые важные части вашей основной програм-
мы. Функция -  это  набор  программ, возвращающих  строковый или
численный  результат, обычно связанный с параметрами, передавае-
мыми  функции.  Помещая  часто  используемый код в эти структуры
можно упростить и сократить ваши  программы.
    Процедуры ТУРБО БЕЙСИКа и определяемые пользователем функции
выходят за  рамки простого струтурирования,  предлагаемыми подп-
рограммами. Хотя миллионы программ в Бейсике были написаны с по-
мощью GOSUB/RETURN  как основных оргнаизационных приспособлений,
мы рекомендуем использовать эти улучшенные структуры.
    Процедуры и  функции ТУРБО БЕЙСИКа обеспечивают истинную ре-
курсию, передачу параметров и доступ к локальным,  статическим и
глобальным переменным.
    У процедур и функций сходства больше, чем различий. Наиболее
яркое различие состоит в том,  что функции возвращают величину и
таким образом,  вызываются неявно,  появляясь  в  выражениях  (с
"FN" присоединенным впереди).  Процедуры не возвращают величин и
должны появляться явно оператором CALL (ВЫЗОВ). Например,

a = b + FNCubeROOT(c)     'вызов функции
CALL OutChar(a)           'вызов процедуры

Подпрограммы
- - - - - - -
Подпрограммы -  это традиционный метод разбивки программ Бейсик;
они состоят из помеченных групп операторов и заканчиваются  опе-
ратором RETURN (ВОЗВРАТ).  Для выполнения подпрограммы можно ис-
пользовать оператор GOSUB c указаниem  метки, связанной с первым
оператором подпрограммы.  При встрече с оператором RETURN управ-
ление переходит к оператору, непосредственно следующему за вызо-
вом GOSUB. Например:

GOSUB AddMonths
PRINT total
END
AddMonhts:
  total = 0
  FOR i = 1 TO 12
    total total + month(i)
  NEXT i
RETURN

Функции
- - - -
Существует два типа  функций:  стандартные (такие,  как  COS или
LEFT$), определяемые языком программирования и заданные  пользо-
вателем, которые могут состоять как из одной,  так и из несколь-
ких строк.  (Более подробно заданные функции описаны в соответс-
твующих разделах  Главы  5.  "Справочное  руководство  по  ТУРБО
БЕЙСИКу.)
    Ниже приведен пример синтаксиса однострочной функции:

DEF FNidentifier [(parameter list)] = expression

где идентификатор - это заданное пользователем имя специфическо-
го выражения, список параметров - дополнительная разделяемая за-
пятыми последовательность  одного   или  более  идентификаторов,
представляющих информационные объекты,  направляемые функции при
ее вызове  во время выполнения программы (не более 16 параметров
для одной функции),  выражение определяет обработку  данных  для
получения значения возвращаемого (выдавыемого) функцией.
    Рассмотрим, например,  метеорологическую программу,  которая
всегда должна  преобразовывать  градусы Фаренгейта. Однострочные
функции - благо для такого типа программ:

DEF FNCtoF(degreesC) = (1.8 * degreesC) + 32
DEF FNFtoC(degreesF) = (degreesC - 32) * .555555

    Для отображения переменной температуры,  которая по соглаше-
нию всегда  выражается  в величинах Цельсия,  используйте FNCtoF
(читается как "функция Ц в Ф") в любом операторе, воспринимающем
численные выражения; например:

temp = 100
PRINT FNCtoF(temp)

    Для перевода значений из градусов Фаренгейта в градусы Цель-
сия пользуйтесь FNCtoC:

INPUT "Введите сегодняшнюю температуру",th
temp = FNFtoC(th)

    Многострочные функции  ТУРБО  БЕЙСИКа  берут на себя гораздо
большую роль, чем разрешено простыми однострочными функциями Ин-
терпретатора Бейсика. ТУРБО БЕЙСИК позволяет функции распростра-
ниться по нескольким строкам программы и фактически быть исполь-
зованной как   подпрограмма,  которая  может  также  и  получить
значение. Формальный синтаксис для описания многострочной  функ-
ции следующий:

DEF FNидентификатор [(список-параметров)]
  [объявления переменных]
  .
  . операторы
  .
  [EXIT DEF]
  [FNидентификатор = выражение]
END DEF

где идентификатор описывает имя функции, список-параметров - это
разделяемый запятыми список формальных параметров,  представдяю-
щих переменные, передаваемые функии при ее вызове.
    Чтобы проиллюстрировать это,  рассмотрим многострочную функ-
цию Факториал. (Факториал положительного целого числа n, описан-
ного как n!, это произведение  положительных целых  чисел меньше
или равных n.  Например, 6! = 6 * 5 * 4 * 3 * 2 * 1 = 720).
    Факториалы не включены  в  набор  встроенных  математических
операторов ТУРБО  БЕЙСИКа,  но  многострочные функции восполняют
этот пробел:

100 DEF FNFactorial#(x%)
110   LOCAL i%, total#
120   IF x% < OR x% > 170 THEN FNFactorial# = -1 : EXIT DEF
130   total# = 1
140   FOR i% = x% TO 2 STEP -1
150     total# = total# * i%
160   NEXT i%
170   FNFactorial# = total#
180 END DEF

FNFactorial демонстрирует структуру многострочной функции (номе-
ра строк включены, поэтому мы можем ссылаться на строки по номе-
рам при их обсуждении - они, конечно, необязательны).
    Описания функции  ограничены  операторами  DEF FN и END DEF;
делая отступ в два знака перед операторами DEF FN и END  DEF, мы
четко очерчиваем эту структуру.
    Строка 100 присваивает функции имя и,  следовательно, тип (#
означает двойную  точность).  У  FNFactorial  один целочисленный
формальный параметр x%.
    Строка 110 описывает пару локальных переменных i%  и total#.
Локальные перменные - это временные структуры, доступные и види-
мые только  внутри  описания  функции или процедуры (поробно они
рассмотрены ниже в разделе "Локальные перменные").
    Строка 120   производит  проверку  аргумента,  передаваемого
FNFactorial .  Бессмысленно пытаться найти факториал отрицатель-
ного числа  (его просто не существует) или числа столь большого,
что оно дает результат,  выходящий за 10^308 - диапазон  двойной
точности (факториалы растут быстро -  170! это 7.26 * 10^308).
    Программы,  в которых используется FNFactorial, должны  рас-
познавать,  что  обратное  значение   -1  представляет состояние
ошибки, и действовать соответствующим образом. ( Точно так же 0!
определяется как 1).
    EXIT DEF  для  функций  то  же,  что RETURN для подпрограмм:
возвращает управление оператору,  вызвавшему функцию.  Так и хо-
чется использовать для этого RETURN - но не делайте этого! Обра-
тите внимание,  что EXIT DEF вовсе необязателен, если только вам
не нужно вернуться до конца выполнения функции.
    Строки с 130 по 160 определяют алгоритм для расчета фактори-
алов. Эта  часть многострочной функции ("тело") может быть сколь
необходимо большой или малой.
    Строка 170   определяет  величину,  выдаваемую  FNFactorial,
присваивая значение имени функции. Удивительно, но назначение не
является  синтаксическим требованием для описания функции.  Если
вы не произведете назначение имени функции,  то возвращаемая ве-
личина будет неопределенной.
    Определение FNFactorial  завершается  операторм  END  DEF  в
строке 180. END DEF как и EXIT DEF, возвращает управление опера-
тору, вызвавшему функцию (Оператор  END  несет,  пожалуй,  самую
большую нагрузку в синтаксисе ТУРБО БЕЙСИКа - он испльзуется для
завершения целого ряда структур.)
    Вы никогда не задумывались,  сколько комбинаций можно произ-
вести с колодой карт? FNFactorial знает:

PRINT FNFactorial#(52)

дает в результате 8.065817517094388E069.
    Поскольку FNFactorial  определяется  как имеющий целочислен-
ный формальный  параметр,  аргументы  с плавающей  запятой перед
передачей   ему   округляются   до   целых   чисел;    например,
FNFactorial(2.7)  то же,  что FNFactorial(3).   Если вы вызовете
FNFactorial с числом, большим,  чем может  быть обработано  про-
граммой ТУРБО БЕЙСИКа "Округлить-до-целого"  (больше  32767  или
меньше  -32768),  вы  получите   ошибку  "6"  при   выполнении -
Переполнение.
    Точно так же обрабатываются аргументы для встроенных функций
ТУРБО БЕЙСИКа, предполагающих целочисленные аргументы; например,
LOCATE 2.7, 1  переводит курсор на 3-ю строку.

Формальные и фактические параметры
- - - - - - - - - - - - - - - - - -
Переменные, появляющиеся в списке параметров при описании  функ-
ции называются  формальными  параметрами.  Они служат только для
определения функции и полностью отделены от других  переменных в
программе с  таким же названием.  В качестве иллюстрации давайте
рассмотрим следующую короткую программу:

100 DEF FNArea(x,y) = x * y
110 x = 56
120 PRINT x, FNArea(2,3),x

    Переменная x в 110 и 120 строках этой программы не связана с
формальным параметром  x,  определенным  в функции Area в первой
строке. При выполнении этой программы x сохраняет  свое значение
в любой части вызова в FNArea: в обоих случаях печатается 56.
    Значения, поступающие функции при выполнении, иногда называ-
ют фактическими параметрами. В последнем примере численные конс-
танты  2  и 3 - фактические параметры, переданные FNArea. Факти-
ческие параметры вполне могут быть перменными:

a = 2: b  = 3
PRINT FNArea(a,b)

Типы функций
- - - - - - -
Функции могут возвращать любой из четырех численных типов (цело-
численный, длинный целочисленный,  с плавающей запятой с одинар-
ной и двойной точностью),  а также строковой тип.  Тип  функции,
так же как и переменной, определяется ее именем. В нем использу-
ются символы описания типов и оператор DEFtype, например:

DEF FNIntSquareRoot%(x) = INT(SQR(x))
PRINT FNIntSquareRoot%(2)

или

DEF FNRepeatFirst$(a$) = LEFT$(a$,1)+a$
PRINT FNRepeatFirst$('Hello')

    Попытка присвоить строковую функцию численной переменной или
численную функцию - строковой переменной приводит,  как и следо-
вало ожидать, к ошибке 13: Несоответствие типа.

Процедуры
- - - - -
Процедуры - это блоки программы,  заключенные между  операторами
SUB и END SUB.  Формальный синтаксис для описания процедуры сле-
дующий:

SUB имя-процедуры [(список параметров)] [INLINE]
    [овъявление переменных]
    .
    .операторы
    .
    [EXIT SUB]
END SUB

    Имя процедуры идентифицирует процедуру и может  содержать до
31 знака,  но оно не должно включаться ни в один другой оператор
SUB в программе; список параметров - это необязательный,  разде-
ляемый запятыми,  список формальных  параметров,  представляющих
переменные,  передаваемые  процедуре при  ее вызове (не более 16
параметров для  одной процедуры). INLINE (ВСТРОЕННАЯ) указывает,
что процедура содержит изменяющееся  количество  нетиповых пара-
метров и встроенный код Ассемблера (см.  Приложение С "Интерфейс
языка Ассемблера").
    Как сказано  ниже, наиболее явное различие между функциями и
процедурами заключается в том,  что процедуры не возвращают зна-
чения; но они также и не вызываются изнутри выражения,  не имеют
типа и не включают присваивания значения имени процедуры. Проце-
дуры вызываются оператором CALL, так же как GOSUB вызывает подп-
рограммы.
    Рассмотрим следующую  программу,  описывающую  и  вызывающую
процедуру PrintTotal (ПечатьИтого):

SUB PrintTotal(a,b,c,d)
  LOCAL total
  total = a + b + c + d
  Print total
END SUB

w = 1: x = 2: y = 0: z = 3
CALL PrintTotal(w,x,y,z)

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

SUB СountZeros(a(1), size, count)
' счетчик возвращает число нулевых элементов в одномерном масси-
' ве a с одинарной точностью, размер которого size + 1 элементов
    LOCAL i
    count = 0
    FOR i = 0 TO size
      IF a(i) = 0 THEN count = count + 1
    NEXT i
END SUB

    Наличие a(1)  в списке параметров определяет первый аргумент
CountZero (СчитатьНули) как одномерный  массив.  Он не  говорит,
каков будет размер массива, - это задача другого аргумента: size
(размер); count используется,  чтобы сообщить количество нулевых
элементов в массиве a. Вызов CountZero производится так:

size = 100: DIM Primes(size)
GOSUB StrikePrimes
CALL CountZeros(Primes(), size, primesCount)
PRINT "There are" primesCount "prime numbers <=" size
END

Определения процедур и функций и ход программы
- - - - - - - - - - - - - - - - - - - - - - - -
Положение определений  процедур или функций внутри программы не-
важно. Функция может быть определена в 1 или 1000  строке  прог-
раммы независимо от того,  где они используются. При этом необя-
зательно направлять ход программы через  процедуру  или  функцию
для этапа  инициализации (которую необходимо проводить у одност-
рочных функций в Интерпретаторе Бейсика). Компилятор заметит оп-
ределение, где бы оно ни находилось.
    Кроме того, в отличие от подпрограмм выполнение программы не
может случайно  "попасть" в процедуру или функцию.  Что касается
маршрута выполнения программы, то определения функций и процедур
"невидимы". Например, при выполнении следующей программы:

CALL PrintSomething
SUB  PrintSomething
  PRINT  "Напечатано из  PrintSomething"
END SUB

сообщение появляется только один раз.
    Заметьте: Определения процедур и функций следует  рассматри-
вать как изолированные островки кода. Но не следует сломя голову
бросаться в них или из них с помощью операторов GOTO, GOSUB или
RETURN - результаты могут быть непредсказуемы и/или фатальны.
    Обратите внимание,  что ни описания  процедур,  ни  описания
функций не  могут  быть вложены;  т.е.  нельзя определить другую
процедуру или функцию внутри определения  данной  процедуры  или
функции (хотя  они  могут  содержать  вызовы других процедур или
функций).

Проверка аргументов
- - - - - - - - - -
ТУРБО БЕЙСИК  проверяет  соответствие  числа и типа аргументов в
вызовах программных процедур  и функций  числу и типу формальных
параметров  в  соответствующих  определениях.  Например, попытка
трансляции программы

DEF FNDummy(a,b)
END DEF
t = FNDummy(3)

приводит к ошибке "Несоответствие Параметра" в 3-ей строке, т.к.
FNDummy требует два аргумента.

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

         Таблица 4-4. Различия между Процедурой и Функцией
  ------------------------------------------------------------
  Действие                 Функции             Процедуры
  ------------------------------------------------------------
  Возвращение значения   Да                  Нет
  Метод вызова           Вызывается изнут-   Используется
                         ри выражений        оператор CALL
  Передача параметра     Передается по зна-  Передается по
                         чению               ссылке и значению
  Переменные по умол-    ОБЩАЯ               СТАТИЧЕСКАЯ
  чанию
  Аргументы массива      Нет                 Да
  ------------------------------------------------------------

    Во-первых, посмотрите на эту короткую программу, описывающую
и вызывающую функцию CylVol (Объем цилиндра):

DEF FNCylVol(Radius, height) STATIC
  FNCylVol = Radius * Radius * 3.14159 * height
END DEF

R = 4.7: h =12.1
vol = FNCylVol(R,h)

    Теперь представьте  себе объем обработки при выполнении этой
программы. Сначала нужно назначить значения переменных  R  и  h.
Затем вызвать FNCylVol, передавая ей численную информацию по R и
h. Но минуточку,  а каким же образом передать функции эту инфор-
мацию?
    Есть два способа:  (1) использовать 4.7 в качестве радиуса и
12.1 - высоты; или (2) использовать перменную R в качестве ради-
уса, а h - высоты.  Первый называется - передать по значению,  а
второй - передать по ссылке.
    Передача по значению означает, что копии аргументов в вызове
располагаются в памяти временно,  а затем переносятся, когда не-
обходимо, в  вызванную  программу.  После  выполнения   процеду-
ры/функции значение из памяти исчезает;  таким образом оригиналы
остаются нетронытыми.
    Передача по ссылке означает, что передается указатель, пока-
зывающий где (адрес) хранятся данные.  В примере адреса перемен-
ных R  и h передаются как параметры.  Вызываемая программа может
затем получить и изменить сами значения.
    В ТУРБО БЕЙСИКе используются оба способа передачи (их разли-
чия станут вам ясны через минуту).
    Метод передачи  по  значению  позволяет  использовать  любую
константу или выражение в качестве  аргументов.  при  выполнении
выражение оценивается и сводится к простому значению,  которое и
передается функции.
    Например:

V = FNCylVol(R,h * 2 + 4.1)

    Методу передачи  по  значению  не составляет  труда сообщить
h * 2 + 4.1 функции FNCylVol.  Выражение вычисляется и результат
посылается функции.
    С другой стороны, однако, метод передачи по ссылке, посколь-
ку он позволяет программе обрабатывать  значение,  не  оперирует
константами и  выражениями (такое выражение,  как h * 2 + 4.1 не
имеет адреса в памяти, - его имеют только переменные). Метод пе-
редачи по  ссылке  действует  только если аргумент в процедуре -
отдельная переменная.
    Первый закон передачи параметра: Как переменные, так и выра-
жения могут быть переданы методом передачи по значению. Передача
по ссылке позволяет посылать только переменные или массивы.
    Преимущество передачи по ссылке заключается в том,  что про-
цедура, вызванная  программой,  может изменять значения парамет-
ров, передаваемых ей и таким образом,  возвращать информацию вы-
зывающей программе. Поскольку программа, которой переданы данные
по ссылке,  получает и адрес, она "знает", где расположена пере-
менная и,  таким образом,  может как считывать, так и записывать
ее. И напротив, программа, которой переменная передана по значе-
нию, не может изменять исходную переменную,  поскольку не знает,
где эта переменная расположена.
    Второй закон передачи параметра:  Переменные,  переданные по
ссылке могут изменяться вызванной программой; переданные по зна-
чению - нет.
    В качестве иллюстрации рассмотрим программу:

a = 0: d = 2: c = 3
CALL Add(a,b,c,total)
PRINT a, total
END

SUB Add(i,j,k,sum) STATIC
   sum = i+j+k
END SUB

    При возврате  Add переменная total (итого) содержит сумму a,
b и c.  (Add можно определить так, чтобы она меняла значение ка-
жого из своих аргументов;  однако, поскольку она дает назначение
только своему четвертому параметру,  - только на  total  (итого)
распространено действие передачи  по ссылке при вызове Add).
    Третий закон - это следствие первого и второго  законов  ТБ:
Аргументы функций передаются по значению; аргументы процедур пе-
редаются по ссылке и по значению.
    Это означает, что отдельные имена переменных могут фигуриро-
вать в вызове процедуры и эти переменные  могут  быть  изменены.
Если Вы  хотите передать по значению одиночную переменную проце-
дуре - заключите ее в скобки. Это заставляет ТБ анализировать ее
как выражение. Процедуры могут также принимать константы и выра-
жения как передачу по значению.  Вызовы функций могут  принимать
константы, переменные и выражения,  но не могут изменять их зна-
чения.
    Вполне можно сделать присвоение формальному параметру внутри
определения функции;  фактически очень часто бывает  удобно  ис-
пользовать формальнве параметры функции в качестве временных пе-
ременных. Это,  однако,  не изменяет значение фактического пара-
метра; например:

DEF FNDummy(a,b,c)
   a = a+b+c
   PRINT a
END DEF

x = 1: y = 2: z = 3
t = FNDummy(x,y,z)
PRINT x

    Присвоение формального параметра a функции FNDummy ("немая")
не влияет на значение переменной x.
    Поскольку передача переменной  функции  подразумевает  также
копирование данных переменных для размещения в локальной памяти,
возникают проблемы, если переменная очень большая (т.е. массив).
Поэтому ТБ  не позволяет передачу переменных типа массив по зна-
чению, хотя можно передать отдельные элементы массивов.

Локальные переменные
- - - - - - - - - - -
В интерпритуруемом Бейсике все переменные - глобальные. Это зна-
чит, что независимо от того,  где появляется переменная в  прог-
рамме -   в   самом   верхнем  шаге  основной  программы  или  в
тривиальнейшем цикле второстепенной подпрограммы - эта  перемен-
ная доступна  для всей программы.  Это позволяет следующему типу
ошибки вкрасться в программу:

' Главная программа
EmployeeCount= 10
n = 1
GOSUB CalcChecks
n = n+1

CalcChecks:
GOSUB CalcDeductions
RETURN

CalcDeductions:
   FOR n=1 to EmployeeCount
    .
    .
    .
   NEXT n
RETURN

PrintChecks:
    .
    .
    .
RETURN

    Переменная n во второстепенной программе CalcDeduction и пе-
ременная n в основной программе - это одно и то же.  В результа-
те, при возвращении управления основной программе,  n не  хранит
значение, равное 1, установленное  здесь ранее основной програм-
мой, но хранит значение EmployeeCount + 1 как результат выполне-
ния цикла FOR/NEXT из CalcDeductions.
    В соответствии со своим неформальным стилем Бейсик не требу-
ет от вас объявления переменных (т.е.  обозначения где-то в опе-
раторе, что вы скоро используете такую-то переменную). Даже если
вы фанатично отслеживаете все переменные вашей  программы,  одно
неверное движение пальца может погубить все.
    Для того,  чтобы предупредить эту проблему ТУРБО БЕЙСИК  до-
пускает наличие  локальных переменных внутри процедур и функций.
В отличие от глобальной переменной локальная переменная  сущест-
вует только в программе,  в которой она определена. Одних только
локальных переменных вполне достаточно для того,  чтобы навсегда
отказаться от   подпрограмм.   Рассмотрим,   например,   функцию
AddReceipts (СложитьПоступления):

DEF FNAddReceipts
  LOCAL x,y,total
  FOR x = 1 TO 12
    FOR y = 1 TO 30
      total = total + Receipts(x,y)
    NEXT y
  NEXT x
  FNAddReceipts = total
END DEF

    Поскольку переменные  x  и  y  описаны  как  локальные   для
FNAddReceipts, можно использовать имена переменных x и y в любом
месте программы , не влияя на значение  x  и  y  в FNAddReceipts
(или наоборот).
    Локальные переменные  функции  AddReceipts существуют только
до тех пор,  пока функция вызвана - до и после этой  функции  их
как бы не существует.  В качестве иллюстрации рассмотрим кодовый
сегмент, вызывающий FNAddReceipts:

x = 35
ThisYear = FNAddReceipts
PRINT x

    Тот факт,  что имя x было присвоено и  другим  переменным  в
процессе расчета значения возврата для FNAddReceipts,  не влияет
на x в строках 1 и 3.  С помощью логической  операции  выделения
стека, x  в FNAddReceipts - это независимая переменная,  которая
не существует после возврата функции.
    Локальные переменные должны быть описаны до любого выполняе-
мого оператора в процедуре или функции. Локальные переменные мо-
гут быть  массивами:  для этого достаточно просто задать размер-
ность массива после объявления его локальным.

SUB Dummy
  LOCAL a, locArray(50)
  .
  .
  .
  ERASE locArray
END SUB

    Выделение локального массива  автоматически  отменяется  при
выходе из процедуры или функции.
    Недостатком локальных переменных является то, что они теряют
свое значение  в  промежутках  между содержащими их программами.
При каждом вызове "хранилище" для них создается  заново,  и  они
инициализируются нулем (или пустыми строками). Например:

SUB Dummy
  LOCAL c
  PRINT c
  c = c + 1
END SUB

    Можно целый день вызывать Dummy и всегда будет печататься 0.

Общие переменные
- - - - - - - - -
Процедуры и  функции  могут также описывать переменные с помощью
общего атрибута.  Общая (SHARED) переменная - антипод локальной:
она "видима"  и  может  использоваться  всей  программой  (такие
перменные часто называются глобальными).

DEF FNDummy
  SHARED a
  a = 6
  FNDummy = a
END DEF
PRINT FNDummy, a

    Благодаря указанию  SHARED переменная a в FNDummy и перемен-
ная a в операторе PRINT одинаковы.
    Неописанные переменные внутри определений функций по умолча-
нию принимаются за общие;  например в функции AddReceipts, пока-
занной ранее,  массив  Receipts  принят  за общую переменную.
    В определениях процедур  по умолчанию массив  -  СТАТИЧЕСКИЙ
(STATIC). Мы настоятельно рекомендуем,  однако,  четко описывать
каждую переменную,  появляющуюся в опеределении функции или про-
цедуры.

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

SUB Dummy STATIC
  STATIC i
  i = i + 1
  PRINT i
END SUB

i = 16
CALL Dummy
CALL Dummy
PRINT i

    Статическая переменная  i  в Dummy отлична от переменной i в
"главной программе".  Однако, в отличие от локальной переменной,
она сохраняет  свое значение между вызовами содержащей ее проце-
дуры. Она начинается с 0,  как и любая другая переменная, и уве-
личивается дважды двойным вызовом Dummy.
    Правильное использование аргументов и  локальных  переменных
поможет сделать  процедуры  и  функции полностью независимыми от
программы, в которой они появляются.  Используя концепцию  ТУРБО
БЕЙСИКа "основной  файл / рабочий файл" и метаоператор $INCLUDE,
эти процедуры и функции можно без труда передавать в новые прог-
раммы.

Рекурсия
- - - - -
ТУРБО БЕЙСИК  обеспечивает рекурсию - процесс, с помощью которо-
го функция или процедура вызывает себя, прямо или косвенно. Поз-
накомьтесь с рекурсивным преобразованием FNFactorial:

DEF FNFactorial#(n%)
  IF n% > 1 AND n% < 170 THEN
    FNFactorial# = n% * FNFactorial#(n%-1)
  ELSEIF n% = 0 OR n% = 1 THEN
    FNFactorial# = 1
  ELSE
    FNFactorial# = -1
  END IF
END DEF

    Одно бросается  в  глаза сразу:  рекурсивный алгоритм короче
нерекурсивного. FNFactorial была сокращена до  одного  блока  IF
без локальных  переменных.  Краткость - характерная черта рекур-
сивных программ.
    Другая черта рекурсивного кода - кажущаяся сложность. Лучший
способ узнать рекурсивный алгоритм - проработать с  каранашом  и
бумагой несколько пробных программ.  Давайте определим факториал
3, мысленно пробежав по спирали, которую совершит программа ТУР-
БО БЕЙСИК, если ей задать оператор

PRINT FNFactorial#(3)

    Для начала аргумент 3 передается функции по значению. Первым
делом FNFactorial удостоверяется,  что этот аргумент больше 1  и
меньше или равен 170; 3 проходит это испытание, поэтому выполня-
ется оператор, управляемый первым предложением THEN:

FNFactorial# = 3 * FNFactorial#(2)

    Этой строкой имени функции присваивается  значение  3  (пока
все хорошо),  помноженное на значение FNFactorial(2),  каково бы
оно ни было.  Прежде чем присвоение состоится,  следует еще  раз
вызвать FNFactorial,  теперь уже с аргументом 2.  Отложим пока в
сторону этот оператор присвоения; вернемся к нему позднее.
    Второй вызов  FNFactorial  получает аргумент 2 и выполняется
нормально. И вновь она удостоверяется,  что аргумент больше 1  и
меньше или равен 170; мы вновь оказываемся у рекурсивного опера-
тора:

FNFactorial# = 2 * FNFactorial#(1)

    Отложите в сторону и этот  оператор  присвоения  и  вызовите
FNFactorial в третий раз, теперь с аргументом 1.
    FNFactorial(1) очень легко представить - он определяется как
1 во втором предложении THEN.  В этот раз,  наконец, FNFactorial
разрешено "отдать" значение,  и она отдает значение 1 последнему
отложенному вызову FNFactorial. Разрешено также закончить выпол-
нение его оператору присвоения:

FNFactorial# = 2 * 1

    Этим промежуточным вызовом  полной  FNFactorial  значение  2
возвращается исходному  вызову FNFactorial и первому оператору -
продолжению:

FNFactorial# = 3 * 2

    После этого последнего  присвоения  управление  возвращается
оператору PRINT и на экране появляется "6".
    Не существует однозначного ответа,  когда же следует  приме-
нять рекурсию,  но в общем, используйте ее, когда проблема носит
"рекурсивный" характер.  Факториалы, например, иногда рекурсивно
определяются в математических учебниках:

Для любого положительного целого числа n,
если n > 1,   то   n! = n * (n-1)!
            иначе  n! = 1

    Использование рекурсивного  алгоритма,  возможно,  потребует
увеличения исполнительного стека программы с помощью  метаопера-
тора $STACK, поскольку каждый уровень рекурсии может занимать до
125 байт (эта величина меняется).  Для  определения  оставшегося
свободного объема стека используйте функцию FRE(-1).

Файлы
-----------------------------------------------------------------
   После  создания  программы  или  подборки данных, к которым Вам
нужно будет возвращаться, следующий логический шаг - сохранить ее.
Сохраняя  данные, Вы создаете файл, который можно использовать для
ввода  и вывода информации. Можно создавать файлы трех типов: пос-
ледовательные, прямого доступа и двоичные. Ниже мы рассмотрим каж-
дый из этих типов, но вначале давайте рассмотрим как назвать файлы
и / или каталоги (содержащие несколько файлов или подкаталогов).
   Будучи  "гражданами" мира MS-DOS файлы, которые Вы создаете и с
которыми работаете используя ТБ, должны именоваться в соответствии
с  нормами  DOS.  Имена файлов состоят из двух частей, разделяемых
точкой:

filename.ext

где имя файла (filename) может включать от 1 до 8 знаков,  а ext -
(расширение) включает до трех знаков. Если в имени файла больше  8
знаков,  ТБ  автоматически усекает  имя и добавляет точку в конце.
Например, если ввести:

TESTINGDATA
оно превратится в
TESTINGD.

   Если строка содержит точку, разделяющую имя файла и расширение,
но имя файла содержит больше 8 знаков, ТБ сокращает имя до 8
знаков, а затем добавляет расширение, как, например:

VERYLONGFILENAME.TBS
становится
VERYLONG.TBS

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

TESTING.01/30/91
станет
TESTING.01/

   Имена файлов и расширения могут содержать следующие символы:

A-Z 0-9 () {} @ # $ % ^ ! - _ ' / ~

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

MYFIRST.TBS
MY1ST.TBS
MY_ABCS.(1)
10-25-51.@@@

   Кроме  операторов  для создания, считывания и записи файлов, ТБ
имеет средства для осуществления определенных DOS-подобных сервис-
ных  программ внутри программы. Оператор NAME переименовывает фай-
лы, KILL - стирает файлы, MKDIR - создает каталоги, CHDIR - меняет
текущий  каталог,  RMDIR  -  уничтожает каталоги. Нет команды COPY
(копировать);  вместо  нее  используйте метод двоичных файлов (или
используйте SHELL для вызова COMMAND.COM).
   Имена  файлов, используемые в операторах ТБ (например, KILL или
OPEN) должны быть в символьной форме, например:

KILL "myfile.bak"
или
a$ = "myfile.bak" : KILL a$
но не
KILL myfile.bak

   Более  подробно  о  файлах и каталогах расказано в Приложении G
"Основы DOS" и в Вашем справочнике по DOS.

Имена каталогов и маршрутов
- - - - - - - - - - - - - -
В  MS-DOS 2.0 и выше (ТБ и создаваемые им программы не будут рабо-
тать  в  более ранних версиях) форматирование гибкого или жесткого
диска создает каталог фиксированного размера, известный под именем
корневого. Элементами корневого каталога могут быть как файлы, так
и дополнительные каталоги (называемые подкаталогами).
   Как обычные файлы, подкаталоги имеют стандартные имена и расши-
рения,  могут разрастаться по мере необходимости. Подкаталоги сами
могут включать свои подкаталоги, организованные в систему файлов и
каталогов, напоминающую перевернутое дерево.
   На  вершине  дерева  файловой  системы  - корневой каталог, имя
которого  предопределено: обратная черта дроби (\). Файлы в дереве
определяются  указанием каталожной цепочки или пути доступа, веду-
щего к ним.
   Пути  доступа  образуют  перечислением  подкаталогов, ведущих к
файлу,  разделенных  обратной  чертой  дроби (\). Другими словами,
полное  описание файла CHESS.BAS в каталоге TURBO\BASIC\GAMES выг-
лядит так

буквенный индекс дисковода и двоеточие
¦
C:\TURBO\BASIC\GAMES\CHESS.BAS
  L--------T---------L-T-- LT-
           ¦           ¦    ¦
     путь поиска      имя  расширение
       каталога      файла

   Первая  обратная черта дроби (\) указывает, что маршрут начина-
ется из корневого каталога. Если имя маршрута начинается не с "\",
то оно описывает маршрут, принадлежащий текущему каталогу.  Напри-
мер, если текущий каталог - \TURBO\BASIC, то оба  следующих  имени
точно описывают положение CHESS.BAS:

GAMES\CHESS.BAS
или
\TURBO\BASIC\GAMES\CHESS.BAS

   В  любом подкаталоге, список файлов каталога из DOS или ТБ (ис-
пользуя  FILES) обязательно включает два особых файла: "." и "..".
Имя  файла "." указывает каталог, числящийся как подкаталог, а имя
".."  относится к родителю каталога. Например, чтобы сменить теку-
щий  каталог  на "родительский", воспользуйтесь оператором CHDIR с
особым именем ".."

CHDIR ".."


Способы сохранения файлов
- - - - - - - - - - - - -
ТБ предлагает три различных способа сохранения и востребования ин-
формации с лиска: последовательный, прямой и двоичный ввод / вывод
файла. У каждого есть свои преимущества и недостатки; какой из них
будет лучшим для Вас зависит от применения.
    Традиционная номенклатура программирования подразделяет  файлы
данных  на  отдельные  записи, каждая из которых состоит из одного
или  более  полей. Например, в программе "список адресов выпускни-
ков"  каждая  запись в файле данных представляет определенного че-
ловека.  Напимер,  штат  и почтовый индекс - это два поля, которые
можно  найти  в файле "список адресов выпускников". При распечатке
строки представляют записи, а колонки - поля.

-----------------------------------------------------------------
Имя          Адрес           Город        Штат Индекс Класс Вклад
-----------------------------------------------------------------
Кати Хервей  1010 В.Редвуд   Сайокс Сити  ИНД  51103   73    0.00
Эндрю Рич    37 Андерс.Роуд  Хьюстон      ТЕХ  77018   58  500.00
Лэрри Ирвинг 32110 Мэйн      Линдсбург    КАНЗ 67456   81    0.00
-----------------------------------------------------------------

   Этот  файл содержит три записи, в каждой по семь полей. По при-
чинам,  на которых мы бегло остановимся, разграничение обычно про-
изводится  между численными и строковыми полями. В данном примере,
последние три поля численные, а остальные - символьные.

Последовательные файлы
- - - - - - - - - - - -
Метод последовательных файлов - это способ прямого чтения и записи
файлов.  Команды  последовательных  файлов в ТУРБО БЕЙСИКе создают
текстовые  файлы: файлы ASCII-символов с парами "возврат каретки /
перевод строки", разделяющими записи.
   Вероятно  одной из основных причин использования последователь-
ных файлов является степень их "переносимости" в другие программы,
языки  программирования  и  компьютеры. По этой причине последова-
тельные  файлы часто можно рассматривать как общий знаменатель об-
работки  данных. Они читаются программами подготовки текстов и ре-
дакторами   (такими  как  в  ТУРБО  БЕЙСИК),  принимаются  другими
прикладными  пограммами  MS-DOS,  такими как управление базой дан-
ных,  и  могут  посылаться  через серийные порты на другие компью-
теры.
   В  основе последовательных файлов лежит сама простота: пишите в
них  так,  словно  они  - экран, и читайте с них так, словно они -
клавиатура.

   Создайте последовательный файл используя следующие шаги

   1. ОТКРЫТЬ  файл в режиме последовательного ВВОДА. Для создания
      файла в ТУРБО БЕЙСИКе необходимо использовать оператор OPEN.
      В  последовательных  файлах есть два пути подготовки файла к
      выводу:
      OUTPUT  (ВЫВОД):  Если  файл не существует - создается новый
      файл. Если файл уже существует, его содержание уничтожается,
      а сам файл рассматривается как новый.
      APPEND  (ДОБАВИТЬ):  Если файл существует - создается  новый
      файл.  Если  файл  уже существует, ТУРБО  БЕЙСИК  дописывает
      любые данные в конец этого файла.
   2. Вывод данных  в  файл.   Используйте  WRITE # , PRINT #  или
      PRINT # USING  для записи данных в последовательный файл.
   3. ЗАКРОЙТЕ  файл. Оператор CLOSE закрывает файловую переменную
      после завершения программой всех операций ввода/вывода.

   Для чтения последовательного файла

   1. ОТКРЫТЬ  файл  в режиме последовательного ВВОДА. Подготовить
      файл для считывания.
   2. Считывать  данные  с файла.  Использовать операторы INPUT #,
      INPUT$, или LINE INPUT #  ТУРБО БЕЙСИКа.
   3. ЗАКРЫТЬ  файл.  Оператор CLOSE закрывает файловую переменную
      после завершения программой всех операций ввода/вывода.

   Недостаток  последовательных  файлов в том, что возможен только
последовательный доступ к данным: за один шаг доступна только одна
запись,  начиная  с  первой.  Это  значит,  что,  если  необходимо
добраться  до последней записи в последовательном файле, состоящем
из  23000  записей,  придется  пройти  через  все предыдущие 22999
записей.
   Поэтому  последовательные  файлы  лучше всего использовать там,
где  нужна  последовательная  обработка  данных (например, подсчет
слов, проверка правописания, распечатка почтовых ярлыков в порядке
следования файлов), или где данные могут одновременно удерживаться
в  памяти.  Это позволяет Вам считывнать весь файл за один быстрый
прогон  с  начала  файла,  и вновь записывать его в конце.
   Последовательные  файлы  очень удобны в таких ситуациях с базой
данных,   когда   записи   имеют  меняющуюся  длину.  Предположим,
например,  что список адресов выпускников имеет поле "примечания".
У  некоторых  адресатов  примечания могут занимать до 100 байт или
более;  у  других,  скорее  даже у большинства, их не будет вовсе.
Последовательные  файлы  легко справляются с этой задачей не тратя
впустую пространство диска.
   Используя  ТУРБО  БЕЙСИК можно создавать последовательные файлы
двух  типов: (1) последовательные файлы с разделенными полями, где
все  поля  на  каждой  строке  файла  разделяются (ограничиваются)
особыми  символами,  и  (2)  неразделенные последовательные файлы,
когда  каждый  файл выглядит абсолютно одинаково и на экране, и на
распечатке.  Эти  два  типа  файлов создаются с помощью операторов
WRITE #  и PRINT #  соответственно.  (Используйте INPUT #, INPUT$,
или LINE INPUT #  для обратного считывания информации с последова-
тельного файла любого типа.)

Последовательные файлы с разделенными полями
- - - - - - - - - - - - - - - - - - - - - - -
Если  рассмотреть последовательный файл, созданный ТУРБО БЕЙСИКом,
Вы  увидите, что данные в файле разделены (разграничены) запятыми,
а  строки  заключены  в  кавычки  -  это форма, в которой оператор
INPUT # находит  данные.  (Кавычки,  в  которые  заключены строки,
исключают  путаницу с встроенными запятыми. Цифры не заключаются в
кавычки, так как записываются без запятых.)
   Рассмотрите и выполните следующую программу:

'Эта программа открывает последовательный файл для вывода. Она
'записывает  пару  строк  данных разного типа в файл используя
'оператор WRITE #.
OPEN "SEQUENTI.BAS" FOR OUTPUT AS #1         'назначить файловую
                                             'переменную #1
StringVariable$ = "This is a string of text" 'определить несколько
Integer% = 1000                              'переменных и
FloatingPoint! = 30000.1234                  'инициализировать их

'теперь запишите строку текста в последовательный файл
WRITE #1, StringVariable$, Integer%, FloatingPoint!

StringVariable$ = "Another String"
Integer% = -32767
FloatingPoint! = 12345.54321

'напишите еще одну строку текста
WRITE #1, Integer%, StringVariable$, FloatingPoint!

CLOSE #1                                     'закрыть файловую
                                             'переменную
END                                          'закончить программу

   Содержание файла SEQUENTI.BAS выглядит так:

"This is a string of text", 1000,30000.123046875
-32767, "Another String", 12345.54296875

   Здесь важно отметить, что оператор WRITE # выводит информацию в
последовательный  файл  в  точно такой форме, в какой рассчитывает
найти ее оператор INPUT # .
   Следующая  программа считывает последовательный файл, созданный
программой из последнего примера:

'Данная программа открывает последовательный файл для ввода. Она
'считывает пару строк данных разного типа из файла используя
'оператор INPUT # .

OPEN "SEQUENTI,BAS" FOR INPUT AS #1          'назначить файловую
                                             'переменную #1
StringVariable$ = ""                         'определить несколько
Integer% = 0                                 'переменных и
FloatingPoint! = 0                           'инициализировать их

'теперь считайте строку текста из последовательного текста
INPUT #1, StringVariable$, Integer%, FloatingPoint!
PRINT StringVariable$, Integer%, FloatingPoint!
StringVariable$ = ""
Integer% = 0
FloatingPoint! = 0

'считайте еще одну строку текста
INPUT #1, Integer%, StringVariable$, FloatingPoint!
PRINT Integer%, StringVariable$, FloatingPoint!

CLOSE #1                                     'закрыть файловую
                                             'переменную
END                                          'закончить программу

   Важно  помнить, что программы, показанные в предыдущих примерах
не  будут  правильно  работать,  если для создания файла данных Вы
используете  оператор  PRINT # . Для создания файла, который будет
читаться  оператором INPUT #,  используйте не оператор PRINT # , a
оператор WRITE # .

Неразделенные последовательные файлы
- - - - - - - - - - - - - - - - - - -
В  неразделенных последовательных файлах данные выглядят одинаково
и  при  выводе  на  экран  оператором  PRINT  и  при распечатке на
принтере   с   помощью   оператора   LPRINT.   Можно  использовать
произвольный    параметр    USING   для   обеспечения   вывода   в
последовательный  файл,  точно также, как Вы бы сделали для вывода
на экран или принтер.

   Рассмотрите и выполните следующую программу:

'Эта программа открывает последовательный файл для ввода. Она
'записывает пару строк данных разного типа в файл используя
'операторы PRINT # и PRINT # USING

OPEN "SEQUENTI.BAS" FOR OUTPUT AS #1         'назначить файловую
                                             'переменную #1

StringVariable$ = "This is a string of text" 'определить несколько
Integer% = 1000                              'переменных и
FloatingPoint! = 30000.1234                  'инициализировать их

'теперь запишите строку текста в последовательный файл
PRINT #1, StringVariable$, Integer%, FloatingPoint!

StringVariable$ = "Another String"
Integer% = -32767
FloatingPoint! = 12345.54321

'напишите еще одну строку текста, но форматируйте ее с помощью
'USING
PRINT #1, USING "+##### & ##.##^^^^;_
           Integer%, StringVariable$,FloatingPoint!

CLOSE #1                                     'закрыть файловую
                                             'переменную
END                                          'закончить программу

           Содержание файла SEQUENTI.BAS выглядит так:

This is a string of text     1000         30000.123046875
-32767  Another String  12.35E+03

   Как  и  в  файлах  с  разделенными полями, здесь важно отметить
формат  данных  и  каким  образом  их  можно  отсчитывать. Если Вы
попытаетесь использовать  тот  же  оператор INPUT #, как во втором
примере  программы,  ТУРБО БЕЙСИК  выдаст "ошибку при выполнении",
поскольку  прочитает  слово  "This" в первой строке как символьную
переменную  и  попытается  прочитать  следующие  два слова как две
численные  переменные. Поэтому нужно использовать операторы INPUT$
или LINE INPUT # для чтения этой информации.
   Следующая  программа считывает последовательный файл, созданный
программой из последнего примера:

'Данная программа открывает последовательный файл для ввода. Она
'считывает пару строк данных разного типа из файла используя
'операторы LINE INPUT# или INPUT$.

OPEN "SEQUENTI.BAS" FOR OUTPUT AS #1          'назначить файловую
                                              'переменную #1
StringVariable$ = ""

'теперь считайте 80-знаковую строку текста из последнего файла
StringVariable$ = INPUT$(80,1)
PRINT StringVariable$

'введите всю строку ввода независимо от ее длины
LINE INPUT# 1, StringVariable$

PRINT StringVariable$

CLOSE #1                                     'закрыть файловую
                                             'переменную
END                                          'закончить программу

  Ниже  приведены  операторы и функции ТУРБО БЕЙСИКа, управляющие
вводом/выводом последовательных файлов:
-----------------------------------------------------------------
Оператор/Функция           Операция
-----------------------------------------------------------------

CLOSE                      Прекращает работу с файлами
EOF                        Дает сигнал по достижении конца файла
INPUT #                    Считывает запись (строку текста) в
                           указанную(ые) переменную(ые)
INPUT$                     Считывает n знаков в символьную перем.
LINE INPUT #               Считывает всю строку в одну символьную
                           переменную
OPEN                       Открывает файл для режимов INPUT,
                           OUTPUT, или APPEND, и присваивает ему
                           номер
PRINT #, PRINT # USING     Пишет в файл как на принтер или экран
WRITE                      Пишет разделенные запятыми данные в
                           файл

-----------------------------------------------------------------

Файлы прямого доступа
- - - - - - - - - - -
Файлы прямого доступа состоят из записей, доступ к  которым возмо-
жен в любой последовательности. Это означает, что данные  хранятся
в том виде, в каком они поступают в память;  таким образом  эконо-
мится время на обработку (поскольку не нужна трансляция)  как  при
записи, так и при чтении файла.
   Вообще, файл прямого доступа более удобен для решения различных
проблем  базы  данных,  чем  последовательный  файл,  хотя и он не
свободен  от недостатков. Во-первых, файл прямого доступа не очень
хорошо "переносим". Нельзя заглянуть внутрь такого файла с помощью
редактора  или показать его в читаемом виде на экране. Фактически,
для  перевода  файла  прямого  доступа  в  другой компьютер или на
другой    язык,   вероятно,   потребуется   написать   специальную
программу-транслятор для считывания файла прямого доступа и вывода
текстового  (последовательного)  файла.  Один из примеров проблемы
"переносимости"   наиболее   впечатляющ.  Поскольку  Интерпритатор
БЕЙСИК   использует   нестандартный  формат  фирмы  Microsoft  для
значений  с  плавающей  запятой,  а  в ТУРБО БЕЙСИКе для этой цели
используются  символы  по стандарту  ИИЭР (Институт  инженеров  по
электротехнике и радиоэлектронике), невозможно читать поля с  пла-
вающей  запятой  файлов  прямого  доступа  Интерпретатор  БЕЙСИК с
помощью  программ  ТУРБО  БЕЙСИКа,  и наоборот, без дополнительной
проработки.
   Мы  предлагаем  четыре  специальных  функции  для решения этого
ребуса: CVMS и CVMD для превращения численных полей Microsoft-фор-
матов  в  обычне  переменные  ТУРБО БЕЙСИКа  с одинарной и двойной
точностью;  а  также  MKMS$  и  MKMD$ для превращения переменных с
одинарной и двойной точностью в строки Microsoft-форматов, которые
можно затем записать в файл.
   Основное   преимущество  файлов  прямого  доступа  ясно  из  их
названия:  любая запись файла доступна в любой момент. Например, в
базе  данных  о  23000  выпускников программа может прямо выйти на
запись  22709  или 11663 без чтения других записей. Такое свойство
делает  эти  программы  наиболее  пригодными  при создании больших
файлов,   и  предпочтительными  при  создании  малых,  особенно  с
довольно длинными записями.
   Файлы  прямого  доступа занимают довольно много места на диске,
поскольку   каждой   записи   выделяется   место  для  максимально
возможного  размера  поля.  Например, включение 100-байтового поля
примечаний  вызывает  увеличение  каждой  записи на 100 байт, даже
если оно использовано в одной записи из тысячи.
   Но  с  другой  стороны, если записи довольно велики и состоят в
основном из чисел,  файлы  прямого  доступа занимают меньше места:
каждое число одинакового типа (целое, данное  целое,  с  одинарной
точностью,  с  двойной  точностью)  занимает  на  диске одинаковое
пространство.  Например,  каждое  следующее  значение  с одинарной
точностью  занимает  4 байта (такое же пространство они занимают в
памяти):

0
1.660565E-27
15000.1
641
623000000

   В  последовательных  файлах,  напротив,  числа занимают столько
байт,  сколько  в  них  ASCII  символов  при  печати плюс один для
разделительной запятой, например:

WRITE #1, 0;0           -  занимает 3 байта,
PRINT #1, 0;0           -  занимает 6 байт, а
PRINT #1, .660565E-27   -  занимает 13 байт.

   Та   незначительная  "плата"  за  преимущество  файлов  прямого
доступа  заключается  в  небольшом  увеличении  объема  обработки,
необходимой  для  ввода/вывода строк и чисел в/из формы, в которой
ими иожет оперировать программа файлов прямого доступа.

   Вы  можете  создавать,  записывать  и  считывать  файлы прямого
доступа следующим образом:

   1. ОТКРЫТЬ файлы и определить величину каждой записи.

      OPEN filespec AS [#]fillenum LEN = record.size

      Параметр LEN показывает ТУРБО БЕЙСИКу, что этот файл прямого
      доступа. В отличии от последовательного файла здесь не нужно
      указывать для чего открывается файл - ввода или вывода - так
      как можно одновременно читать/писать в файл прямого доступа.

   2. Выполнить оператор FIELD для определения соответствия  между
      сериями символьных переменных (после этой операции они  ста-
      новятся "переменными полями") и "буфером файла".

      FIELD filenum, width AS string-var [width AS string-var]...

      Этот буфер является загрузочным деком данных, которые  будут
      считаны или записаны именно в этот файл. Оператор FIELD хотя
      бы один раз должен быть выполнен для данного  файла  прямого
      доступа.

   3. При записи в файл используйте LSET и RSET для загрузки пере-
      менных поля данными для записи. Числа должны быть превращены
      в строковую фарму соответствующей фаункцией "make"(например,
      MKS$ для величины с одинарной точностью) прежде чем  исполь-
      зовать LSET и RSET. И наконец, используйте PUT для  исполне-
      ния записи в файл в указанном месте.

   4. При считывании записи с файла используйте оператор  GET  для
      считывания нужной записи. Затем загрузите строковые  и  чис-
      ленные переменные Вашей программы из соответствующих  буфер-
      ных переменных. Прежде чем Вы сможете оперировать численными
      данными их нужно превратить из символьной  формы  с  помощью
      одной из функций "превращения" (например, CVS для величин  с
      одинарной точностью).

   5. После завершения работы ЗАКРЫТЬ файл.

   Ниже перечислены операторы и функции ТУРБО БЕЙСИКа, управляющие
считыванием и записью в файле прямого доступа.
------------------------------------------------------------------
Оператор/Функция            Операция
------------------------------------------------------------------

CLOSE                       Закрыть файл
CVI, CVL, CVS, CVD          Превращает переменную поля в соответ-
                            ствующий численный тип
FIELD                       Определяет переменные поля
GET                         Считывает запись
LOC                         Определяет # последней читаемой записи
LSET, RSET                  Назначается переменной поля
MKI$, MKL$, MKS$, MKD$      Превращает определенные типы численных
                            величин в форму, которая может быть
                            назначена переменной поля
MKMS$, MKMD$, CVMS, CVMD    Функции-трансляторы форматов Microsoft
OPEN                        Отрывает файл для прямого доступа
PUT                         Осуществляет запись
-----------------------------------------------------------------

   А вот образец программы, изпользующий файлы прямого доступа:

'программа для чтения базы данных "список рассылки"
'пользователь вводит номер записи; программа показывает
'все поля данной записи.
'эта программа не может читать вашеописанные последовательные
'файлы.
OPEN "ADDRESS.DTA" AS #1 LEN = 81    ' "LEN = 81" показывает - это
                                     ' файлы прямого доступа
'определите расположение записей файла #1
FIELD #1, 25 AS FileName$, 25 AS address$,_
          15 AS city$, 2 AS state$,_
           4 AS zip$, 2 AS class$, 8 AS contrib$

'zip$ длинное целое число, class$ целое число, contrib$ действи-
'тельное двойной точности,
'все они существуют в файле как строки.
'При считывании этих численных полей необходимо вначале превращать
'их обратно в числа.
INPUT "Which record do you want to see:", recnamber
GET #1, recnamber
' данные теперь в буфере файла
PRINT "Data for record" recnamber
PRINT "Name:" FileName$
PRINT "Address: " address$
PRINT "City/State/Zip: " city$, state$,CVL(zip$)
PRINT "Class: " CVI(class$)
PRINT "Most recent contrib:" CVL(contrib$)

   Внимание:  Никогда  не  присваивайте  переменной  поля; т.е. не
используйте  переменную  поля  в левой части оператора присвоения.
Присвоение  переменной  поля  отрывает  переменную от назначенного
буфера. Например, после

zip$ = a$

переменная  поля zip$ уже не относится к буферу, которому она была
присвоена  в  операторе FIELD. Использование LSET и RSET не вернет
ее.

Двоичные файлы
- - - - - - - -
Метод  двоичных  файлов  в  ТУРБО  БЕЙСИКе - это усовершнствование
Интерпретируемого  БЕЙСИКа,  позволяющие  относится к любому файлу
как  к нумерованной последовательности байтов, независимо от ASCII
символов,  противопоставления  численный-строковый,  длины записи,
символов  "возврат  каретки",  и  т.п.  При  использовании  метода
двоичных  файлов  считывание  и запись в файл производится простым
указанием  какие байты записать, и где расположить их в файле. Это
очень  похоже  на  системные  вызовы  DOS  (используемые  в  языке
ассемблера) для чтения и записи файлов.
   Но,  за  гибкость  всегда  приходится  платить.  ДВОИЧНЫЙ режим
работы  с  файлами предпологает, что Вы сами принимаете решения по
расположению  данных.  Это  вероятно наиболее подходящий режим для
работы   с   "чужими"  файлами,  использующими  не  ASCII  формат,
например,  dBASE  или  Lotus  1-2-3  файлы. Разумеется, необходимо
точно  знать  структура  файла, прежде чем начать разбивать его на
числа и строки, подходящие для ТУРБО БЕЙСИКа.
   Любой   файл,   открываемый   в   ДВОЙЧНОМ   режиме,   содержит
соответствующий  указатель  позиции,  показывающий  в файле место,
которое будет считываться или записываться следующим. Используется
оператор SEEK для установки положения указателя, а функцию LOC для
чтения.

   Доступ в двоичный файл осуществляется следующим образом:

   1. ОТКРЫТЬ файл в ДВОИЧНОМ режиме. Не нужно указывать считывае-
      те Вы или записываете - можно делать и то, и другое одновре-
      менно.

   2. При чтении файла используйте SEEK  для  установки  файлового
      указателя на желаемый байт. Затем с помощью GET считайте оп-
      ределенное количество знаков (от 1  до 32767)  в  символьную
      переменную.

   3. При записи в файл загрузите символьную переменную информаци-
      ей, которую хотите записать.  Затем с помощью  PUT$  укажите
      строковые данные и место в файле, куда она должна быть запи-
      сана.

   4. Окончив работу ЗАКРЫТЬ файл.

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

CLOSE                       Закрывает файл
GET$                        Считывает определенное количество
                            знаков начиная от указателя
LOC                         Опредегяет положение указателя в
                            файле
LOF                         Показывает длину файла
OPEN                        Открывает файл и определяет его как
                            двоичный
PUT$                        Записывает определенное количество
                            байт, начиная от указателя
SEEK                        Перемещает указатель
-----------------------------------------------------------------

Устройства ввода/вывода
- - - - - - - - - - - -
ТУРБО  БЕЙСИК  обеспечивает  так  называемые файлы устройств; т.е.
обеспечивает аппаратные средства - такие как клавиатура, дисплей и
принтер   -  как  последовательные  файлы.  Каждая  обеспечиваемое
устройство  имеет  резервированное  файловое  имя,  оканчивающееся
двоеточием:
-----------------------------------------------------------------
Имя                Функция
-----------------------------------------------------------------

KYBD:              Клавиатура может быть открыта для ввода. Считы-
                   вание  файла  "KYBD:"  сходно  с использованием
                   INKEY$.
SCRN:              Экран  может  быть открыт для  вывода. Запись в
                   файл устройства "SCRN:" сходно с использованием
                   PRINT.
LPT1-3             Линейные принтеры от 1 до 3.
COM1-2             Комуникационные порты от 1 до 2.
-----------------------------------------------------------------

   Например:

OPEN "SCRN:" FOR OUTPUT AS #1, "Hello"
производит такой же эффект как

PRINT "Hello"

а

OPEN "KYBD:" FOR INPUT AS #1, a$, b$
то же, что

INPUT a$,I b$

Дисплей
------------------------------------------------------------------
Когда дело доходит до отображения информации на экране, программа,
генерируемая  ТУРБО  БЕЙСИК  должна  работать в рамках аппаратного
обеспечения  имеющихся дисплеев. ТУРБО БЕЙСИК обеспечивает следую-
щие типы видео-интерфейсов на ИБМ-совместимых персональных компью-
терах: Монохромный дисплейный адаптер, воспроизводящий только один
цвет;  Цветной графический адаптер (CGA = Color Graphics Adapter);
Усовершенствованный   графический   адаптер  (EGA  =  IBM Enhanced
Graphics Adapter), способные воспроизводить  графику  и тексты как
цветные, так и черно-белые;  Многоцветный графический адаптер мас-
сивов  (MCGA = Multi-Colors Graphics Array Adapter),  работающй на
IBM PS/2 Модель 30;  и  Графический  видео-адаптер  (VGA  =  Video
Graphics Array Adapter),  работающий  на IBM PS/2 Модели 30, 60, и
80 (Таблица 4-5).

        Таблица 4-5     Характеристики графических адаптеров
------------------------------------------------------------------
Адаптер  Режим экрана  Формат текста         Примечания
------------------------------------------------------------------
Моно     0             80 знаков     80*25  знаков, с 4 атрибутами
                                     (нормальный, подсветкка,  ми-
                                     гающий и подчеркивание.)

CGA      0,1,2         40/80 знаков  До 16 цветов ( только в текс-
                                     товом режиме); до 4 - в  гра-
                                     фическом. Разрешение  экрана:
                                     640*200.

EGA      0,1,2,7,8,    40/80 знаков  Разрешение  экрана:  640*350.
         9,10                        До  16  цветов  из 64-цветной
                                     палитры   (в  зависимости  от
                                     памяти).

MCGA     0,1,2,11      40/80 знаков  Разрешение  экрана:  640*480.
                                     Черно-белый в режиме 11.

VGA      0,1,2,7,8,    40/80 знаков  EGA  и  MCGA   с  разрешением
         9,10,11,12                  экрана 640*480; до 16  цветов
                                     в режиме 12.
------------------------------------------------------------------
   Соответственно  уровню  этих видео-устройств ТУРБО БЕЙСИК может
выбрать любой текстовый или графический режим с помощью операторов
SCREEN и/или WIDTH.

Текстовые режимы
- - - - - - - - -
В   80-знаковом   текстовом   режиме   (единственно   возможном  в
Монохромном дисплейном адаптере), экран состоит из 25 строк, по 80
знаков  в  каждой, пронумерованных от 1 до 25 (сверху вниз) и от 1
до 80 (слева направо). В 40-знаковом текстовом режиме (в адаптерах
CGA/EGA) экран состоит из 25 строк по 40 знаков. Знаковая  позиция
в каждой строке может быть занята любым символом из списка, приве-
денного в приложении F.
   В  общем,  знаки  помещаются  на  экран передвижением курсора в
желаемое   место  с  помощью  оператора  LOCATE,  a  затем  PRINT.
Экзотические  символы,  которые  трудно генерировать с клавиатуры,
можно сделать с помощью функции CHR$ (См. Приложение F).
   25-ая (нижняя)  строка экрана заслуживает особого рассмотрения:
оператор  KEY  загружает  ее  описанием   функциональных  клавишей
("программируемых клавишей"). Для защиты этих описаний, 25-я стро-
ка никогда не "прокручивается"  и  в нее  ничего  не пишется, если
только не выключить "дисплей клавишей функций" с помощью KEY OFF.
    В монохромных адаптерах, оператор COLOR можно использовать для
создания  спецэффектов,  например, негативного или "приглушенного"
изображения,  подчеркивания  и/или  мигания  текста.  В  адаптерах
CGA/EGA  оператор  COLOR  используется  для  выбора текущего цвета
отображаемых данных, выводимвх оператором PRINT.
   В  текстовом  режиме  адаптеры  CGA/EGA  отображают  до  восьми
дисплейных  "страниц";  обрабатываемая и/или отображаемая страница
задается оператором SCREEN.

Графические режимы
- - - - - - - - - -
ТУРБО  БЕЙСИК  содержит полный набор команд для построения цветных
точечных  и линейных графиков, графических символов на экране. Но,
если у Вас нет соответствующего аппаратного  дисплейного обеспече-
ния, такого как CGA/EGA или аналогичного, все эти команды не будут
функционировать правильно. (Фактически, попытка использовать  гра-
фические команды на "только-текстовом" оборудовании вызывает появ-
ление сообщения об ошибке "Неверный Вызов Функции".)
   В   графических  режимах  экран  рассматривается  как  матрица,
состоящая из  растровых элементов, каждый из  которых  может  быть
включен  (белый или иной цвет), или выключен  (цвет  фона,  обычно
черный).  Точки  определяются по их положению (X,Y) в матрице; ис-
ходное  положение по умолчанию - левый верхний угол экрана. Значе-
ние X представляют расстояние по горизонтальной оси от левого края
экрана, а Y - расстояние по вертикали от верхней  границы  экрана.
Заметьте, что данное обозначение отличается от обычных  декартовых
координат, гда значение уменьшается по оси сверху вниз.
   В  зависимости  от  избранного  режима  (и, вероятно, от объема
"видео"-памяти),  разрешение по горизонтали может быть 320 или 640
точек, а по  вертикали  -  200  или 350. Эта комбинация определяет
также и максимальное число возможных цветов (2/4/16), равно  как и
максимальное  число  видео-"страниц" (1/2/4/8).  Отсчет  растровых
точек начинается с нуля, в отличие  от счета  текстовых  строк или
знаков (колонок), который начинается с единицы.
    Каждая растровая точка имеет соответствующее цветовое значение
(или   атрибут),   от  0  до  максимального  для  данного  режима.
Результирующий  цвет  для каждого значения управляется операторами
SOLOR и PALETTE.

Последнее указание точки
- - - - - - - - - - - - -
После большинства графических операций изображаемая точка привязы-
вается  к (X,Y) координатам  на  экране.  Это положение называется
последним указанием точки или ПУТ (LPR). При первом  вызове графи-
ческого  режима  оператором SCREEN,  ПУТ  устанавливается в центре
экрана.
   Некоторые  графические  команды  способны  воспринимать ПУТ как
координатный  аргумент.  Например,  если  оператором  LINE  задана
только  одна  точка  назначения,  то  линия проводится между ПУТ и
указанной   точкой.   В   дальнейшем  ПУТ  будет  являться  точкой
назначения.

Абсолютные и относительные координаты
- - - - - - - - - - - - - - - - - - -
Большинство  графопостроительных  операторов  ТУРБО  БЕЙСИКа могут
давать координаты как в относительной, так и в абсолютной форме.
   В  абсолютной  форме  координатная пара (X,Y) точно определяет,
где произвести операцию; например, PSET (50,75) включает растровую
точку  на  расстоянии  50  точек  от левого края экрана и 75 точек
сверху.
   В относительной форме координата определяется относительно ПУТ,
и  представляет  собой  величину горизонтального или вертикального
смещения  относительно  ПУТ.  В  этой  форме использовано ключевое
слово  STEP  для того, чтобы отличить ее от стандартных абсолютных
координат. Например, если ПУТ (60,75):

PSET (10,20)

определяет точку (10,20), то

PSET STEP (10,20)

определяет  точку  (70,95),  на  10  правее  и  на  20  ниже  ПУТ.
Отрицательное  относительное  значение координаты определяет точку
выше и левее ПУТ.

Смена координат экрана
- - - - - - - - - - - -
Если Вас не устраивает такая система адресации, используйте оператор
WINDOW для смены управления соответствием между парами (X,Y) данными
в графопостроительных операторах и точками на экране.
Например:

WINDOW (-1,1)-(1-1)

меняет  координаты  экрана,  приближая  их  к декартовой плоскости
аналитической   геометрии,   с   начальным   адресом  посредине  и
возрастанием  значения  у  вверх  по  оси.  После  этого оператора
WINDOW, PSET (0,0) включает  физическую растровую точку (100,160),
PSET (-1,-1)  -  крайнюю  левую   точку   в   нижней   строке,   а
PSET (0.5,0.5) - физическую растровую точку (220,50) в центре пра-
вого верхнего квадрата.
   Оператор  VIEW  позворляет  Вам  создавать  активную  (текущую)
облать, или "окно", на графическом дисплее, и по выбору определять
координаты, соответствующие верхнему левому углу окна. Оперировать
точками вне окна невозможно.