
- •Основы программирования на языке Паскаль
- •Часть 1. Основы языка Паскаль 2
- •Часть 2. Элементы профессионального программирования на Паскале 44
- •От автора
- •Часть 1. Основы языка Паскаль
- •1. Алгоритм и программа
- •1.1. Алгоритм
- •1.2. Свойства алгоритма
- •1.3. Формы записи алгоритма
- •1.4. Программа и программное обеспечение
- •1.5. Этапы разработки программы
- •2. Данные в языке Паскаль
- •2.1 Константы
- •2.2 Переменные и типы переменных
- •3. Арифметические выражения
- •4. Линейный вычислительный процесс
- •4.1 Оператор присваивания
- •4.2 Оператор ввода
- •4.3 Оператор вывода
- •4.4 Управление выводом данных
- •4.5 Вывод на печать
- •5. Структура простой программы на Паскале
- •6. Компилятор и оболочкаTurboPascal
- •7. Разветвляющийся вычислительный процесс и условный оператор
- •7.4. Короткий условный оператор
- •If логическое_выражение then оператор1;
- •7.5. Полный условный оператор
- •If логическое_выражение then оператор1
- •7.7. Вложенные условные операторы
- •7.9. Примеры программ с условным оператором
- •8. Директивы компилятора и обработка ошибок ввода
- •9. Оператор цикла. Циклы с предусловием и постусловием
- •10. Цикл со счетчиком и досрочное завершение циклов
- •11. Типовые алгоритмы табулирования функций, вычисления количества, суммы и произведения
- •11.1 Алгоритм табулирования
- •11.2 Алгоритм организации счетчика
- •11.3 Алгоритмы накопления суммы и произведения
- •12. Типовые алгоритмы поиска максимума и минимума
- •13. Решение учебных задач на циклы
- •14. Одномерные массивы. Описание, ввод, вывод и обработка массивов на Паскале
- •15. Решение типовых задач на массивы
- •Часть 2. Элементы профессионального программирования на Паскале
- •16. Кратные циклы
- •16.1 Двойной цикл и типовые задачи на двойной цикл
- •16.2 Оператор безусловного перехода
- •17. Матрицы и типовые алгоритмы обработки матриц
- •18. Подпрограммы
- •18.1 Процедуры
- •18.2 Функции
- •18.3 Массивы в качестве параметров подпрограммы
- •18.4 Открытые массивы
- •19. Множества и перечислимые типы
- •20. Обработка символьных и строковых данных
- •20.1. Работа с символами
- •20.2 Работа со строками
- •21. Текстовые файлы
- •21.1 Общие операции
- •21.2 Примеры работы с файлами
- •21.3 Работа с параметрами командной строки
- •22. Записи. Бинарные файлы
- •23. Модули. Создание модулей
- •23.1. Назначение и структура модулей
- •Implementation
- •23.2. Стандартные модули Паскаля
- •24. МодульCrTи создание простых интерфейсов
- •25. МодульGraphи создание графики на Паскале
- •Приложение 1. ТаблицыAscii-кодов символов для операционных системDoSиWindows
- •Приложение 2. Основные директивы компилятора Паскаля
- •Приложение 3. Основные сообщения об ошибках Паскаля
- •Приложение 4. Дополнительные листинги программ
- •Приложение 5. Расширенные коды клавиатуры
- •Ascii‑коды
- •Расширенные коды
- •Приложение 6. Правила хорошего кода
- •Приложение 7. Рекомендуемая литература
18.2 Функции
О параметрах-переменных часто говорят, что подпрограмма возвращаетих значения, подчеркивая тем самым, что они являются (илимогутявляться) выходными данными некоторого вычислительного процесса.
Распространены подпрограммы, требующие возврата всего одного выходного параметра, являющегося скаляром(то есть, единственным значением, а не вектором или матрицей). В этих случаях вместо подпрограммы-процедуры используется подпрограмма-функция, возвращающая скалярный результат своей работы в основную программу. Поэтому для функции в ее заголовке необходимо указатьтипвозвращаемого результата, а в теле функции должен присутствовать хотя бы один оператор присваивания, в левой части которого записывается имя функции:
Function Имя (Список формальных параметров):ТипРезультата;
{выше - заголовок подпрограммы}
Var описания локальных переменных;
Begin
{Тело функции}
Имя:=Выражение1;
End;
Здесь Выражение1 должно иметь тот же тип, что и указанный в заголовкеТипРезультата, а операторИмя:=Выражение1; не обязан быть последним в теле функции. Поскольку результат выполнения функции возвращается в основную программучерез ее имя, то обращение к функции записывается аналогично стандартным функциямв виде операнда выражения, стоящего справа от знака присваивания:
Результат:=Выражение2;
Выражение2может состоять только из вызова функции видаИмя(список фактических параметров)или включать этот вызов как часть более сложного выражения., переменнаяРезультатдолжна соответствовать типу функции. В записи выражения должны быть соблюдены все изученные ранее правила соответствия типов.
Все, сказанное выше о согласовании формальных и фактических параметров, а также о параметрах-значениях и переменных, в полной мере относится и к функциям.
Фактически, стандартные подпрограммы Паскаля также делятся на функции и процедуры, в зависимости от способа их вызова. Так, writelnотносится к стандартнымпроцедурам, аsin– кфункциям. Правда, некоторые из стандартных подпрограмм имеют переменное число параметров, что запрещено пользовательским подпрограммам.
Разумеется, при использовании функций никто не запрещает возвращать более одного значения через параметры-переменные, подобные тем, что мы изучили для процедур. Можно сказать, что функции в Паскале – это процедуры, способные возвращать дополнительное скалярное значение. Во многих других языках программирования специального разделения на процедуры и функции нет.
Чтобы окончательно уяснить не-синтаксические различия между функциями и процедурами, обобщим их в таблице:
Процедура |
Функция |
Вызывается отдельным оператором |
Вызывается из выражения справа от знака присваивания |
Использует параметры-значения и переменные |
Использует параметры-значения и переменные, дополнительно доступен параметр-переменная с именем, совпадающим с именем функции |
Напишем и вызовем простейшую функцию, находящую максимальный из двух своих вещественных аргументов:
Function Max (a,b:real):real;
Begin
If a>b them Max:=a
Else Max:=b;
End;
Вызвать эту функцию мы могли бы так:
Var x,y,z,r,t:real;
. . .
Read (x,y,z);
r:=max(x,y); {Максимальное из значений x,y записали в r}
t:=max(max(x,y),z); {Максимальное из значений x,y,z записали в t}
Последний вызов иллюстрирует, что, как и стандартные функции, функции, написанные программистом, могут вызываться из сколь угодно сложных выражений – при условии, что мы будем соблюдать правила соответствия параметров. Написанную нами функцию maxнетрудно было бы реализовать и в виде процедуры:
Procedure Max (a,b:real; var c:real);
Begin
If a>b them c:=a
Else c:=b;
End;
Однако, при вызове этой процедуры постоянно пришлось бы передавать "лишний" параметр c, служащий только для хранения возвращаемого значения.
Мы уже знакомы с оператором Halt;, позволяющим аварийно (или просто из бесконечного цикла) завершить программу. Немедленно завершить текущийблок, в том числе и подпрограмму-функцию или процедуру, позволяет операторExit;.
Теперь примеры.
Пр. Используя подпрограмму, вычислить сумму первыхkчленов ряда 1+1/n.
Сумма ряда – это скаляр, естественным выглядит использование подпрограммы-функции. Применив известные алгоритмы, составим программу:
function sum (k:integer):real;
var i:integer;
s:real;
begin
s:=0;
for i:=1 to k do s:=s+1/i;
sum:=s;
end;
var k:integer;
s:real;
begin
write ('Введите число шагов:');
readln (k);
s:=sum(k);
writeln ('Сумма=',s:8:3);
end.
Обратите внимание — несмотря на то, что функция Sumвычисляет единственную величинуs, мы были бы не вправе написать в ней операторfor i:=1 to k do sum:=sum+1/i;- ведьsum– это имя функции, и справа от знака присваивания оно было бы воспринято как попытка функцииsumвызвать саму себя, причем, без соблюдения правил соответствия параметров. Поэтому нам понадобилась "промежуточная" переменнаяs.
Тем не менее, рекурсивныефункции, вызывающие сами себя, существуют и будут кратко рассмотрены чуть ниже.
Пр. Вычислить значение выражения
,
гдеz(x)=sin2x+ln|x|.
Очевидно, что повторное вычисление выражения z(x) с различными значениями аргументаxнеэффективно, куда удобнее написать подпрограмму-функцию, считающую по формулеz(x)=sin2x+ln|x| :
function z(x:real):real;
begin
z:=sin(2*x)+ln(abs(x));
end;
begin
write ('Ответ=', (z(3)+2*sqr(z(5)))/(z(0.1)+1):6:2);
readln;
end.
Как видно из примера, использование функции позволило выполнить расчет единственным оператором (с учетом того, что writeln"умеет" печатать значения вычисленных выражений).
В заключение раздела скажем несколько слов о рекурсии. Рекурсивными называют функции, способные повторно вызывать сами себя. С точки зрения программирования, в этом нет ничего удивительного – просто при повторном входе в функцию в программном стеке создается ее новая копия и расчет выполняется заново с измененным значением параметра функции. Затем функция проверяет значение параметра, при необходимости изменяет его и вновь повторно вызывает сама себя. Разумеется, во избежание зацикливания, при некотором значении параметра должно быть предусмотрено завершение вычислительного процесса. Использование рекурсии целесообразно везде, где расчетследующегозначения некоторой функции зависит от еепредыдущегозначения. Так, классический пример на рекурсию – расчет факториала (факториал целого положительного числаn, обозначаемыйn!, равен произведению всех чисел от1доNвключительно). Используя очевидную формулуn!=n*(n-1)!, напишем следующую рекурсивную функцию:
Function Factorial (n:integer):longint;
begin
if n<2 then Factorial:=1
else Factorial:=n*Factorial(n-1);
end;
Тип функции longintиспользован, так как факториал очень быстро растет и уже 8!=40320, то есть, больше максимального значения типаinteger. Эта функция, как нетрудно увидеть, при значении аргумента, меньшем двух, возвращает единицу, а в противном случае возвращает свой аргументn, умноженный на факториал отn-1. Несложный тест функции мог бы выглядеть так:
begin
writeln ('10!=',Factorial(10));
end.
Рекурсия удобна при составлении различного рода переборных стратегий. Так, игровая задача составления пути по "лабиринту" лучше всего решается рекурсивно – ведь всякий такой путь – это шаг плюс новый путь, уменьшенный на один шаг.
При написании рекурсивных функций следует учитывать, что рекурсивные вызовы довольно быстро заполняют стек и грозят его переполнением. Поэтому основная проблема при написании такой функции – отсечь заведомо лишние вложенные вызовы. Для этого используют дополнительные проверки условий, определяющих, целесообразно ли углублять дальше текущий уровень вложений.