Учебник Turbo Pascal. Введение

ОГЛАВЛЕНИЕ


Основные элементы языка Паскаль

Любой естественный язык строится из элементарных составляющих — букв, образующих алфавит языка. Буквы используются для построения слов, слова складываются в предложения, а предложения... Из предложений состоит любой текст — письмо, роман, секретное донесение. Всякий язык программирования организован примерно так же. Имеется алфавит языка, то есть набор символов, которые можно использовать в программе. Существуют зарезервированные слова, имеющие вполне определенный смысл и определенное назначение. Их нельзя изменять: любая неточность в написании таких слов является серьезной ошибкой. В отличие от естественных языков человеческого общения, в языках программирования можно вводить свои собственные слова и придавать этим словам свой собственный смысл. Небольшую программу можно уподобить письму или маленькому рассказу. Большой проект - это роман. Как и обычное письмо, программа может быть написана хорошим или плохим «слогом» (стилем), и чем лучше стиль, тем понятнее программа, тем меньше вероятность появления в ней ошибок.

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

Язык Турбо Паскаль состоит приблизительно из 80 зарезервированных слов и специальных символов. Алфавит языка составляют буквы латинского алфавита, цифры, а также специальные символы, такие, например, как +, -, _. Специальными символами языка являются и некоторые пары символов. Как уже отмечалось, зарезервированные слова в языке Паскаль могут применяться только по своему прямому назначению, то есть в качестве имен операторов, названий операций и т. д. В табл. 1.1 приведен алфавитный список зарезервированных слов. В большинстве случаев овладение даже небольшой частью этого «словаря» достаточно для начала успешной работы по программированию на Паскале.

Таблица 1.1. Зарезервированные слова языка Паскаль

absolute

and

array

assembler

begin

break

case

const

constructor

continue

destructor

div

do

dpwnto

else

end

external

far

file

for

function

goto

if

implementation

in

inline

interface

interrupt

label

mod

near

nil

not

object

of

or

packed

private

procedure

program

public

record

repeat

set

shl

shr

string

then

to

type

unit

until

uses

var

virtual

while

with

xor

   

В дальнейшем мы разберем применение приведенных в данной таблице зарезервированных слов, а сейчас лишь кратко поясним смысл некоторых из них, наиболее часто используемых в программах на Паскале. Заголовки, то есть первые операторы программ и библиотечных модулей — это program и unit. Для описания переменных, констант и составных частей программы — подпрограмм-процедур и подпрограмм-функций — используются зарезервированные слова var, const, procedure, function. Операторы описания типов переменных, задаваемых пользователем, — это type, array, string, record...end, file of... . Слова, используемые для программирования составных операторов, а также начинающие и оканчивающие последовательность исполняемых операторов программы, — begin и end. Операторами, управляющими ходом выполнения программы (они так и называются — управляющие операторы), являются if-then...else, for...to...do, repeat...until, case...of...end, for...downto...do, while...do. В библиотечных модулях используются зарезервированные слова implementation, interface. Зарезервированные слова для обозначения арифметических и логических операций — div, mod, shl, shr, and, or, not и некоторые другие. В программах, написанных с использованием методов объектно-ориентированного программирования, применяются зарезервированные слова object, constructor, destructor, public и virtual.

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

Таблица 1.2. Одиночные и двойные специальные символы языка Паскаль

:=

Присваивание переменной (слева от символа) значения выражения (справа от символа)

;

Разделитель операторов в программе

( )

Скобки для арифметических и логических выражений

:

Разделитель в описаниях переменных и формате операторов вывода

..

Многоточие для списков

+,-,*,/

Бинарные операции (не только арифметические!)

=

Логическое равенство, элемент описания констант и типов

<>

Логическое неравенство

<=

Отношения «меньше чем» и «больше чем»

>=

Отношения «меньше или равно» и «больше или равно»

.

Конец программы или модуля, а также десятичная точка в константах вещественного типа

'

Ограничители константы строкового типа

{}

(* *)

Пары скобок для комментариев

,

Разделитель элементов списка

[ ]

Скобки для ссылки на элемент массива или указания диапазона значений индекса


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


Программа

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

Мы будем считать, что программа представляет собой последовательность операторов и других элементов языка, построенную в соответствии с определенными правилами п предназначенную для решения определенной задачи. Первым в программе идет зарезервированное слово program. За ним, после одного или нескольких пробелов, следует идентификатор — имя программы. Идентификаторы могут содержать любое количество символов, но Турбо Паскаль распознает только первые 63 из них. что, разумеется, намного превосходит реальные потребности. Идентификатор должен начинаться буквой или символом подчеркивания. Затем могут идти буквы, цифры и символы подчеркивания. Взятая в целом, фраза program s_kate; является заголовком программы с именем s_kate. Каждое описание должно завершаться точкой с запятой. Таким образом, первая строка любой программы имеет вид

program   name;

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

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

Каждая программа обязательно должна иметь часть, которая выполняет какие-либо действия н называется разбелом операторов (иногда — телом программы).

Минимально допустимой выполняемой частью программы является составной оператор

begin
    Si: S2; ... ; Sn;
end

где SI..... Sn — операторы, а зарезервированные слова begin и end играют роль скобок, но только для операторов, а не для математических выражений. Они так и называются операторными скобками. Каждому begin в программе должен соответствовать end. Обратное, вообще говоря, неверно, так как end может заканчивать разделы, начинающиеся зарезервированными словами case и record. За телом программы должна следовать точка — признак того, что здесь находится конечная точка останова программы. Структура программы изображена на рис. 1.1.

program name

Раздел описаний

begin

Раздел операторов

end

Рис.1.1. Структура программы на языке Паскаль

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

program DE;
begin
    ;;;;;
end.

program FG;
begin
    begin
        ;
    end;
    ;
end.

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

Важной частью исходного текста программы являются комментарии. Комментарий представляет собой текст, который находится между фигурными скобками или между парами символов, состоящими из круглой скобки и звездочки (см. табл. 1.2). Текст комментария не обрабатывается компилятором и не включается в исполняемый файл. Комментарии позволяют включить подробное описание программы и пояснения к ней прямо в исходный текст. Грамотное и уместное применение комментариев упрощает понимание программы, облегчает жизнь ее автору и программистам, работающим с уже готовым текстом. У фигурных скобок есть и нестандартное применение — во время отладки часто возникает необходимость временно убрать из программы какие-то операторы, сохранив, тем не менее, их запись. Простейший способ — заключить соответствующий фрагмент программы в фигурные скобки.


Разделы описаний

Описание переменных

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

var  v1,   v2,...   :   type_id;

Здесь v1, v2,... — список переменных, в котором имена переменных разделяются запятыми, a type_id задает тип переменных из данного списка. Если в данной программе используются переменные разных типов, то в предложении var приводятся списки имен переменных каждого типа:

var v_1_1,  v_l_2,... : type_id_i; 
    v_2_l, v_2_2,... : type_id_2;
    v_n_l, v_n_2,... : type_id_n;

Пример описания переменных:

var
    cows, sheeps    : Word;
    overman         : Real;
    milkmaid        : Extended;

Здесь Word, Real и Extended — названия типов.

Типы Word, Real и Extended

Паскаль — это гибкий язык, в котором имеется большое число различных типов. Сейчас мы познакомимся только с некоторыми из них. Word — зарезервированное слово, обозначающее целочисленный тип с диапазоном значений [О, 65 535]. Переменные типа Word могут принимать целые значения только из указанного интервала.

Числовой тип Real — это вещественные значения из диапазона плюс-минус [2,9x10 :siJ, 1,7х10:и]. Переменные типа Real не могут принимать значения, сколь угодно близкие к нулю.

Тип Extended имеет более широкие возможности, чем Real, так как его диапазон составляет плюс-минус [3,4х10"4(Ш, l.lxlO4932], и поэтому в программах рекомендуется использовать именно этот тип.

Описание констант

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

const
    v_1 = val_1;
    v_2 = val_2;
    v_n = val_n;

Здесь v_1, v_2, ..., v_n — имена констант, a vali — значения этих констант. Позже мы узнаем, что константы в Паскале бывают двух видов — петипизи-рованиые (как в данном случае) и типизированные.

Пример описания констант:

const
    my_birth_year       = 1905;
    mass_of_electron    = 9.1095е-28;
    my_salary          = 'invisible';

Исполняемые операторы

Тело каждой программы или подпрограммы состоит из последовательности операторов, каждый из которых выполняет определенное действие. Рассмотрим некоторые операторы языка Паскаль. Начнем с оператора присваивания:

variable :=   expression;

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

Примеры операторов присваивания:

al   := 0.5;
у     := х /  (1.0 + х);

Вызовы процедур

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

WriteLn;
Read(infile, x);

Программирование процедур обсуждается во втором уроке.

Составной оператор

Составной оператор — это оператор вида

begin 
    S_1;
    S_2;
     ...
    S_n;
end;

где операторы S_i, в свою очередь, могут быть простыми или составными операторами. Составной оператор трактуется как один оператор. Такая конструкция используется в ситуациях, когда, согласно формальным правилам языка, разрешается использование лишь одного оператора, а в действительности требуется несколько операторов. С этим приходится сталкиваться, например, при программировании циклов или условных операторов.

Два слова о знаках пунктуации. Строго говоря, символ «точка с запятой» в Паскале является не ограничителем, а разделителем операторов. Поэтому точка с запятой после S_n необязательна. Тем не менее использование точки с запятой облегчает модификацию программы, например, при включении добавочных операторов.

Пример составного оператора:

begin
    S := S + 1;
    а := S - sqr(S);
end;

Структурные операторы

Структурные операторы строятся из специальных зарезервированных слов. логических выражений и других операторов. Каждый такой оператор явно или неявно содержит одну или несколько логических проверок.


Оператор if...then...

Оператор if...then... называется условным оператором и имеет вид

if   expression   then   statement;

где выражение expression1 является логическим. Логическое выражение принимает одно из двух возможных значений — True (истина) или False (ложь). Часто в роли логического выражения выступает какое-то условие, которое может выполняться либо нет. В первом случае его значение — «истина», а во втором — «ложь». Программирование логических выражений мы будем разбирать позже. Если логическое выражение expression1 принимает значение «истина», то выполняется оператор statementl. В противном случае выполняться будет оператор, следующий за данным логическим оператором.

Следует отметить, что, согласно формальным правилам языка, в условном операторе после then допускается применение только одного оператора. Но в практике программирования чаще возникают ситуации, когда при выполнении условия в логическом выражении expression1 следует выполнить несколько операторов языка. Решается эта проблема, как уже было сказано, применением составного оператора.

Операторы if...then... можно вкладывать друг в друга, так как конструкция

if  expression2   then   statement2; 

также является оператором и может заместить оператор statementl:

if  expression1   then 
if expression2 then statement2;

Пример условного оператора:

if  Centigrade   =   0   then   
Write('Температура замерзания воды');


Оператор if...then...else...

Этот оператор является полной версией условного оператора и имеет вид

if  expression   then   statementl   else  statement2;

Выполняется данный оператор следующим образом: если выражение expression принимает значение «истина», то управление передается на оператор statementl, если же нет, то на оператор statement2. Приведу ошибочный вариант данного оператора:

if  expression  then   statementl  else:   statement2;

Здесь первая точка с запятой завершает оператор if...then...e1se, не выполняя никаких действий в случае else, а затем (в любом случае) выполняется оператор statement2.

Оператор

if expression then 
    if expression2 then
        statement2
    else
        statement;

допускает двоякую интерпретацию. Первый вариант соответствует последовательности операторов

if expression then 
begin
    if expression2 then
        statement2
    else
        statement1;
end;

Второй вариант:

if expression then 
begin
    if expression2 then
        statement2
end
else
    statement1;

Компилятор Паскаля всегда выбирает первый из приведенных вариантов — каждому else соответствует ближайший предшествующий if. Если требуется реализация второго варианта, молено использовать операторные скобки begin...end. В общем случае, чтобы четко определить, что чему подчинено, используйте begin...end аналогично круглым скобкам в арифметических выражениях.

Пример условного оператора:

if Two = 2 then
    Writeln('Два равно 2')
else
    Writeln('Это не 2. В чем дело? ');


Вложенные операторы if...then...else...

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

if expression_1 then
    statement_1
else
    if expression_2 then
        statement_2
    else
        if expression_3 then
            statement_3
    ...
    else
        if expression_n then
            statement_n;

Вначале вычисляется значение логического выражения expression_1. Если оно истинно, выполняется оператор statement_1, если же это значение ложно, вычисляется значение выражения expression_2. В том случае, когда полученное значение истинно, будет выполняться оператор statement_2, при значении «ложь» будет вычисляться выражение expression_3 и т. д.

Если выражения expression независимы, то есть вычисление их значений в любом порядке дает один и тот же результат для каждого из них, имеет смысл располагать их в таком порядке, чтобы выражение, с наибольшей вероятностью принимающее значение «истина», стояло на первом месте, выражение, принимающее значение «истина» с меньшей вероятностью, — на втором и т. д. Это уменьшит время выполнения данного фрагмента программы, особенно если вложенный оператор появляется в цикле, который выполняется многократно.

Пример вложенных условных операторов:

if Two = 2 then 
    if One = 1 then
        Writeln('Единица равна 1')
    else
        Writeln('Единица не равна 1')
else
    if Three = 3 then
        Writeln('Три равно 3')
    else
        Writeln('Три не равно 3');


Оператор case...of...end

Для ситуаций, где имеется несколько (три и более) альтернатив, больше подходит оператор case. Этот оператор называется оператором выбора и имеет следующий вид:

case expression of
    values_l: statement_l;
    values_2: statement_2;
    ...
    values_n: statement_n;
else
    statement;
end;

Рассмотрим элементы этой конструкции. Во-первых, это три зарезервированных слова: case, of и end. Между case и of находится выражение expression, принимающее значение, которое, возможно, имеется в одном из списков значений, находящихся слева от двоеточий. Данное выражение называется селектором оператора case. Каждый оператор, идущий за двоеточием, отделяется от следующего списка значений точкой с запятой. Ветвь else, отвечающая всем не перечисленным значениям выражения expression, необязательна. При выполнении данного оператора вначале вычисляется значение селектора. Затем выбирается тот список значений, которому принадлежит полученное значение, и выполняется соответствующий оператор.

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

Поясню применение данного оператора следующим примером. Пусть необходимо преобразовать целое число N в зависимости от величины остатка от его деления на 17 следующим образом:

если N mod 17 = 0, то N := 0;

если N mod 17 = 1 или 6, то N := -N;

если N mod 17 = 2, 3 или 5, то N := 2xN;

если N mod 17 = 4, то N := ЗхN;

во всех прочих случаях N :=* 5xN.

Решение этой задачи на Паскале выглядит следующим образом:

case N mod 17 of
    0                : N := 0;
    1, 6        : N := -N;
    2, 3, 5    : N := 2 * N;
    4                : N := 3 * N;
else                N := 5 * N;
end;

В данном примере селектором является выражение N mod 17. Кроме того, имеются 4 списка значений и ветвь else.


Оператор цикла while...do...

Оператор цикла является важнейшим оператором и имеется в большинстве современных языков программирования (а сама идея цикла возникла еще в XIX веке!). Цикл позволяет многократно выполнить некоторое множество действий, задаваемых операторами, составляющими его тело. В Паскале имеется несколько разновидностей оператора цикла. Начнем с оператора цикла с предусловием. Данный оператор имеет вид

while  expression  do   statement;

При выполнении этого оператора вначале вычисляется значение логического выражения expression. Если это значение истинно, выполняется оператор statement, затем значение выражения проверяется вновь и т. д., до тех пор, пока выражение не примет значение «ложь». Если выражение принимает значение «ложь» при первой же проверке, то оператор statement не выполняется вообще. Особо отмечу частный случай:

while True do statement;

Здесь оператор statement будет выполняться бесконечно.

Пример оператора цикла с предусловием:

while Counter < 10 do 
begin
    write('Значение счетчика равно ', Counter);
    Writeln;
    Counter := Counter + 2;
end:


Оператор цикла repeat...until...

Оператор цикла с постусловием имеет вид

repeat  statement   until   expession;

Здесь вначале выполняется оператор statement, а затем вычисляется значение логического выражения expression. Процесс повторяется, пока выражение expression принимает значение «ложь». Как только это значение станет истинным, выполнение цикла прекращается. Оператор statement может быть любым, в том числе и составным оператором:

begin
    statement_l;
    statement_2;
    ...
    statement_n;
end;

В цикле repeat-until... операторные скобки begin...end могут быть опущены. Таким образом, в общем случае оператор repeat...until имеет следующий вид:

repeat
    statement_l;
    statement 2;
    statement_n;
until expression;

Точка с запятой перед зарезервированным словом until необязательна. В приведенном ниже частном случае

repeat
    statement_l;
    statement_2;
    ...
    statement_n;
until False;

цикл выполняется бесконечно. Еще раз обращаю ваше внимание на то, что если в операторе while...do... проверка выполняется в начале цикла, то в цикле repeat...until... проверка выполняется в последнюю очередь, и Тело цикла в любом случае выполняется хотя бы один раз.

Вот пример цикла с постусловием:

repeat
    Write('Значение счетчика равно ', Count);
    WriteLn;
    Count := Count + 2;
until Count = 10;


Операторы цикла for...to...do... и for...downto...do...

Третий вариант оператора цикла — цикл со счетчиком. Можно считать, что есть две очень похожих друг на друга разновидности цикла со счетчиком. Первый из этих операторов имеет вид

for j   := expression1 to expression2 do statement;

Здесь переменная j, называемая управляющей переменной цикла for, является произвольным идентификатором, который объявляется как переменная любого скалярного типа (к скалярным относятся целый, символьный, булев и перечислимые типы).

При выполнении оператора for сначала вычисляется значение выражения expression1, затем вычисляется значение выражения expression2, далее управляющая переменная цикла последовательно пробегает все значения от expression1 до expression2. В том случае, когда значение expression1 оказывается больше значения expression2, тело цикла не будет выполняться вовсе. Эти значения остаются неизменными в ходе выполнения всего цикла for. Рассматриваемый вариант цикла for эквивалентен следующей последовательности операторов:.

j := expression1; 
k := expression2;
while j <= k do
begin
    statement;
    inc(j);
end:

в предположении, что при каждом выполнении оператора statement не изменяются значения j и к.

Оператор for вида for j := expression1 to expression2 do statement; неэквивалентен последовательности операторов

begin
    j := expression1;
    while j <= expression2 do
    begin
        statement;
        j := j + 1;
    end;
end;

потому что выражение expression2 может изменяться при каждом выполнении оператора statement в цикле while.

В теле цикла for следует избегать операторов, изменяющих значение управляющей переменной j. Несмотря на то что использование подобных конструкций не приводит к ошибкам компиляции, они потенциально опасны и могут приводить к неприятным последствиям. Рассмотрим пример:

sum := 0;
for k := 1 to 100 do
begin
    sum := sum + Sqr(k);
    k := k + 2;
end;

Этот фрагмент программы является попыткой просуммировать n2 по всем целым значениям вида n * (3*&+1), лежащим в диапазоне от 1 до 100. Здесь допущена ошибка реализации алгоритма, так как управляющая переменная k изменяется в составном операторе, управляемом той же переменной k. Правильной будет конструкция следующего вида:

sum := 0;
for k := 0 to 33 do
    sum := sum + Sqr(3*k + 1);
или
sum := 0;
k := 1;
repeat
    sum := sum + Sqr(k);
    k := k + 3;
until k > 100;

После выполнения цикла for значение управляющей переменной становится неопределенным.

Вариант for...downto...do... цикла for аналогичен циклу for...to...do... за исключением того, что в нем управляющая переменная на каждом шаге выполнения не увеличивается, а уменьшается на единицу:

for j := expression1 downto expression2 do statement;

Подводя итоги, для применения циклов можно сформулировать следующие ^рекомендации:

  • Используйте цикл for в том случае, когда точно знаете, сколько раз должно быть выполнено тело цикла. В противном случае обратитесь к циклам repeat или while.
  • Используйте repeat, если необходимо, чтобы тело цикла выполнялось по крайней мере один раз.
  • Используйте while, если хотите, чтобы проверка была произведена прежде, чем будет выполняться тело цикла.

Иногда бывает удобно проводить проверку на возможный выход из цикла где-нибудь в его середине, а не в начале или конце. Такой выход из цикла обеспечивается процедурой Break модуля System, которая прерывает выполнение самого внутреннего вложенного цикла, будь то for, whilе или repeat. Указанный модуль подключается к программе автоматически, если в этом есть необходимость. Пример:

while true do 
begin
    statement1;
    if expression then Break;
    statement2;
end;

Следует также упомянуть процедуру Continue, которая прерывает выполнение тела самого внутреннего цикла for, while или repeat и передает управление на его заголовок, так что начинается выполнение очередной итерации цикла.


Первые программы

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

Листинг 1.1. Самая первая программа

program    first_program;
begin
end.

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

имеет смысл придумывать очень длинные имена. Кроме того, неважно, в каком регистре клавиатуры имя набрано. Между заголовком и оператором begin размещаются описания переменных и других объектов программы. Между зарезервированными словами begin и end находятся исполняемые операторы программы. Завершает программу точка. В нашем первом примере нет ни описаний, ни исполняемых операторов.


Вычисление суммы натуральных чисел

Второй пример сложнее. Мы вычислим сумму натуральных чисел от 1 до 20. Читатель, знакомый с математикой, знает, что сумму первых п натуральных чисел можно найти по формуле Sn = n(n + 1)/2. Используя эту формулу, мы сможем проверить, правильно ли работает программа и не допущены ли во время ее набора ошибки. При разработке программ их отладка, а также проверка правильности работы являются обязательной составной частью работы программиста. Ведь даже если все операторы программы написаны правильно с точки зрения формальных правил языка, ошибка может быть допущена в самом алгоритме или в его записи на языке программирования. Программу можно считать готовым продуктом, только если программист убедился сам и убедил заказчика в том, что программа работает правильно, дает правильный результат. Для тестирования программы используются такие наборы значений входных параметров или такие предельные случаи ее работы, для которых известен точный результат. Результат работы программы в этом случае сравнивается с точными значениями.

Листинг 1.2. Вычисление суммы натуральных чисел

program summation;
var
    i, summa: Word;
begin
    {переменной summa присвоим начальное значение}
    summa : = 0;
    for i := 1 to 20 do
        summa := summa + i;
    WriteLn('l + 2 + ... + 20 = ', summa):
    Write('Нажмите клавишу <Enter>'):
    ReadLn;
end.

В этой программе по сравнению с первым примером появились новые элементы. Это описание двух переменных, используемых в программе:

var
i. summa: Word;

Исполняемая часть программы, начинающаяся строкой begin и завершающаяся строкой end, уже не пустая, она содержит исполняемые операторы. Первая строка в разделе операторов — комментарий, заключенный в фигурные скобки (напомню, что в качестве ограничителей комментария допустимы и пары символов, состоящие из круглой скобки и звездочки). Оператор summa := 0; инициализирует переменную summa, используемую для хранения частичной суммы, присваивая ей нулевое значение. Замечу, что до первого оператора присваивания, содержащего имя переменной в левой части, ее значение не определено.

Затем идет цикл со счетчиком. В нашем примере тело цикла выполняется 20 раз, и каждый раз к значению переменной s прибавляется значение переменной — счетчика i.

Следующие две строки реализуют вывод результата на экран. Для этого в программах на языке Паскаль используются операторы вывода Write и WriteLn. Они содержатся в библиотечном модуле System и во время компиляции автоматически включаются в исполняемый код. Вначале выводится символьная строка. Текст, выводимый на экран, заключается в одиночные кавычки '...'. Затем выводится численное значение — summa.

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

ВНИМАНИЕ В Паскале не различаются большие и маленькие буквы, то есть Sum, sum, SUM и т. д. представляют собой один и тот же идентификатор.

ПРИМЕЧАНИЕ Обратим внимание на знаки пунктуации в этой программе. Точка с запятой (;) не только завершает описания, но и разделяет операторы в исполняемой части программы. Запятая (,) всегда используется для того, чтобы разделить элементы списка, — в данном случае в разделе var и при вызове процедуры WriteLn. Двоеточие (:) отделяет список описываемых переменных от названия их типа Word. Программы, как и обычные предложения, заканчиваются точкой.

ПРИМЕЧАНИЕ В рассматриваемой программе имеются два оператора присваивания :=. В Паскале одиночный знак равенства для присваивания никогда не используется. он имеет другое назначение.

В арифметических выражениях используются символы арифметических операций. Эти символы приведены в табл. 1.2. Так, например, в произведениях между сомножителями должен находиться символ операции умножения *.

Математическое выражение ах2 в программе па Паскале записывается как а*х*х. Следует заметить, что особенностью языка Паскаль является отсутствие стандартной встроенной функции вычисления произвольной степени числа, кроме второй. • Это, конечно же, неудобно при программировании сложных вычислений. При программировании арифметических выражений следует помнить о приоритетах операций, то есть о порядке их выполнения. Первыми выполняются арифметические операции умножения и деления, они считаются равноприоритетными операциями. Затем выполняются операции сложения и вычитания, тоже равноприоритетные. Если подряд идут несколько равпопрп-оритетных операций, они выполняются слева направо. Порядок выполнения операций может быть изменен с помощью круглых скобок. При наличии в арифметическом выражении круглых скобок первыми будут выполняться операции в круглых скобках, начиная с самых внутренних.


Вывод таблицы соответствия температур по Цельсию и Фаренгейту

Следующая программа предназначена для вывода таблицы соответствия между температурными шкалами Цельсия и Фаренгейта в интервале температур от точки замерзания воды до точки ее кипения. Температурная шкала Фаренгейта была предложена немецким физиком Габриэлем Фаренгейтом и используется в настоящее время в ряде англоязычных стран. В этой шкале при стандартном атмосферном давлении температура замерзания воды равна 32 °F, а температура кипения составляет 212 Т. В более привычной для нас шкале Цельсия аналогичными опорными точками являются, соответственно, 0 °С и 100 °С. Эти значения и используются для пересчета одних температур в другие. Нетрудно проверить, что формула для пересчета имеет вид: tf = 9/5*tc + 32, где tf — температура по Фаренгейту, a tc — температура по Цельсию.

Листинг 1.3. Вывод таблицы соответствия температур по Цельсию и Фаренгейту

program Celsius_to_Fahrenheit; 
var
    i, Celsius, Fahrenheit: Word;
begin
    Writeln('Таблица соответствия между температурными шкалами');
    Writeln('Цельсия и Фаренгейта');
    Writeln;
    for i := 0 to 20 do
    begin
        Celsius := 5 * i:
        Fahrenheit := 32 + Celsius * 9 div 5;
        Write(' С = ', Celsius);
        Write(' F = ', Fahrenheit);
        Writeln;
    end;
    Writeln('Нажмите <Enter>');
    ReadLn;
end.

Здесь есть новый элемент — операция div. В Паскале имеются две разновидности операции деления. Это обычная операция деления / и операция целочисленного деления div. В первом случае делимое и делитель могут быть любого числового типа, а во втором они должны быть целыми числами. Результат целочисленного деления — тоже целое число, которое получается отбрасыванием дробной части частного. Есть еще операция вычисления остатка от деления одного целого числа на другое — mod. В программе переменная Fahrenheit имеет целый тип Word, поэтому применение операции / привело бы к вещественному результату и, как следствие, к ошибке компиляции «Type mismatch» («Несоответствие типов»). Отмечу, что Паскаль является языком со строгим контролем за соблюдением типов. Это несомненное достоинство языка, так как, принуждая программиста быть аккуратным при работе с переменными, система программирования на Паскале избавляет его от необходимости искать ошибки на этапе выполнения программы. Это значительно более трудная проблема.

Оператор вывода Write отличается от оператора WriteLn тем, что при выводе значений не происходит переход на следующую строку. В нашем случае оба числа будут выведены в одной строке.


Вычисление суммы

В следующей программе вычисляется сумма:

 

Листинг 1.4. Вычисление суммы

program summation_2;
var
    i, n: Word;
    t, add, summa: Real:
begin
    Write('Введите количество слагаемых n = ');
    ReadLn(n);
    summa := 0:
    for i := n downto 1 do
    begin
        t := 1.0/i:
        add := Sqr(t):
        summa := surma + add;
    end;
    WriteLn('Сумма l/i^2 от i = 1 до ', n);
    WriteLn('S = ', summa):
    Write('Нажмите клавишу <Enter>');
    ReadLn;
end.

В этой программе есть переменные типов Real и Word. Во втором операторе присваивания внутри цикла используется функция вычисления квадрата числа — Sqr. Кроме того, здесь мы встречаемся с разновидностью цикла for, в которой благодаря использованию ключевого слова downto вместо to значение управляющей переменной i уменьшается каждый раз на единицу. В рассматриваемом случае лучше всего проводить суммирование в обратном порядке, то есть от слагаемых с наибольшими номерами, принимающими наименьшие значения, к слагаемым с наименьшими номерами, принимающими наибольшие значения. При суммировании в прямом порядке, начиная с некоторого номера, может возникнуть ситуация, когда при каждом проходе цикла к относительно большому значению суммы будет прибавляться сравнительно малое значение очередного слагаемого. Погрешность выполнения такой операции увеличивается — это особенность машинной арифметики. При суммировании в обратном порядке слагаемые не так сильно различаются между собой по величине, и, следовательно, точность вычисления полной суммы будет выше.


Решение диофантовых уравнений

Следующая программа предназначена для решения диофантовых уравнений. Таким образом мы попадаем в один из сложных разделов математики. Не стоит этого пугаться, ведь мы вооружены компьютером и знанием, правда, еще далеко не полным, языка программирования. Маленькая историческая справка. Открытие диофантовых уравнений связано с именем греческого математика Диофанта, личности полулегендарной. О его жизни практически ничего не известно, но сегодня диофантов анализ — это обширная и важная область математики. Полная математическая теория разработана только для линейных уравнений, а общий метод решения нелинейных диофантовых уравнений все еще неизвестен. Иногда анализ простого на вид нелинейного диофантова уравнения может представлять огромные трудности даже для математика высокой квалификации. Используя компьютер, оснащенный системой программирования на Турбо Паскале, мы сможем применить его возможности для решения как линейных, так и нелинейных диофантовых уравнений.

Самое простое линейное диофантово уравнение имеет вид ах + by = с, где а, Ъ и с — заданные числа, а х и у — неизвестные. Особенность этих уравнений заключается в том, что для них ищутся целочисленные решения. Это можно сделать методом перебора.

Для того чтобы немного оживить наше повествование, решим следующую старинную задачу из области экономики сельского хозяйства. Зажиточный крестьянин потратил 100 рублей на покупку 100 различных домашних животных. Каждая корова обошлась ему в 10 рублей, свинья в 3 рубля, а овца в 50 копеек. Предполагая, что крестьянин приобрел по крайней мере по одному животному каждого вида, найдем, сколько голов скота каждого вида он купил. Условие задачи записывается в виде двух уравнений:

10х + % + г/2 - 100;

х + у + z = 100,

где х, у, z — количество коров, свиней и овец соответственно. Избавимся от знаменателя в первом уравнении, умножив его на 2. Из полученного таким образом уравнения вычтем второе. Это позволяет исключить переменную z. Получаем уравнение 19х + 5у = 100. Решениями данного уравнения должны быть целые положительные числа (видел ли кто-нибудь отрицательное число коров?), меньшие 100. Следующая программа предназначена для решения данного уравнения.

Листинг 1.5. Решение линейного диофантова уравнения

program diophantine_equation_l;
var
    x, y: Integer;
begin
    WriteLn('Целые решения уравнения 19х + 5y = 100 из диапазона');
    WriteLn('l <= x <= 100, 1 <= у <= 100: ');
    for x := 1 to 100 do
        for у := 1 to 100 do
            if 19*x + 5*y = 100 then
                WriteLn('(x, y) = (', x, ', ', y, ')');
    Write('Нажмите <Enter>');
    ReadLn;
end.

Эта программа знакомит нас с новыми элементами. Здесь имеется двойной вложенный цикл for. Внутренний цикл содержит условный оператор if... then.... Оператор WriteLn выполняется только в том случае, когда истинно условие в операторе if. В данном случае это условие 19*х+5*у=100. Обратим внимание на то, что знак равенства обозначает здесь не оператор присваивания, а логическое отношение равенства двух значений. Результатом такого сравнения может быть или True (истина), если условие выполнено, или False (ложь), если условие не выполнено. Оператор вывода

WriteLn('(x, y) = (', x, ', ', y, ')');

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


Решение нелинейного диофантова уравнения

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

x3 = у2 + 2;

Известно его решение: х = 3, у = 5. Усложним задачу и решим уравнение

x3 = у2 + 63;

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

Листинг 1.6. Решение нелинейного диофантова уравнения

program diophantine_equation_2;
var
    х, у, z, w, n: Longint;
begin
    {Вначале найдем наибольшее n, для которого nA2 + 63 <= MaxLongint.
    MaxLongint = 2 147 483 647 - встроенная константа модуля System.
    задающая максимальное значение переменной типа Longlnt}
    n := MaxLongint - 63;
    n := Trunc(SqrtCn)); {Trunc(t) - целая часть величины t}
    n := n - 8;
    х := 0;
    WriteLn('Bce целые решения уравнения х^3 = у*2 + 63.');
    WriteLn('для 1 <= у <= ', n, ':');
    for у := 1 to n do
    begin
        z := у * у + 63;
        repeat
            Inc(x); {Функция Inc(x) увеличивает значение х на единицу}
            w := х * х * х;
        until w >= z;
        if w = z then
            WriteLn('(x, у) = (', х, ', ', y, ')')
        else
            Dec(x); {Функция Dec(x) уменьшает значение х на единицу}
    end;
    Write('Работа закончена, нажните <Enter>');
    ReadLn;
end.

Для того чтобы увеличить диапазон поиска решений уравнения, целые переменные программы х, у, z, w, n описаны как переменные типа LongInt («длинное целое»). Диапазон значений для типа «длинное целое» составляет [- 2 147 483 648, +2 147 483 647]. Здесь же используется и оператор цикла repeat...until.... И, наконец, в программе содержится обращение к встроенным функциям округления к нулю (Trunc), увеличения и уменьшения значения аргумента на единицу (Inc и Dec).


Вычисление произведения заданного числа сомножителей

В следующей программе, предназначенной для вычисления произведения заданного числа сомножителей вида 1 + (-1)n/n2, отметим функцию Odd из модуля System. Это логическая функция, которая принимает значение «истина» только в том случае, когда аргумент — нечетное число.

Листинг 1.7. Вычисление произведения

program product; 
const
    m = 400;
var
    p, x : Extended;
    n : Word;
begin
    product := 1.0;
    for n := 2 to m do
    begin
        x := Sqr(1.0/n);
        if Odd(n) then
            {Для нечетных п}
            product := product * (1.0 - x)
        else
            {Для четных п}
            product := product * (1.0 + x);
    end;
    WriteLn('Произведение для 2 <= n <= ', m, ':');
    WriteLn;
    WriteLn('(1 + (-1^n / n^2) ='. product);
    WriteLn;
    Write('Нажмите <Enter>');
    ReadLn;
end.


Вычисление суммы значений в прямом и обратном порядке

Следующая задача — вычисление суммы значений 1/n5 в прямом и обратном порядке. Программа forward_back_sum знакомит нас с зарезервированным словом uses, которое дает возможность подключать к программе библиотечные модули. В данном примере используются процедуры для работы с экраном в текстовом режиме. Для очистки экрана используется процедура ClrScr, которая находится в библиотечном модуле Crt. Перед вычислением четвертой степени значения целой (типа Word) переменной к ее значение присваивается вещественной переменной х. Это делается для того, чтобы избежать переполнения. Ведь диапазон значений вещественных переменных значительно больше, чем диапазон значений целых переменных типа Word.

Листинг 1.8. Суммирование в прямом и обратном порядке

program forward_back_sum;
uses Crt;
{Crt - библиотечный модуль, который содержит процедуры
для работы с экраном в текстовом режиме}
var
    х, summa, ammus : Real;
    k : Word;
begin
    ClrScr;
    WriteLn('l/n^5. 1 to 1000'):
    {Суммирование в прямом порядке}
    summa := 0.0;
    for k := 1 to 1000 do
    begin
        x := k;
        summa := summa + 1.0/(x*Sqr(Sqr(x)));
    end;
    {Суммирование в обратном порядке}
    ammus := 0.0;
    for k := 1000 downto 1 do
    begin
        x := k;
        ammus := ammus + 1.0/(x*Sqr(Sqr(x)));
    end;
    WriteLn(C'Прямая сумма = ', summa);
    WriteLn('Обратная сумма = ', ammus);
    WriteLn('Разность = ', summa - ammus);
    WriteLn;
    Write('Нажмите <Enter>');
    ReadLn;
end.

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


Подпрограммы-функции

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

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

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

function    name_of_f unction    (argumentslist):    type_of_result;

Здесь идентификатор типа результата type_of_result описывает тип значения, носителем которого является идентификатор (имя) функции name_of_function. Список параметров argumentslist содержит перечисление идентификаторов переменных — параметров функции. Эти параметры используются для передачи данных в подпрограмму-функцию. После имени переменной или группы имен следуют двоеточие и идентификатор типа переменных из этой группы. Список параметров может отсутствовать. Имя функции задается в соответствии с обычными правилами Паскаля.

Внутренняя структура подпрограммы-функции аналогична структуре программы, то есть сначала в ней идут описания, а затем, после зарезервированного слова begin — исполняемые операторы. Завершается подпрограмма-функция зарезервированным словом end, но за ним следует не точка, а символ «точка с запятой». В разделе описаний подпрограммы-функции могут содержаться описания других подпрограмм. Таких описаний может быть несколько. В теле подпрограммы-функции должен присутствовать оператор присваивания, в левой части которого указано имя функции, а в правой находится выражение.

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

Сделаю одно замечание. В дальнейшем наши программы будут использовать математический сопроцессор. Математический сопроцессор — это устройство, предназначенное для ускорения математических вычислений с вещественными числами. Были времена, когда математический сопроцессор был необязательной частью компьютера. Внутреннему представлению данных в сопроцессоре соответствует вещественный тип Extended. Указание транслятору генерировать код для математического сопроцессора задается директивой компилятора {$N+}, размещаемой в начале программы. Обратите внимание на то, что это не комментарий, хотя здесь используются фигурные скобки. Конструкция {$...} со знаком доллара сразу после открывающей фигурной скобки всегда обозначает

директиву компилятора. Директива компилятора позволяет включить требуемый режим его работы.

ПРИМЕЧАНИЕ Директиву {$N+} можно опустить, если в интегрированной среде Турбо Паскаля выбраны опции Options | Compiler | 8087/80287 (в интегрированной среде разработки компиляция для 80x87 выполняется по умолчанию) и Options | Compiler | Emulation (одновременно). Если же вы не располагаете математическим сопроцессором, следует выбрать в диалоговом окне пункт Emulation, но в этом случае не стоит рассчитывать на высокую скорость выполнения программы.


Вычисление квадратного корня из числа методом Герона

В программе geron_sqrt, предназначенной для вычисления квадратного корня из числа (в данном примере из двойки) методом Герона, используется подпрограмма-функция дегоп. Метод Герона — это метод последовательных приближений. Если задано число а и из него требуется приближенно вычислить квадратный корень, то вначале выбирается произвольное начальное приближение x0. Затем задается точность вычислений е > 0 и строится последовательность хn+1 = (хn + а / хn) / 2. Вычисления прекращаются при выполнении условия |xn+1 - хn| < е.

Алгоритму Герона можно придать следующий геометрический смысл (рис. 1.2). Изобразим график функции у = х2 и проведем прямую у = а. Если в точке (хn, хn2) провести касательную, то ее пересечение с прямой у - а даст значение хn+1. Обозначая (дельта)х = хn - xn+1 и (дельта)у = хn2 - а, получаем (дельта)y / (дельта)х - tg а. По формулам дифференциального исчисления tg а = dy / dх = 2хn, поэтому (хn2 - а)/(хn - хn+1) = 2хn, откуда и получается формула алгоритма Герона.


Рис. 1.2. Геометрическая иллюстрация алгоритма Герона

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

Листинг 1.9. Вычисление квадратного корня методом Герона

{$N+}
program geron_sqrt;

function geron(x: Real): Extended;
const
    eps = 1.0E-100;
var
    y, z: Real;
begin
    y := 1.0;
    {Цикл while выполняется до тех пор. пока, не будет достигнута
    заданная точность вычисления квадратного корня}
    while Abs(z - у) >= eps do
    begin
        z := у;
        y := (y + x / у) / 2;
    end;
    geron := у;
end;

begin
    WriteLn( 'Алгоритм Герона для вычисления квадратного корня из двойки:');
    WriteLn('Geron(2.0) =', geron(2.0));
    WriteLn('Sqrt(2.0) =', Sqrt(2.0));
    Write('Нажмите <Enter>');
    ReadLn;
end.

В подпрограмме-функции geron в условии оператора цикла while...do... используется функция вычисления абсолютного значения Abs. Здесь же мы встречаемся с отношением нестрогого неравенства. При программировании логических отношений в Паскале используются четыре специальных символа для обозначения строгих и нестрогих неравенств: <, >, <= и >=. Отношение неравенства программируется при помощи пары символов <>.

ПРИМЕЧАНИЕ Функция geron имеет свой собственный набор описаний — константы eps и переменных у, z. Эти величины локальны по отношению к geron, они «невидимы» из других процедур.


Решение неравенства

В следующем примере речь идет о решении неравенства bn <= а <= bn+1 относительно n при условии а >= 1, b > 1. Неравенство решается перебором значений n, метод решения реализован в функции largestpower, аргументами которой являются значения а и b.

Листинг 1.10. Решение неравенства

program solve_linequality;

function 1argest_power(a, b: LongInt): Word;
var
    n: Word;
    x: LongInt;
begin
    x := b;
    n := 0;
    while x <= a do
    begin
        x := b*x;
        Inc(n);
    end;
    largest_power := n;
end;

begin
    WriteLn('3^n <= 10000 < 3^(n+l)');
    WriteLn('n = ', largest_power(10000, 3));
    Write('Нажмите <Enter>');
    ReadLn;
end.


Вычисление корня функции методом деления отрезка пополам

В следующем примере запрограммировано вычисление корня функции методом деления отрезка пополам. Напомню, что корень функции F(x) — это такое значение ее аргумента х*, при котором выполняется условие F(x*) = 0. Известно, что для решения такого уравнения надо вначале задать интервал [а, Ь], на котором будет искаться решение. Если решение действительно существует, принадлежит заданному интервалу и является на этом интервале единственным, функция F(x) принимает на границах интервала значения противоположных знаков. Другими словами, произведение значений функции на границах интервала отрицательно: F(a)F(b) < 0. Далее исходный интервал делится средней точкой с = (а+b)/2 на две равные части, из которых выбирается лишь та, которая содержит решение уравнения. Процедура деления отрезка пополам повторяется до тех пор, пока корень функции не будет найден с заданной точностью. Оценкой погрешности в данном случае может быть величина последнего интервала |а-b| или значение |F(x)|. Последний критерий используется в программе, приведенной в листинге 1.11.

Листинг 1.11. Решение нелинейного уравнения методом деления отрезка пополам

{$N+}
program bisection;
type
    real_function = function (x: Real): Real;

function G(x: Real): Real; far;
begin
    G := Sqr(x) - 2.0;
end;

function zero(F: real_function; x, y: Real): Real;
const
    eps = 1.0e-12;
var
    mid, fx, fy, fm: Real;
begin
    fx := F(x);
    fy := F(y);
    if fx * fy > 0.0 then
        Halt; {Прекращение работы подпрограммы, если на выбранном участке нет корня}
    repeat
        if Abs(fx) < eps then
        begin
            zero := x;
            Exit;
        end
        else if Abs(fy) < eps then
        begin
            zero := y;
            Exit;
        end
        else begin
            mid := 0.5 * (x + y);
            fm := F(mid);
            if fx*fm <= 0 then
            begin
                у := mid;
                fy := fm;
            end
            else begin
                x := mid;
                fx := fm;
            end;
        end
    until false;
end;
    
begin
    WriteLn('G(', zero(G, 1.0, 2.0):10:10, ') = 0');
    ReadLn;
end.

Остановимся подробнее на этой программе. Вторая строка содержит оператор type, разговор о котором пойдет позже. Сейчас же скажу, что этот оператор позволяет программисту ввести свой собственный тип. Слева от знака равенства записывается название нового типа данных, а справа задается сам этот тип. В нашем случае это функциональный тип. В третьей строке программы используется новый для нас элемент — ключевое слово far. Это директива компилятора, которая используется в описаниях подпрограмм, передаваемых в качестве параметров другим подпрограммам. Такой подпрограммой в нашем случае является подпрограмма-функция G. Она используется в качестве аргумента другой подпрограммы-функции zero. В функции zero запрограммировано вычисление корня произвольной непрерывной функции, имеющей на границах исходного интервала противоположные знаки. Использование функции G в качестве аргумента подпрограммы-функции zero делает программу более универсальной, ведь для решения другого нелинейного уравнения достаточно будет подставить в данную программу описание другой функции.

Оператор цикла repeat...until... в программе сделан бесконечным, так как условие завершения — просто логическая константа false. Если окажется, что корень функции найден с заданной точностью, будет выполняться условный оператор, следующий сразу же за заголовком цикла, а его выполнение завершится вызовом процедуры Exit. Эта процедура завершает выполнение функции. Если условие противоположности знаков нарушено, процедура Halt остановит выполнение программы. В этом случае у функции на заданном интервале нет корня.


Ввод и вывод

Компьютер является устройством, которое принимает данные, обрабатывает их и выводит результат. Для ввода и вывода информации используются файлы. При выполнении программы на Паскале автоматически открываются два файла со специальными именами Input и Output. Файл Input обеспечивает ввод символов с клавиатуры, а файл Output — вывод символов и графических элементов на экран. Клавиатура и экран являются стандартными устройствами ввода/вывода. Обращение к файлам Input и Output происходит автоматически, без дополнительных усилий программиста. Процедуры ввода Read и ReadLn обычно считывают ввод с клавиатуры, а процедуры вывода Write и WriteLn обычно посылают вывод на экран. У компьютера могут быть и другие устройства ввода и вывода: дисководы, принтер, последовательный порт и т. д. К каждому из этих устройств можно обратиться из программы путем определения соответствующих файлов. При сопоставлении файлов устройствам используются обычные имена, принятые в MS-DOS, например prn — принтер, com1 — последовательный порт, c:\users\ivanov\res.dat — файл на жестком диске и т. д.

Следующие две программы предназначены для вывода таблицы значений функции F(x) = х/(1+х) на экран и в файл на диске.


Вывод таблицы значений функции на экран

В программе 1.12 вывод производится на экран.

Листинг 1.12. Вывод таблицы значений функции на экран

program table_of_values;
var
    х: Real;
    k: Word;

function F(x: Real): Real;
begin
    F := x/(1.0 + x);
end;

begin
    x := 0.0;
    WriteLn('Таблица значений функции F(x) = x/(l + x)');
    WriteLn;
    WriteLn('x':10, 'F(x)':20);
    WriteLn;
    
    for k := 0 to 50 do
    begin
        WriteLn(x:10:4, F(x):20:10);
        x := x + 0.1;
        if k mod 10 = 9 then ReadLn;
    end;
    
    WriteLn;
    Write('Нажмите <Enter>');
    ReadLn;
end.

Обратите внимание на то, что программа приостанавливает вывод через каждые 10 строк и ожидает нажатия клавиши Enter. Для этого при каждом проходе цикла вычисляется остаток от деления (операция mod) счетчика строк (переменная k) на 10. В операторах вывода после элементов вывода идут целые беззнаковые значения, разделяемые двоеточиями. Эти значения задают формат вывода. Первое значение, отделяемое двоеточием от элемента вывода (идентификатора переменной), определяет количество позиций в строке, отводимое под данное значение. Второе число, если оно присутствует, определяет количество позиций, отводимых под мантиссу числа (его дробную часть).


Вывод таблицы значений функции в файл

В программе 1.13 вывод этой же таблицы значений направляется в файл.

Листинг 1.13. Вывод таблицы значений функции в файл

program file_of_values;
var
    х : Real;
    k : Word;
    out_file : Text; {тип Text соответствует файлу. состоящему из строк текста}

function F(x: Real): Real;
begin
    F := x/d.O + x);
end;

begin
    Assign(out_file, 'c:\user\ivanov\table.dat');
    Rewrite(out_file);
    x := 0.0;
    WriteLn(out_file, 'Таблица значений функции F(x) = x/(l + x)');
    WriteLn(out_file);
    WriteLn(out_file, 'x':9, 'F(x)':19);
    WriteLn(out_file);
    for k := 0 to 50 do
    begin
        WriteLn(out_file, x:9:3, F(x):19:9);
        x := x + 0.1:
        if k mod 10 = 9 then WriteLn(out_file);
    end;
    Close(out_file);
end.

Здесь Text обозначает тип файла, состоящего из символов, организованных в строки. Такой файл создается обычным текстовым редактором, содержит текст и называется текстовым файлом. Процедура Assign модуля System связывает специальную файловую переменную out_file с файлом на диске, имеющим то имя, которое задает программист. В нашем случае это файл table.dat, который находится в каталоге user\ivanov на диске С:. В этот момент файловая переменная только связывается с именем файла — ничего больше не происходит. Процедура Rewrite открывает файл для записи и, если файл table.dat уже существует на диске, удаляет его содержимое. Об этом не следует забывать! В дальнейшем процедура Close закрывает файл, выполнив предварительно его полное обновление, и размещает его на диске.

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

WriteLn(par_1,   раг_2,....раr_n);

с произвольным числом параметров par_i. Если первым параметром является файловая переменная, то все остальные значения будут записываться в соответствующий файл, в противном случае они будут выводиться на экран (то есть на стандартное устройство вывода).


Ввод данных из внешнего файла

Следующая программа предназначена для ввода данных в программу из внешнего текстового файла table.dat, созданного программой file_of_values, и вывода их на принтер.

Листинг 1.14. Ввод данных из внешнего файла

program get_file;
const
    FF = #12; {Код управляющего символа «прогон страницы»}
var
    in_file, out_fi1e: Text;
    k, count: Word;
    ss: string[80];
begin
    Assign(in_file, 'c:\user\ivanov\table.dat');
    Reset(in_file);
    Assign(out_file, 'prn');
    Rewrite(out_file);
    count := 0;
    
    {Вывод на печать пустых строк}
    for k := 1 to 3 do
        WriteLn(out_file);
    
    {В следующем цикле считывается из входного файла и выводится на печать заголовок таблицы (4 строки)}
    for k := 1 to 4 do
    begin
        ReadLn(in_file, ss);
        WriteLn(out_file, ss);
    end;
    count := 0;
    
    {Считывание из внешнего файла и вывод на печать таблицы значений}
    while not Eof(in_file) do
    begin
        ReadLn(in_file, ss);
        WriteLn(out_file, ss);
        Inc(count):
        if count = 44 then
        begin
            count := 0;
            Write(out_file, FF);
            for к ;= 1 to 4 do
                WriteLn(out_file);
        end;
    end;
    
    Write(out_file, FF);
    Close(out_file);
    Close(in_file);
end.

Считывание файла происходит в цикле while. Данный цикл выбран потому, что он допускает крайний случай — если файл пуст, то цикл не выполняется вообще. Функция Eof модуля System возвращает значение true, если текущая позиция в файле находится за последним его элементом. Таким образом, операторы цикла будут выполняться до тех пор, пока мы не доберемся до конца файла in_file.

Зарезервированное слово string определяет строковые переменные. Объявление ss как переменной типа string[80] означает, что ss является строкой, содержащей до 80 символов. Без параметра [n] максимально допустимая длина строки по умолчанию составляет 255 символов. Таким образом, предложение

var  SS:   string;

объявляет ss строкой, длина которой может быть до 255 символов.

В этой программе через каждые 45 строк в текст вставляется специальный управляющий символ «прогон страницы». Код этого символа содержится в константе FF.

ВНИМАНИЕ Каждый файл, открытый программой, следует закрывать процедурой Close. Это защитит внешние файлы от аварийных ситуаций. Можно сказать, что это — признак хорошего тона в программировании.


Модули

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

Разберем в качестве примера модуль с описаниями гиперболических функций. Напомню их определение:

sinh(x) = (ex-e-x)/2,

cosh(x) = (ex+e-x)/2,

tanh(x) = sinh(x)/cosh(jc).

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

Листинг 1.15. Модуль с гиперболическими функциями

{$N+}
unit hyp_fun;

interface

type
    Float = Extended;

function sinh(x: Float): Float;
function cosh(x: Float): Float;
function tanh(x: Float): Float;

implementation

var
    t: Float;

function sinh(x: Float): Float;
begin
    t := Exp(x);
    sinh := 0.5 * (t - 1.0/t);
end;

function cosh(x: Float): Float;
begin
    t := Exp(x);
    cosh := 0.5 * (t + 1.0/t);
end;

function tanh(x: Float): Float;
begin
    t := Exp(2.0 * x);
    tanh := (t - 1.0) / (t + 1.0);
end;

end.

Зарезервированные слова interface и implementation здесь играют важную роль. Каждый модуль имеет части (секции), озаглавленные этими словами. Секция interface (она называется интерфейсной секцией) содержит описания констант, типов, переменных и процедур, доступных из вызывающей программы или модуля. Секция implementation (секция реализации) содержит исходный код подпрограмм. Она может также содержать локальные описания, такие как var t: Real; из нашего примера.

Каждый модуль начинается с зарезервированного слова unit и заканчивается словом end, за которым следует точка. Для этого end не требуется соответствующего слова begin, хотя можно и поставить его непосредственно перед end. Оператор type в начале нашего модуля определяет тип Float, который в данном случае эквивалентен типу Extended. Указав справа от знака равенства любой другой вещественный тип, можно изменить точность вычисления гиперболических функций.

В следующей программе используется только что рассмотренный модуль hypfun. Проверьте результат ее работы с помощью калькулятора.

Листинг 1.16. Пример использования модуля hyp_fun

{$N+}
program testhyperbolic_funs;
uses
    CRT, hyp_fun;
begin
    ClrScr;
    WriteLn('sinh( 0.5) =',    sinh(0.5));
    WriteLn('cosh(-0.5) =',    cosh(-0.5));
    WriteLn('tanh( 1.5) =',    tanh(1.5));
    Write('Нажмите <Enter>');
    ReadLn;
end.

ПРИМЕЧАНИЕ Результатом компиляции модуля xyz.pas с заголовком unit xyz будет файл xyz.tpu.


Упражнения

В упражнениях 1.1-1.8 приведены только разделы операторов программы и предполагается наличие в программе следующих описаний:

var
    i, j, k, m, n: Word;
    a, b, c: LongInt;
    s, x, y, z: Real;

He прибегая к помощи компьютера, определите результат выполнения каждой программы.

Упражнение 1.1

begin
    n := 0;
    х := 1.0;
    repeat
        Inc(n);
        х := 2.0 * х;
    until х > 1.0е5;
    WriteLn(n, ' ', х);
    ReadLn;
end.

Упражнение 1.2

begin
    S := 0.0;
    n : = 1;
    while n <= 100 do
    begin
        x := 3.0 * n + 2.0;
        x := 1.0/x;
        S := S + x;
        Inc(n);
    end;
    WriteLn(n, ' ', s);
    ReadLn;
end.

Упражнение 1.3

begin
    S := 0;
    n := 0;
    while n < 100 do
    begin
        Inc(n);
        x := 3 * n + 2;
        x := 1.0/x;
        S := S + x;
    end;
    WriteLn(n, ' ', s);
    ReadLn;
end.

Упражнение 1.4

begin
    n := 25;
    a : = 1;
    b : = 1;
    for j := 3 to n do
    begin
        с := b;
        b := a + b;
        a := c;
    end;
    WriteLn('F_', n, ' = ', b);
    ReadLn;
end.

Упражнение 1.5

begin
    n := 25;
    a := 1;
    b ;= 1;
    for j := 2 to n do
    begin
        b := a + b;
        a := b;
    end;
    WriteLn('F(', n - 1, ') - ', b);
    ReadLn;
end.

Упражнение 1.6

begin
    x := Pi;
    у := 0.0;
    for n := 1 to 20 do
        у := у * x + n;
    WriteLn('G(', x, ') = ', y);
    ReadLn;
end.

Упражнение 1.7

begin
    С := 2;
    s := 0;
    for n := 1 to 99 do
    begin
        с := 6 - с;
        у := n/(100 + n);
        s := s + с * у;
    end;
    s := s + 0.5;
    WriteLn('I = ', s/300.0);
    ReadLn;
end.

Упражнение 1.8

begin
    for k := 1 to 100 do
    begin
        j := 2 + k * k;
        m := 1;
        n := 1;
        while n <= j do
        begin
            if n = j then
                WriteLn('(', k, ', ', m, ')');
                Inc(m);
                n := m * m * m;
            end;
        end;
    ReadLn;
end.

В упражнениях 1.9-1.13 вам предлагается написать программы для решения соответствующих задач.

Упражнение 1.9

В настоящее время используются пять температурных шкал. Это шкалы Цельсия, Фаренгейта, Кельвина, Ренкина и международная термодинамическая шкала температур. Наиболее популярная из них — это шкала Цельсия, официально утвержденная в качестве международной шкалы в 1950 году. Шкала Фаренгейта используется в англоязычных странах. Опорные точки для этих шкал приведены в комментарии к программе 1.2. В шкале Кельвина за точку отсчета принят абсолютный ноль — это -273,15 °С, или -459,67 *F. В шкале Ренкина отсчет температуры также идет от абсолютного нуля, а один градус равен градусу по Фаренгейту. Точка замерзания воды по шкале Ренкина — 492 °R, а точка ее кипения — 672 °R. И, наконец, в 1933 году ученые приняли международную шкалу температур, в которой используются дополнительные опорные точки.

Дополните программу 1.2 таким образом, чтобы она выводила таблицу соответствия между температурными шкалами Цельсия, Фаренгейта, Кельвина и Ренкина.

Упражнение 1.10

Найдите все целочисленные решения неравенства х2 - 4ху + у2 < 100.

Упражнение 1.11

Выведите на экран таблицу квадратов целых чисел от 0 до 999. Таблица должна состоять из 100 строк по 10 значений в каждой строке.

Упражнение 1.12

Выведите таблицу из упражнения 11 в файл.

Упражнение 1.13

Напишите программу для печати таблицы из упражнения 11 на принтере.