Длинное Вступление
Здраствуйте!
Сегодня будет длинное вступление. Как вы наверное заметили :) рассылка не выходила в свет чуть больше недели. Я надеюсь, что за это время вы не забыли предыдущие уроки и готовы читать новые. Я вообще считаю, что обучение должно продвигаться не линейно, как учат в школе и институте, а скачками - от одного уровня к другому. При этом перерыв между уровнями может быть очень большой. Итак будем считать, что первый уровень кончился и его мы прошли благополучно. Начинается второе пришествие рассылки :)
К сожалению в прошлом выпуске в программе проверки является ли слово полиндромомом была допущенна ошибка. Правильный вариант программы приведён ниже:
Program Polindrom;
var
str : string;
i, j : integer;
flag : boolean;
begin
i := 1;
flag := true;
readLn (str);
j := ord (str[0]);
while i < j do
begin
if str[i] <> str[j] then
begin
flag := false;
break
end;
{ ВОТ ЭТО БЫЛО ПРОПУЩЕННО !! }
i := i + 1;
j := j - 1
end;
if flag then
writeLn ('ДА !')
else
writeLn ('НЕТ !')
end.
Спасибо Александру, который указал на эту неточность. Кстати он ещё и прислал формулы для выражения arcsin и arccos через arctg, которых я честно не знал:
arcsin(x)=arctg(x/sqrt(1-sqr(x)))Редакция рассылки :) приносит свои извинения. Виновные будут найдены и повешены на ближайшем суку.... Да, возвращаясь к предыдущему выпуску. Читая книгу Мартина Гарднера - Математические головоломки и развлечения, нашёл одну интересную историю про задачу о Ханойских башнях, программку для решения которой мы написали в прошлый раз:
arccos(x)=arctg(sqrt(1-sqr(x))/x)
Число необходимых перекладываний колец выражается формулой: 2n - 1 (n - число колец)Кстати ещё одна новость для любителей статистики - так как мы перешли в нумерации на Hex числа, то сегодняшний выпуск опять юбилейный - 10 :)
А вот история: в городе Бенарес (Индия) есть храм, в котором стоят "Пирамиды барминов" (у нас эти пирамиды и назывались - Ханойскими башнями). Как гласит легенда, эта пирамида состоит из 64 золотых колец, которые и по сей день перекладывают жрецы храма. Как только они справятся с этой задачей мир исчезнет :) Такие вот радостные новости, однако не стоит волноваться 264 - 1 = 18 446 744 073 709 551 615 :))) Так, что на наш век хватит и другим ещё останется.
Теория
Я уже наверное вбил в вам в голову, что Паскаль - строготипизированный язык. Вы уже знакомы с довольно большим количеством типов. Однако мы можем и создавать свои типы! Например нам надо создать тип, значение которого будут меняться от 0 до 5. Для описаний типов отведён ещё один отдел программы - type. Он должен находится в самом начале программы, где-то среди var, const и label. Хотя желательно размещать этот раздел первым. Новый тип объявить очень просто, как константу:
Имя = типНу не нравится мне, например, каждый раз писать длинное слово integer, а хочется чего-то покороче - int. Пожалуйста:
type
int = integer;
var
i : int;
Естественно, что наш "новый" тип int сохранит все свойства типа integer - максимальное и минимальное значение, допустимые операции. Мы просто обозвали integer как int, по этому теперь эти переменные имеют одинаковый тип:
type
int = integer;
var
i : int;
j : integer;
begin
i := 1;
j := i;
Однако раздел описаний не создан для того, что бы переименовывать существующие типы. Он создан для создания новых (вот каламбур получился:) Перечисляемый тип. Этот тип задаётся перечислением всех возможных значений. Каждому значению присваивается некоторый индетификатор и распологается в списке, обрамлённым круглыми скобками. Например:
type
TColor = (red, green, blue);
var
color : tcolor;
Теперь мы можем присваивать переменной color значения red, или green или blue вот так: color := red; Для численного представления элементов перечисляемого типа используется уже известная функция ORD. Причём первому элементу соответствует 0, второму 1 и так далее... Т.е. ord (red) = 0, ord (green) = 1, ord (blue) = 2. Допускается и обратное преобразование типов из целых в перечисляемые. Делается с помощью указаня имени типа и в скобках целого значения:
color := tcolor (1); - равносильно color := green;Функции преобразования создаются автоматически. Давайте познакомимся ещё с несколькими функциями языка Паскаль:
function pred(X): - возвращает предшественник аргументат.е. предыдущее значение типа. Например
pred (1) = 0
pred (blue) = green
function Succ(X): - возвращает следующее значение типаНапример:
succ (0) = 1Кстати переменную перечисляемого типа можно объявлять без объявления типа, сразу разделе var:
succ (green) = blue
varПеречисляемый тип это конечно хорошо, а если нужно содать тип в котором будет больше 100 значений (!) тут он явно не подойдёт. Тут нам поможет тип-диапазон. Тип-диапазон - это подтип (подмножество) базового типа, которым может быть любой целый тип, кроме типа-диапозона (логично, не правда ли :). Тип-диапазон задаётся своими границами: мин_значение .. макс_значение. Две точки .. рассматриваются как один символ и неотделимы! Например создадим тип-диапазон, который будет менять значения от -25 до 32:
color : (red, green, blue);
type
tsome = -25 .. 32;
var
c : tsome;
begin
c := 0;
При этом -25 <= c >= 32 Присвоение с := 33 вызовет ошибку.
Можно создавать и такие типы-диапозоны:
type
Tday = 1..31;
Tmonth = 1..12;
Tabc = 'A' .. 'Z';
var
buka : Tabc;
begin
buka := 'D';
buka := chr (66);
writeLn (buka)
end.
тип Tabc является под-типом char, поэтому над ним разрешаются только операци допустимые типом. Такое неправильно: buka := 66 !!! Рассмотрим ещё один пример:
type
Tweek = (mo, tu, we, th, fr, sa, su);
TweekEnd = sa .. su;
var
w : TweekEnd;
begin
w := sa;
w := pred (su);
writeLn (ord (w));
w := succ (w);
writeLn (ord (w))
end.
ну и что же выведет такая программа? На самом деле - 5 и 6. Так как не стоит забывать, что первому элементу Tweek соответствует 0, а последнему 6. А TweekEnd у нас является под-типом Tweek и соответственно сохраняет значения элементов. Напомню, что в Паскале есть ещё две функции, которые работают с типами:
function High(X) - возвращает максимальное значение типа-диапозона, к которому принадлежит переменная Х
function Low(X); - соответственно возвращает минимальное значение типа-диапозона, к которому принадлежит переменная Х
Программа
Возиожно это и неочивидно, но ведь мы можем создать тип функции или тип процедуры. Делается это так же, как и для типа нужно объявить таким образом:
Имя = function (параметры) : тип_возвращаемого_значения;естественно параметры не обязательны. Так же, для простоты можно создать тип массива:
имя = procedure (параметры);
имя = array [диапозон] of тип;Рассмотрим такой пример:
program fillarray;
uses CRT;
const
N = 10;
type
Tarray = array [1 .. N] of integer;
Tfunc = function : integer;
procedure fill (var res : tarray; f : Tfunc);
var
i : integer;
begin
for i := 1 to N do
res[i] := f;
end;
procedure printarray (res : tarray);
var
i : integer;
begin
for i := 1 to N do
write (res[i], #32)
end;
function f1 : integer; far;
begin
f1 := random (256)
end;
function f2 : integer; far;
begin
f2 := random (100)
end;
var
A : tarray;
begin
randomize;
ClrScr;
fill (A, f1);
printarray (A);
writeLn;
fill (A, f2);
printarray (A)
end.
Итак пусть нам надо написать процедуру для заполнения массива, каждый элемент массива - это значение, возвращенное некоторой функцией, которые в свою очередь разные. Вот блин загнул :) Этим у нас и займётся процедура procedure fill (var res : tarray; f : Tfunc); - первый параметр - это массив, второй - функция, с помощью которой мы этот массив и заполним. Что бы передать в качестве параметра функцию, нужно просто указать её имя. А при описании функции использовать директиву far. Что это такое я раскажу позже. Пока, что запомните, что эта директива позволяет выступать функции в качестве параметра. В процедуре fill мы объявляем параметр f, как тип-функцию. Теперь внутри процедуры fill мы можем обращаться с ней так, как будто бы такая функция действительно есть, т.е. мы можем вызвать её, указав имя - f. Если бы были и параметры, то можно было бы передать их как то так: f (par1, par2); Мы как бы создаём виртуальную функцию, которая потом замещается настоящей. Такие сложности на самом деле окупаются. Техника передачи параметров функций поможет создавать различные варианты для программы - от примитивного заполнения массива, до расчёта сложных физических процессов.
Факториал
В прошлый раз (эх давно это было :( было предложено написать программу для вычисления факториала, с использованием рекурсии. Выглядит это как-то так:
Program Factorial;
function fact (n : integer) : longint;
begin
if n <= 1 then
fact := 1
else
fact := fact (n - 1) * n
end;
var
a : integer;
begin
Write ('Введите число:');
ReadLn (a);
WriteLn ('Факториал ', a, '! = ', fact (a) )
end.
В чём заключается идея? В такой простой формуле:
n! = (n - 1)! * nСоответственно мы и останавливаемся при n = 0 (или 1) : 0! = 1 (1! = 1)

