#10 Второе пришествие

Длинное Вступление

Здраствуйте!
Сегодня будет длинное вступление. Как вы наверное заметили :) рассылка не выходила в свет чуть больше недели. Я надеюсь, что за это время вы не забыли предыдущие уроки и готовы читать новые. Я вообще считаю, что обучение должно продвигаться не линейно, как учат в школе и институте, а скачками - от одного уровня к другому. При этом перерыв между уровнями может быть очень большой. Итак будем считать, что первый уровень кончился и его мы прошли благополучно. Начинается второе пришествие рассылки :)
К сожалению в прошлом выпуске в программе проверки является ли слово полиндромомом была допущенна ошибка. Правильный вариант программы приведён ниже:

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 - число колец)
А вот история: в городе Бенарес (Индия) есть храм, в котором стоят "Пирамиды барминов" (у нас эти пирамиды и назывались - Ханойскими башнями). Как гласит легенда, эта пирамида состоит из 64 золотых колец, которые и по сей день перекладывают жрецы храма. Как только они справятся с этой задачей мир исчезнет :) Такие вот радостные новости, однако не стоит волноваться 264 - 1 = 18 446 744 073 709 551 615 :))) Так, что на наш век хватит и другим ещё останется.
Кстати ещё одна новость для любителей статистики - так как мы перешли в нумерации на Hex числа, то сегодняшний выпуск опять юбилейный - 10 :)

Теория

Я уже наверное вбил в вам в голову, что Паскаль - строготипизированный язык. Вы уже знакомы с довольно большим количеством типов. Однако мы можем и создавать свои типы! Например нам надо создать тип, значение которого будут меняться от 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
succ (green) = blue
Кстати переменную перечисляемого типа можно объявлять без объявления типа, сразу разделе var:
var
   color : (red, green, blue);
Перечисляемый тип это конечно хорошо, а если нужно содать тип в котором будет больше 100 значений (!) тут он явно не подойдёт. Тут нам поможет тип-диапазон. Тип-диапазон - это подтип (подмножество) базового типа, которым может быть любой целый тип, кроме типа-диапозона (логично, не правда ли :).

Тип-диапазон задаётся своими границами: мин_значение .. макс_значение.

Две точки .. рассматриваются как один символ и неотделимы! Например создадим тип-диапазон, который будет менять значения от -25 до 32:

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)
Решения, присланные читателями можно посмотреть тут: http://www.ibp7.narod.ru/index.html?dz4.html

Короткое послесловие

Ну вот и состоялось второе пришествие :) Следующий выпуск ждите в среду.
Удачи!


[Назад] [Содержание] [Дальше]
Hosted by uCoz