Главная » Basic » МОДУЛЬНОЕ ПРОГРАММИРОВАНИЕ

0

Предыдущие главы содержали введение в программирование и описание большинства свойств языка программирования  Бейсик,  знание  которых  достаточно  для  разработки  и  написания   простых программ. В гл. 3 были представлены структограммы, являющиеся превосходным способом наглядного представления эффекта комбинирования небольших групп операторов Бейсика. В сочетании с методом пошаговой детализации, представленным в разд. 3.6, структограммы могут служить также полезным средством разработки программы.

Однако  проблема  разработки  законченной  программы  для   выполнения  достаточно   сложных действий пока еще не обсуждалась. Попытка разработать программу, не отрываясь от работы за ВТУ, становится более или менее бесплодной, как только программа перестает  помещаться на экране дисплея (от 20 до 40 строк) , так как мы склонны забывать то, что уже  было написано, и терять управление по мере усложнения ситуации.

По  методу  пошаговой  детализации  предлагается  делить  задачу  на  небольшие  и  относительно независимые части, решение которых можно разрабатывать отдельно. Можно ли провести параллель между  такими частями, или  модулями, и  элементами языков  программирования? Да,  их  можно ассоциировать с функциями и подпрограммами, которые  может определить сам программист. К сожалению,  Бейсик   недостаточно  полно   реализует   все   те   требования,  которые  необходимо выполнить  для  полного  обеспечения  модульности.  Тем  не  менее  Бейсик  предоставляет  вполне приемлемые  средства  модульного  программирования. В  некоторых  расширениях  Бейсика  предпринимаются попытки тем  или  иным  путем  устранить упомянутые недостатки; в  последующих разделах будут описаны как  эти расширения, так и стандартные возможности создания функций и подпрограмм.

5.1.  ФУНКЦИИ, ОПРЕДЕЛЯЕМЫЕ ПОЛЬЗОВАТЕЛЕМ

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

20   Y=5

30   Z = SQR(Y*Y+ 2*Y) или

10   PRINT SQR (981+Z)

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

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

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

5.1.1.  ОПЕРАТОР DEF FN

Если  повторяющиеся действия  можно  выразить  в  виде  одного  оператора  Бейсика,  результатом исполнения которого является единственное значение (наподобие квадратного  корня), то для их выполнения можно определить

функцию.

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

Рассмотрим   задачу   вычисления   чистого   дохода   индивидуума   при   условии,   что   сумма,   не превышающая 1200 фунтов стерлингов, налогом не облагается, а за каждый фунт сверх этой суммы взимается налог в 35 пенсов:

10 REM ЗАДАЧА ПОДСЧЕТА ПРОСТОГО НАЛОГА

20 REM G=ОБЩИЙ ДОХОД

30 REM N=ЧИСТЫЙ ДОХОД ПОСЛЕ УПЛАТЫ НАЛОГА

40 DEF FNT(A)=A-(A-1200)*35/100

50 REM

60 INPUT "ВВЕДИТЕ ОБЩИЙ ДОХОД";G

70 IF G<=1200 THEN 100

80   PRINT "ОБЩАЯ СУММА =";G;"4MCTAH ПРИБЫЛЬ =";FNT(G)

90 STOP 100   PRINT "ВЫЧЕТОВ НЕТ. ПРИБЫЛЬ=";G

110 END RUN

ВВЕДИТЕ ОБЩИЙ ДОХОД ? 4500

ОБЩАЯ СУМКА=4580 ЧИСТАЯ ПРИБЫЛЬ=3345

END AT LINE 90

Всякий раз, когда в строке 80 встречается ссылка на функцию FNT(G), вместо нее  используется выражение, указанное в операторе DEF FNT в строке 40; при этом в  выражение  подставляется текущее значение G. При вычислении функции значение G не изменяется.

Переменная А, указанная в определении функции FNT (строка 40 приведенной выше программы) в скобках после имени функции, называется формальным параметром. Когда функция используется в строке  80,  то  переменная G  выступает как  фактический параметр,  предоставляющий функции конкретное входное значение.

Оператор DEF FN

Общая форма записи: DEF FNx = выражение или

DEF FNx (у) = выражение

где х имя функции, обычно ограничиваемое одной буквой; у — простая переменная,  называемая параметром, или список параметров, в котором переменные указаны через  запятую.  Параметры локальны по отношению к определению функции (разъяснение приводится ниже). Функция может быть  определена  для  работы  со  строками  символов  или  числами,  и  после  имени  функции  и переменных при необходимости указываются соответствующие признаки типа значения {$, %, #). Примечание.  Некоторые  системы,  например  Sinclair  ZX81,   не   обеспечивают  возможность определения пользователем функций; другие системы ограничивают применение функций, допуская в  их  определении только  числовые значения; во  многих  системах, допускающих длинные имена переменных, например в системах Microsoft и ВВС, допускаются и длинные имена функций; полное определение функции должно умещаться в одной строке; по поводу блочных функций см. подразд. 5.1.2.

Приведем примеры однострочных функций: DEF FNP                =3.14159

DEF FNA (S)          = S*S+4

DEF FNP (Q)          =3*B + Z*Q

DEF FNZ(Q)          =SQR(13.7*Q + A)

DEF FNA (A,B,C)   = (A + В + С)/3

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

10 REM ПРИМЕР ОПЕРАТОРА FN

20 А=2

30 В=30

40 С=-21

50 REM

60 DEF FNX(A)=A*B+C

70 REM

80 PRINT "А =";А; В =";В; "С =";С

90 PRINT "ЗНАЧЕНИЕ 6*В+С =";FNX(6)

100 PRINT "A=";А;"В=";В;"С=";С

110 END

Исполнение строки 60 не вызывает каких-либо действий, помимо регистрации определения функции, используемой в строке 90 со значением 6. Переменная А в строке 60 является локальной (фиктивной) и обязана отличаться от переменной А, не входящей в определение функции. При распечатывании значения последней переменной до и после обращения к переменной должно изображаться одно и то же значение.

В состав указанного в определении функции выражения могут входить другие функции при условии, что все эти функции пользователя уже были определены. Иначе говоря, в него могут входить либо встроенные функции, либо функции, появившиеся в программе ранее. Например, функция

20   DEF FNR(X) =INT(X * RND(l) + 1)

возвращает значение случайного целого числа в диапазоне 1 . . . X, так что при исполнении цикла

30   FORI=1 TO 10

40      PRINT FNR (6)

50   NEXT I будет напечатано десять случайных значений в диапазоне 1 … 6.

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

5.1.2.  БЛОЧНЫЕ ФУНКЦИИ

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

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

Система  с  Бейсиком  для  ЭВМ  ICL  2903/4  допускает  включение  в  образующий  функцию  блок операторов любого типа; блок завершается новым оператором FNEND.  Определение  локальных переменных  помещается  в  строке  с  оператором  DEF  FNx  после  скобки,  завершающей  список параметров.  Приведенный  ниже  пример  иллюстрирует  блочную  функцию,  вычисляющую  N!≡N факториал, где

N!≡N*(N-1)*(N-2)* …*3*2*1 так что

3! =3 * 2* 1                                       5!=5*4*3*2*1 142

10 REM ПРИМЕР БЛОЧНОЙ ФУНКЦИИ

20 DEF FNF(N) T, I

30 T=I

40 FOR I=1 TO N

50  T=T*I

60 NEXT I

70 FNF=T

80 FNEND

90 REM

100 PRINT "3! =";FNF(3)

110 PRINT "2! =";FNF(2)

120 PRINT "5! =";FNF(5)

130 END RUN 3!=6

2!=2

5!=120

END AT LINE 130

Образующий функцию блок простирается от  строки 20  до  строки 80  (включительно) ,  а  Т  и  I

являются дополнительными локальными переменными.

В Бейсике ICL 2903/4 можно определять рекурсивные функции. Рекурсия в данном случае означает, что функция может вызывать сама себя. Это свойство языка программирования полезно, но ценность его часто преувеличивается. Конструирование рекурсивных функций требует большого мастерства, но  при  исполнении программы может  оказаться, что  их  применение  неэффективно и  вызывает затруднения.  Приведенную  выше  программу  вычисления   факториала  можно  написать  в  виде рекурсивной функции:

10 REM ПРИМЕР РЕКУРСИВНОЙ ФУНКЦИИ

20 DEF FNF(N)

30 IF N=1 THEN 60

40   FNF=N*FNF(N-1)

50   GOTO 70

60 FNF=1

70 FNEND

В этом примере значения локальной переменной N  образуют стек. Каждый раз, когда  функция вызывается из самой себя, на верх стека помещается новое значение. Вызов FNF(3) приводит к тому, что N полагается равным 3 и управление передается функции FNF. При ее исполнении в строке 40 происходит вызов FNF(2), в результате чего значение 3 помещается в стек. Вызов FNF(2) приводит к тому, что N  полагается равным 2,  и  при исполнении строки 40  при  данном вызове значение 2 помещается в стек значений N и происходит вызов FNF(1),B  результате которого только всего и

происходит, что возвращается значение FNF, равное 1. Теперь можно по очереди исполнить  все предыдущие вызовы: каждый раз снимается верхний элемент стека значений N и  умножается на текущее значение FNF. Последовательность возвратов результатов такова:

FNF=1              влечет за собой FNF(1) =1,

затем 2*FNF (1) =2*1 влечет за собой FNF (2) = 2, затем 3*FNF (2) =3*2 влечет за собой FNF (3) = 6 143

Конечный результат — правильное значение 3!.

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

20   DEFFNF(X) =Х*Х+3*Х-20     . 10   DEF FNS (X) = SQRT (X+В-С)

Если определение функции занимает несколько строк, то знак присваивания (=), задающий значение функции в Бейсике ВВС, может встречаться в нем один или несколько раз. Например,  следующая функция возвращает в качестве результата наибольшее из двух значений:

100   DEF FNZ(Q,R)

110   IF Q>R THEN = Q ELSE = R;

Таким образом, при исполнении оператора 10   PRINT FNZ (2,3)

будет получен результат 3. Повторим пример функции для вычисления факториала: 10 REM ПРИМЕР ФУНКЦИИ В БЕЙСИКЕ ВВС

20 DEF FNFACT(N)

30 LOCAL T,I

40 T=1

50 FOR I=1 ТО N

60   Т=Т*I

7В NEXT I

80 =Т

Знак присваивания в строке 80 служит признаком завершения определения функции и дает значение функции FNFACT ( ) .

В  Бейсике  ВВС  функции  могут  быть  рекурсивными,  поэтому  в  случае  применения   признака завершения определения (=)  можно  переписать и  рекурсивный пример  вычисления  факториала, воспользовавшись   к   тому   же   возможность   дать   функции   более   полное   имя.   Употребляя расширенный вариант оператора IF Бейсика ВВС, можно написать следующее определение:

10 REM ПРИМЕР РЕКУРСИВНОЙ ФУНКЦИИ В БЕЙСИКЕ ВВС

20 DEF FNFACTORIAL(N)

30 IF N=1 THEN =1 ELSE =N*FNFACTORIAL(N-1)

40 REM

Здесь определение функции занимает лишь строки 20 и 30. Учтите, что в Бейсике ВВС определения функций и процедур не должны входить в основную часть программы.

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

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

Источник: Уолш Б.    Программирование на Бейсике: Пер. с англ. М.: Радио и связь, 1988. 336 с: ил.

По теме:

  • Комментарии