355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Олег Деревенец » Песни о Паскале (СИ) » Текст книги (страница 6)
Песни о Паскале (СИ)
  • Текст добавлен: 6 ноября 2017, 03:30

Текст книги "Песни о Паскале (СИ)"


Автор книги: Олег Деревенец


Жанр:

   

Драматургия


сообщить о нарушении

Текущая страница: 6 (всего у книги 29 страниц)

N = 3

3

2

1

Г) Существует вариант цикла FOR, где счетчик цикла не наращивается, а уменьшается, этот оператор выглядит так:

FOR N:= начальное_значение DOWNTO конечное_значение DO оператор

Ключевое слово DOWNTO задает счет в обратном порядке (DOWN – «вниз»); при этом начальное значение счетчика должно быть больше или равно конечному, иначе цикл не выполнится ни разу. Воспользуйтесь этим оператором для решения предыдущей задачи (задание В).

Д) Пусть программа запросит два числа N и M, а затем вычислит их произведение без использования операции умножения (*). Подсказка: организуйте цикл суммирования N раз числа M.

Е) Напишите программу, вычисляющую сумму чисел от 1 до N, где N – число, вводимое пользователем.

Ж) Напишите программу, вычисляющую сумму только тех чисел от 1 до N, которые делятся либо на три, либо на пять.

Задачи на темы предыдущих глав

И) Платный участок трассы протянулся с километра P1 до километра P2 (P1

К) Дорожная служба запланировала ремонт трассы на участке с R1 по R2 (R1

• Будут ли ремонтировать весь платный участок P1–P2 ?

• Будут ли ремонтировать хотя бы часть платного участка P1–P2 ? Если да, то определить длину ремонтируемой платной части.

• Будут ли ремонтировать хотя бы часть бесплатного участка? Если да, то определить длину ремонтируемой бесплатной части.

Глава 18

Аз, Буки

Вот вам новая задача: побуквенная распечатка строки. Программа должна запросить строку и напечатать ее по буквам, например:

Введите строку: PASCAL

P

A

S

C

A

L

Да будь я хоть семи пядей во лбу, спасовал бы перед этой задачей, если бы… если бы не знал внутреннего устройства строки.

Символьный тип данных

Строковые данные, которыми мы так запросто орудуем, не так уж просты, и нам следует разобраться в этом. Вспомните первый класс, с чего все началось? С освоения букв. Строки тоже складываются из букв, точнее из символов. Символы – это не только буквы, но и цифры, знаки препинания, и даже пробел. Существуют и невидимые, так называемые управляющие символы, но о них мы поговорим в другой раз. Рассмотрим следующую строковую константу:

      ’Привет, Мартышка!’

Сколько символов в этой строке? Здесь 14 букв, к ним надо прибавить запятую, восклицательный знак и пробел, и тогда получится 17.

Для представления отдельных символов в Паскале имеется тип данных CHAR – от английского CHARACTER, что значит «символ». Так же, как и строковые, символьные данные могут быть константами и переменными. Переменные символьного типа объявляют так:

var c1, c2, c3 : char;

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

      c1:= ’A’; c2:= ’B’; c3:= c1;

Символьные константы, как и строковые, заключают в апострофы. Но, в отличие от строк, они могут содержать ровно один символ, – не больше и не меньше! Для острастки я покажу ошибочные операторы, компилятор их обязательно забракует.

      c1:=’ABBA’;       { нельзя присвоить более одного символа }

      c2:=’’;       { и менее одного тоже! }

Но строковым переменным разрешено присваивать значения символьных данных, например:

var c1 : char; S: string;

...

      S:= c1;

Это и понятно, ведь строка может вмещать много символов! Строковые и символьные данные можно «склеивать» операцией сложения, результат получится строковым, например:

      c1:= ’A’; c2:= ’B’; c3:= ’A’;

      S:= c1 + c2 + ’B’ + c3;       { результат равен ’ABBA’ }

      S:= ’pascal’+ c1 + S;       { «склеивание» символов и строк }

Подобно строкам, отдельные символы вводятся процедурой Readln, и печатаются процедурами Write и Writeln, например:

      Readln(c1);

      Writeln(c1);


Индексация

Ясно, что «склеить» символы в строку немудрено, но ведь для решения поставленной задачи требуется обратное – разобрать строку на отдельные символы. Взглянем на строку с иной стороны – как на стройный ряд символов. Каждый символ в этом строю, подобно солдатам, занимает свою позицию. Позиции нумеруются слева направо, начиная с единицы. Например, в слове «PASCAL» символ «P» занимает первую позицию, а «L» – шестую.

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

      c1:= S[3];

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

      c1:= S[2*N+1];

Если N равно двум, то в символьную переменную c1 будет помещен пятый символ строки S.

Длина строки

Разумеется, что значение индекса не должно превышать количество символов в строке. Но как избежать таких ошибок? Если строка перед глазами, вы посчитаете символы, тыча в строку пальчиком. А если это строковая переменная?

В Паскале есть функция, определяющая количество символов в строке, или, иначе говоря, длину строки. Эта функция так и называется – Length – «длина». Вызвать её можно, например, так:

      K:= Length(S);

Здесь переменной K целого типа присваивается значение длины строковой переменной S. Вот ещё примеры (в комментариях указаны результаты).

      S:= ’’; K:= Length(S);       { К=0 }

      S:= ’PAS’ K:= Length(S);       { К=3 }

      K:= Length(S+’CAL’);       { К=6 }

      K:= Length(’Привет, Мартышка!’);       { К=17 }


Распечатка строки

Теперь мы достаточно подкованы, чтобы решить поставленную задачу – разбить строку на отдельные символы. Вот как выглядит один из вариантов решения.

{ P_18_1 – распечатка отдельных символов строки }

var S: string;

      C: char;

      k, L : integer;

begin

      repeat

      Write(’Введите строку: ’); Readln(S);

      L:= Length(S); { определяем длину строки }

      for k:=1 to L do begin

      C:= S[k]; { выбираем очередной символ }

      Writeln(C); { и печатаем его в отдельной строке }

      end;

      until L=0;       { L=0, если строка пуста }

end.

После ввода запрошенной строки определяем её длину, а затем, пробегая по строке, выбираем и печатаем символы. Программа работает, пока пользователь не введет пустую строку; тогда длина строки L станет равной нулю, и цикл завершится.

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

{ P_18_2 – распечатка отдельных символов строки, краткий вариант }

var S: string; k : integer;

begin

      repeat

      Write(’Введите строку: ’); Readln(S);

      for k:=1 to Length(S) do Writeln(S[k]);

      until Length(S)=0;

end.

Здесь функция Length вставлена в оператор FOR, а параметром процедуры Writeln является текущий символ строки S[k]. В цикле FOR выполняется теперь лишь один оператор, поэтому отпала нужда в блоке BEGIN-END. Обратите внимание на условие завершения цикла UNTIL, – оно записано с применением функции Length.

На этом прервем изучение символов и строк. Однако тема не исчерпана, и к ней мы ещё вернемся.

Итоги

• Строки – это цепочки символов. Для работы с отдельными символами в Паскале предусмотрен тип данных CHAR.

• Данные типа CHAR можно «склеивать» друг с другом и со строковыми данными, в результате получаются строки.

• Доступ к отдельным символам строки возможен путем индексации. Эта операция обозначается парой квадратных скобок, следующих за строкой; внутрь скобок помещают числовое выражение – индекс.

• Доступ по индексу применяется как для чтения символов строки, так и для их изменения.

• Для обработки строки необходимо знать её длину. С этой целью в Паскале применяется функция Length.

• Для последовательной обработки символов строки обычно используют цикл со счетчиком FOR-TO-DO.

А слабо?

A) Напишите программу для подсчета букв «А» во введенной пользователем строке. Или слабо?

Б) Напишите программу, меняющую символы «А» строки на символы «Б». Подсказка: изменение символа строки делается оператором присваивания вида S[i]:=…

В) Что делают со строкой S следующие операторы?

      for i:=1 to Length(S) do S:= S + S[i];

      for i:=Length(S) downto 1 do S:= S + S[i];

Проверьте свои предположения на практике.

Г) Записи телефонных номеров обычно содержат дополнительные символы: скобки, черточки, пробелы, например: 8(123)45-67-89. Предположим, что пользователь их так и вводит. Пусть ваша программа удалит из такой строки все символы, кроме цифр. Например, после ввода указанного выше номера она должна напечатать: 8123456789.

Д) Пусть ваша программа напечатает введенную пользователем строку вразрядку, добавляя подчёркивание либо пробел после каждого символа, например: 'Pascal' преобразует в 'P_a_s_c_a_l'.

Глава 19

Процедуры и функции: разделяй и властвуй


Снежный ком

Чем дальше в лес, тем больше дров, – наши программы становятся все замысловатей! Чем измеряют сложность программ? – усилиями, что потребны на их осмысление. С ростом размера программы её сложность растет снежным комом: так программа в десять страниц стократ сложней одностраничной! Почему?

Три базовые структуры: линейная последовательность, условный переход и цикл – это строительные блоки наших изделий. В ходе постройки программы эти структуры причудливым образом внедряются друг в друга: условные – внутрь циклов, циклы – внутрь условных операторов и так далее. План «постройки» определяется решаемой задачей – алгоритмом – и тут ничего не упростить. С ростом программы не только запутывается её текст, но и плодятся полчища переменных. «Расползаясь» по телу программы, они затрудняют контроль над собой. Поверьте, продолжая «строительство» в прежнем стиле, вы скоро свихнетесь, – ведь серьезные программы насчитывают тысячи страниц!

В 15-й главе я поведал о соединении усилий программистов в работе над одним проектом, – им на выручку приходят процедуры и функции. Мы уже пользовались ими, извлекая готовенькими откуда-то из «недр» Паскаля (такими, как Writeln, Readln, Length, Random). Заботит ли вас устройство и сложность этих процедур? Нет? То-то же! Подобно усердным слугам, они лишь исполняют наши капризы. Но, то – чужие «слуги», созданные другими программистами, не пора ли обзавестись своими? Разбив сложную программу на «кусочки», мы значительно упростим её. Как говорят, разделяй и властвуй!

Описание процедур

Для постройки нашей первой процедуры возьмем знакомый пример. Вот как организована пауза с ожиданием нажатия клавиши Enter в одной из наших первых программ.

      Write(’Нажмите Enter…’); Readln;

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

Создать процедуру, – это значит дать ей имя и описание. Делается это с применением ключевого слова PROCEDURE, после которого указывается имя процедуры и её тело, содержащее операторы. Процедуре назначают имя по тем же правилам, что для констант и переменных. Сейчас мы заменим пару упомянутых выше операторов процедурой по имени Pause (пауза), вот как будет выглядеть её описание (рис. 40).


Рис.40 – Описание процедуры Pause

После заголовка процедуры ставится точка с запятой. Далее следует тело, заключенное в блок BEGIN-END. Завершается описание процедуры ещё одной точкой с запятой. В блоке BEGIN-END размещают любое количество исполняемых операторов по тем же правилам, что применялись нами ранее. Обратите внимание: блок BEGIN-END в теле процедуры обязателен! Даже если внутри блока будет всего один оператор, или не будет вовсе!

Теперь решим, где расположить это хозяйство? На рис. 41 показана знакомая вам структура простой программы. После объявления констант и переменных следует главная программа, где исполняемые операторы заключены между BEGIN и END.


Рис.41 – Структура простой программы

По правилам языка любой объект программы – константа, переменная, процедура – должен объявляться до своего использования. Стало быть, описание процедуры надо поместить до того, как будет сделан её вызов. Поскольку процедура Pause вызывается из главной программы, её описание должно быть помещено перед нею.

{ P_19_1 – Пример применения процедуры }

var Man : string;

procedure Pause; {– описание процедуры –}

begin

      Write(’Нажмите Enter…’);

      Readln;

end;

begin       {– главная программа –}

      Writeln(’Как тебя зовут?’); Readln(Man);

      Writeln(’Здравствуй, ’, Man);

      Pause;       { вызов процедуры }

end.

Но в каком порядке будут выполняться операторы этой программы? Мы знаем, что компьютер исполняет программу, как бы читая её слева направо и сверху вниз. Стало быть, операторы в теле процедуры выполняются первыми?

А вот и нет! Главная программа на то и главная, чтобы исполняться первой. Все начнётся с запроса имени пользователя и так далее. Когда же дело дойдет до вызова процедуры Pause, вступят в бой операторы в теле этой процедуры. Последовательность исполнения показана на рис. 42 (обратите внимание на нумерацию строк). Вызов процедуры Pause приведет, как говорят программисты, к передаче управления внутрь тела процедуры. После исполнения расположенных там операторов, управление возвращается в главную программу к оператору, следующему за вызовом.


Рис.42 – Последовательность выполнения операторов

Итак, хотя процедура размещается в тексте выше главной программы, её операторы выполняются позже – после вызова процедуры.

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

begin       {– главная программа –}

      Writeln(’Как тебя зовут?’); Readln(Man);

      Writeln(’Здравствуй, ’, Man);

      Pause;

      Pause;

      Pause;

end.


Процедуры с параметрами

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

      Pause (’Будьте любезны нажать Enter!’);

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

procedure Pause (msg : string);

Здесь имя параметра msg назначено нами произвольно (это сокращение от слова message – «сообщение»). Параметр, объявленный в заголовке, называют формальным, он доступен только внутри процедуры, где можно обращаться с ним, как с обычной переменной. Например, вывести на экран, как в нашем случае.

procedure Pause (msg : string); { объявление процедуры с параметром }

begin

      Write(msg); Readln;

end;

Что касается вызывающей программы, то имя формального параметра ей неизвестно.

Как действует такая процедура? В момент вызова в главной программе формальному параметру msg автоматически присваивается указанное в вызове фактическое значение, – оно и будет напечатано. Повторяю: присвоение формальному параметру фактического значения происходит автоматически, без участия программиста. Теперь наша программа станет такой.

{ P_19_2 – применение процедуры с параметром }

var Man : string;

{– объявление процедуры с параметром msg –}

procedure Pause (msg : string);

begin

      Write(msg); Readln;

end;

begin       {– главная программа –}

      Writeln(’Как тебя зовут?’); Readln(Man);

      Writeln(’Здравствуй, ’, Man);

      Pause(’Нажмите Enter…’);

      Pause(’Еще раз…’);

      Pause(’И ещё разок!’);

end.

Здесь процедура Pause вызвана трижды с тремя разными фактическими параметрами, испытайте эту программу.

Итоги

• С ростом размера программы стремительно растет её сложность. Для упрощения программ их разбивают на процедуры и функции.

• Чтобы создать процедуру или функцию, необходимо поместить в программе её описание, состоящее из заголовка и тела.

• Внутрь процедуры или функции можно передать один или несколько параметров. Для этого в заголовке процедуры объявляют формальные параметры, а при вызове указывают фактические.

• Тип фактического параметра должен совпадать с типом формального параметра, объявленного в процедуре.

А слабо?

А) Напишите ещё одну версию процедуры Pause, выводящую сообщение либо на русском, либо на английском языке. Параметр этой процедуры должен быть булевым и работать она должна так:

      Pause(true);       { печатается «Нажмите Enter…» }

      Pause(false); { печатается «Press Enter…» }

Б) Напишите и испытайте процедуру (назовем её Line – «линия»), печатающую строку заданной длины, составленную из звездочек, например:

      Line(3); { печатает «***» }

      Line(7); { печатает «*******» }

Подсказка: внутри процедуры надо организовать цикл.

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

Г) Напишите и испытайте процедуру, принимающую два параметра – числа, и печатающую их сумму и их разность.

Задачи на темы предыдущих глав

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

• начиная с первых цифр, например 112-345-1;

• начиная с последних цифр, например 1-123-451.

Е) Почтальон разносит газеты по улице, состоящей из N домов. Четные и нечетные номера расположены по разные стороны улицы. В здравом уме почтальон не рискует лишний раз переходить её. Ваша программа должна напечатать последовательность номеров, по которым будут разнесена почта, когда почтальон начинает работу:

• с первого дома;

• со второго дома;

• с N-го (то есть последнего) дома.

Глава 20

Процедуры: первый опыт

Некоторые считают программирование искусством. Если так, то в чем оно? Искусный программист умеет (кроме прочего) превращать сложную программу в простую, – он равномерно распределяет сложность между процедурами и функциями. Как научиться этому? Усвойте несколько ключевых истин, но главное здесь – практика. Без «шишек» и «синяков» тут не обойтись. Однако, сколько за одного битого небитых дают?

Следующая задача слегка надумана, – это всего лишь полигон для испытания наших собственных процедур. Условие задачи таково: пусть пользователь введет одну за другой несколько строк, например, три (потребуется цикл со счетчиком, улавливаете?). В каждой введенной строке надо заменить латинские буквы «A» – если они там есть – на латинские буквы «B». Например, приняв строку «ABBA», программа должна превратить её в строку «BBBB».

Мухи – налево, котлеты – направо!

Рис. 43 избавляет вас от необходимости малевать алгоритм будущей программы. Ясно, что программа не так проста, – она включает условный оператор и два цикла, причем один из них вложен в другой. Внешний цикл отвечает за ввод строк, а внутренний – за их обработку. Можно ли упростить это сооружение? Бывалый программист сразу смекнет, как отделить здесь мух от котлет, – внутренний цикл, отмеченный серым цветом, лучше выделить в отдельную процедуру, и тогда программа распадется на два несложных алгоритма (рис. 44). Слева на этом рисунке показан алгоритм главной программы, а справа – алгоритм процедуры, которой я дал имя Scan. Пунктирные линии со стрелками показывают места входа в процедуру и выхода из нее.


Рис.43 – Блок-схема программы с двумя циклами


Рис.44 – Блок-схемы главной программы (слева) и процедуры (справа)

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

Сверху вниз

Легко сказать «приступить», но с чего начать? Настрочить программу целиком и сразу? – вот прекрасный способ запутаться! Нет, профессионалы поступают иначе, следуя одному из двух направлений. Первое из них именуется разработкой «сверху вниз», – проект лепят начиная с главной программы, переходя затем к процедурам. Другое направление противоположно первому и называется разработкой «снизу вверх». Оба направления имеют свои достоинства, поэтому в крупных проектах их иногда используют одновременно. Но сейчас нам лучше подходит первый способ.

Итак, последуем выбранному нами порядку разработки «сверху вниз». Этот подход хорош тем, что на промежуточных этапах получаются почти работающие программы. Почему «почти»? – сейчас поймете. Итак, забудем на время о недостающей процедуре Scan и напишем лишь главную программу, вот она.

{ P_20_1 – первый этап разработки }

var S: string; k: integer;

begin       {– главная программа –}

      for k:=1 to 3 do begin

      Write(’Введите строку: ’); Readln(S);

      { Scan(S); }

      Writeln(S);

      end;

end.

Обратите внимание на закомментированный вызов процедуры Scan(S), – он напоминает о незавершенной части работы. Скелет нашей будущей программы готов, его можно не только скомпилировать, но и запустить, – сделайте это обязательно! Разумеется, программа не выполняет всего задуманного, но уже делает кое-что.

Убедившись в работоспособности скелета, перенесём внимание на процедуру. На этом этапе тоже есть свои хитрости: сначала дадим частичное описание процедуры, создав заголовок и оставив тело пустым. Такую процедуру называют заглушкой или пустышкой. Написав заглушку уберите комментарий с вызова Scan(S), и тогда на скелете нарастет немного «мяса».

{ P_20_1 – второй этап разработки }

var S: string; k: integer;

      {– Заглушка процедуры –}

procedure Scan(arg : string);

begin

end;

begin       {– главная программа –}

      for k:=1 to 3 do begin

      Write(’Введите строку: ’); Readln(S);

      Scan(S);

      Writeln(S);

      end;

end.

Процедура Scan принимает строковый параметр arg (это сокращение от слова argument). Аргумент – так ещё называют параметр процедуры или функции. Теперь снова запустите программу. Если все в порядке, значит вызов процедуры Scan(S), как говорят программисты, видит описание этой процедуры, и его фактический параметр S отвечает формальному параметру процедуры arg.

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

      for k:=1 to Length(arg) do

      if arg[k]=’A’ then arg[k]:=’B’;

Напомню, что arg – это переданная в процедуру строка, а k – счетчик цикла. Вставив этот цикл в тело процедуры Scan, получим готовенькую программу.

{ P_20_1 – третий этап разработки }

var S: string; k: integer;

procedure Scan(arg : string);

begin

      for k:=1 to Length(arg) do

      if arg[k]=’A’ then arg[k]:=’B’;

end;

begin       {– главная программа –}

      for k:=1 to 3 do begin

      Write(’Введите строку: ’); Readln(S);

      Scan(S);

      Writeln(S);

      end;

end.

Обратите внимание на счетчик циклов k. Он – счетчик – используется нами в двух местах: в главной программе и в процедуре. Налицо экономия памяти, не так ли? Насколько оправдана эта надежда? Скоро узнаем.

Для пишущих на Delphi. Компилятор Delphi не позволит использовать счетчик k так, как сделано в этой программе, – но об этом чуть позже.

Первые раны

Теперь запустите наше творение. Если вам это удалось, значит компилятор не нашел ошибок. Но вот незадача: работает программа неправильно! Во-первых, буква «A» не меняется на букву «B». Ещё печальней то, что перестал работать цикл главной программы. Она, что называется, зациклилась, запрашивая непрестанно все новые и новые строки. А ведь на скелете цикл работал, – мы проверяли!

Впрочем, если ввести строку из трех символов, программа чудесным образом завершится. Это наводит на размышление, – ведь цикл главной программы тоже считает до трех. Не промахнулись ли мы, доверив переменной k «служить двум господам», работая в двух циклах? Ведь внутри процедуры значение счетчика k изменяется, что нарушает работу цикла в главной программе. И лишь когда счетчик случайно станет равен трем, программа завершается.

Как исправить ошибку? Объявить для счетчика внутреннего цикла переменную с другим именем? Да, можно. Но я воспользуюсь этой ошибкой, чтобы показать иной подход и лучше раскрыть механизм процедур и функций.

Глобальные и локальные

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

const c1 = ’Глобальная’;

procedure Local;

begin

      Writeln(c1);

end;

begin       {– главная программа –}

      Local;

      Writeln(c1);

      Readln;

end.

Очевидно, программа дважды напечатает константу C1, – проверьте меня. Теперь добавим объявление локальной константы с тем же именем C1, поместив его между заголовком процедуры Local и её телом. К совпадающим именам я прибегнул не от бедности фантазии, – мой умысел скоро прояснится.

const c1 = ’Глобальная’;

procedure Local;

const c1 = ’Локальная’;

begin

      Writeln(c1);

end;

begin       {– главная программа –}

      Local;

      Writeln(c1);

      Readln;

end.

Известно, что компилятор не допускает совпадающих имен, но здесь – иное дело. Локальная константа C1 «спряталась» внутри своей процедуры и, как говорят программисты, не видна за её пределами.

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

• локальные объекты (константы, переменные и прочие) видны лишь внутри тех подпрограмм, в которых они объявлены;

• при совпадении имен локального и глобального объектов, внутри подпрограммы имеет силу локальный объект; при этом глобальный объект скрывается локальным.

С учетом сказанного нашу неработающую программу можно исправить так:

{ P_20_1 – вариант программы с локальной переменной }

var S: string; k: integer; { глобальная переменная }

procedure Scan(arg : string);

var k: integer; { локальная переменная }

begin

      for k:=1 to Length(arg) do

      if arg[k]=’A’ then arg[k]:=’B’;

end;

begin { главная программа }

      for k:=1 to 3 do begin

      Write(’Введите строку: ’); Readln(S);

      Scan(S);

      Writeln(S);

      end;

end.

Теперь совпадение имен локальной и глобальной переменных k не нарушает работу программы, поскольку это разные переменные. Они могли бы иметь даже разные типы! Убедитесь, что отныне путаницы в циклах нет.

Локально – это разумно!

Локальные объекты – константы, переменные и прочие – отличное средство для разумного распределения данных в пространстве вашей программы.

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

Другой выигрыш заключается в экономии памяти. Все переменные занимают оперативную память («оперативку»). Чем больше переменных, тем больше памяти им подавай. Глобальные переменные занимают память в течение всего времени работы программы. А для локальных память выделяется лишь на время работы соответствующей процедуры или функции. Завершилась подпрограмма – освободилась память.

Неподдающаяся строка

Теперь вновь проверим нашу программу. В ответ на запрос строки введите что-нибудь вроде «QAAAW». Если все нормально, программа напечатает «QBBBW» (буква «A» заменяется буквой «B»). Не вышло? Что ж, тогда идем «на поклон» к отладчику, – мы сделаем это в следующей главе.

Итоги

• Программа упрощается, если вынести части алгоритма в отдельные подпрограммы – процедуры и функции.

• Объекты, используемые лишь внутри подпрограмм, следует объявлять там же, – как локальные.

• Локальные объекты (константы, переменные и прочие) видны, то есть доступны, лишь внутри тех подпрограмм, где они объявлены.

• Если имя локального объекта совпадает с глобальным, то внутри подпрограммы действует локальный объект, а глобальный делается «невидимкой».

• Локальные объекты упрощают программирование, придают программам надежность и экономят оперативную память.

А слабо?

А) В 17-й главе нами создан экзаменатор, проверяющий знания таблицы умножения. Переработайте программу P_17_1 так, чтобы оценка выставлялась в процедуре, принимающей один параметр – количество допущенных ошибок.

Б) Создайте процедуру, печатающую все числа, кроме единицы, на которые без остатка делится число N, где N – параметр процедуры. Напишите программу для проверки этой процедуры.

В) Два сотрудника подали своему начальнику заявления на отпуск. Первый попросил отпустить его с A1 по B1 день (дни отсчитываются с начала года), второй – с A2 по B2 день. Считаем, что A1

Г) Подойдя к перекрестку, пешеход решает, переходить ли ему улицу или остановиться. На решение влияет характер пешехода и ещё два фактора: сигнал светофора и близость опасно движущегося транспорта. Напишите программу с процедурой, которая принимает и печатает решение в зависимости от переданных в неё трех параметров, а именно.

• Параметр A = true, если горит зеленый;

• Параметр B = true, если поблизости опасно движется транспорт;

• Параметр C – это число, определяющее характер пешехода так:

1 – послушный и осторожный – учитывает и светофор и опасность;


    Ваша оценка произведения:

Популярные книги за неделю