Make your own free website on Tripod.com

                                                       פרופ' אבא  אנגלברג

          טורבו  פסקל. תכנות  מבני  למתחילים  ומתקדמים  כולל  גרסאות  5.5  ו- 6.0

                                                     Проф. Аба Ангельберг   

Турбо Паскаль. Структурное программирование для начинающих и продолжающих (включительно версии 5.5 и 6.0)

 

Глава  7. Процедуры  и  функции

7.1.Введение

Фрагмент(Segment)  есть   логическая  часть  алгоритма  решения  задачи, которую  можно  выделить  в  качестве  самостоятельного  модуля. Если  в  алгоритмах  присутствуют  части, которые  надо  исполнять  не  один  раз  в  ходе  решения, использование  метода  деления  на  модули  упрощает  логическую  структуру  решения  и  самой  программы. При  написании  блок-схемы  программы  в  ней  отдельно  описывают  любой  определенный  в  ней  сегмент. Когда  пишется программа  по  блок-схеме, разделенной  на  фрагменты, их  можно  писать  отдельно  или  на  своем  месте  в  теле  программы. В программах, которые  демонстрировались  до  сих  пор, показана  лишь  возможность  написания  фрагментов  в  теле  программы, обычно  в  виде  цикла. В  этом  есть  ряд  недостатков:     a).Программа  становится  длинной, а  потому  труднее  отделить  основной  ее  процесс  от  деталей. Можно  писать  примечания  около  каждого  фрагмента, который  начинается  словом  begin,  но  в  этом  нет  решения  важной  проблемы  представления всего  процесса. Когда  пользуются  этим  способом, приходится  разбирать  всю  программу  перед  тем,  как  заняться  ее  деталями.    

b).Когда  нужно  исполнять  фрагмент  программы  несколько  раз, его  пишут  с  начала  до  конца  в  каждом  месте, где  он  нужен.

Чтобы  преодолеть  эти  проблемы, используют  способ  программирования  процедур (Procedures)  и  функций(Functions). Процедура и  функция  может  называться  общим  именем - подпрограмма(Subroutine), она  содержит  программу  краткого  и  распространенного  действия, или  операции, которая  совершается  не  один  раз  внутри  основной  программы. Далее  мы  изучим, когда  и  как  пользоваться  ими.

 

7.2.Процедуры

В  следующей  программе  мы  напишем  новую  версию  maskort3  из  главы  3. Ее  блок-схема  остается  неизменной(chishuv - вычисления).

program maskorte;                                                                                     {1}

(*Programma vichislaet zarplatu vseh 50 rabotnikov predpriatia *)              {2}

var                                                                                                               {3}

 shiur,shaot,sachar: real;                                                                              {4}

 mone: integer;                                                                                              {5}

 

procedure chishuv;(*Na sheme glavi 3 fragment nazvan "obrabotka"*)      {6}

begin                                                                                                            {7}

 write('Skolko platat v chas? Skolko chasov on rabotal? : ');                          {7a}

 read(shiur, shaot);                                                                                        {8}

 sachar := shiur*shaot;                                                                                  {9}

 writeln('Plata za ',shaot:6:1,' chasov ravna ', sachar:8:2);                            {10}

 mone := mone + 1                                                                                       {11}

end;                                                                                                              {12}

 

begin  (*glavnaia programma*)                                                                     {13}

 mone := 1;                                                                                                    {14}

 while mone <= 50 do chishuv    (*obrashenie k procedure*)                      {15}

end.                                                                                                              {16}

Объяснение. В  этой  программе  мы  не  писали  фрагмент  “chishuv”  в  теле  программы  так, как  это  было  в  maskort3, но  написали  лишь  слово  chushuv. Детализация  этой  команды  осуществлена  в  процедуре  с  тем  же  именем, начиная  со  строки  {6}. В  ней  написано  слово  procedure, а  следом  идет  имя  процедуры, которым  мы  пользуемся  для  запуска  процедуры  в  ходе  программы.              Декларацию  процедуры(Procedure  Declaration)    нас  в  строке  {6})  пишут  всегда  после  декларации  переменных  программы. Переменные, используемые  в  процедуре, объявлены  в  декларации  переменных  главной  программы. Любая  переменная, объявленная  в  главной  программе,  и  любое  ее  значение  действительны  и  в  процедуре. Любое  изменение  значения  такой  переменной, выполненное  в  процедуре, действительно  и  для  всей  программы.

В  следующем  примере  предыдущая  программа  изменена  так, чтобы  можно  было  обрабатывать  любое  число  работников, а  не  только  50. А  кроме  того  мы  будем  вычислять  средние  значения  зарплаты, числа  рабочих  часов  и  часового  тарифа  работников. Поскольку  три  последних  действия  занимаются  вычислением  средних,  имеет  смысл  определить  одну  процедуру  вычисления  среднего  и  обращаться  к  ней  трижды. Блок-схему  этой  программы  см.  ниже. (Рис. 1  Прочти    Запиши  среднее  Завершение   mispar - число, tipul_kelet - обработка  ввода, memuza - среднее).

Объяснение. Главная  схема  использует  две  основные  процедуры. Первая - tipul_kelet - управляет  циклом, который  с  помощью  вспомогательной  процедуры  читает  данные, вычисляет  зарплату  и  печатает  ее. Вторая  основная  процедура  memuza - вычисляет  средние  значения  чисел, которые  находятся  в  массиве. Обращаемся мы  к  этой процедуре  трижды, вычисляя  средние: зарплату, часы  работы, часовой  тариф. С  процедурами  в  этой  программе  связано  ряд  переменных, которые  будут  объяснены  в  дальнейшем.  (Рис. 2  Вернись    kria, ktiva - чтение, запись).      (Рис. 3  Прочти    Запиши  Вернись).       (Рис. 4  Вернись).

Используя  процедуру  в  главной  программе, надо  написать  ее  имя(например, memuza  или  tipul_kelet) и  затем  в  круглых  скобках  имена  требуемых  процедуре  переменных. В  самой  процедуре  готовится  соответственный  список  переменных, в  котором  обычно  находятся  другие  имена, т.к. процедура  предназначается  для  общего  пользования  и  часто  пишется  отдельно  от  главной  программы. Поэтому  в  процедурах  tipul_kelet  и  memuza  вместо  mispar  написано  number, а  вместо  shiur, shaot  и  sachar  используются  массивы  hours, rate  и  salary, соответственно. В  процедуре  memuza  используется  имя  marach, которое  при  каждом  обращении  заменяется  одним  из  массивов  shiur, shaot  или  sachar. Следующая  программа  основана  на  предшествующих  блок-схемах (totzaa - результат):

program maskortf;                                                                               {1}

type arraytype = array[1..100] of real;                                                 {2-3}

var                                                                                                        {4}

 shiur,shaot,sachar: arraytype;                                                              {5}

 mispar: integer;                                                                                    {6}

 totzaa: real;                                                                                          {7}

 

procedure tipul_kelet(number: integer;                                               {8-9}

                                    var salary,hours,rate: arraytype);                     {10}

var i: integer;                                                                                       {11-12}

 

procedure kria_ktiva;                                                                         {13}

begin                                                                                                  {14}

 write('Vvedi chislo chasov i chasovoi tarif rabotnika ',i,': ');                 {15}

 readln(hours[i], rate[i]);                                                                       {16}

 salary[i] := hours[i]*rate[i];                                                                   {17}

 writeln('Plata za ',hours[i]:6:2,' chasov ravna ', salary[i]:6:2);              {18-19}

end;                                                                                                    {20}

 

begin                                                   (*Procedura  tipul_kelet*)       {21}

 for i := 1 to number do kria_ktiva       (*Obrashenie k procedure*)    {22-23}

end;                                                                                                    {24}

 

 

procedure memuza(number: integer; marach: arraytype;                 {25-26}

                                 var average: real);                                            {27}

var i: integer;                                                                                     {28-29}

  sum: real;                                                                                         {30}

begin                              (*Procedura  memuza*)                              {31}

 sum := 0;                                                                                           {32}

 for i := 1 to number do sum := sum + marach[i];                              {33-34}

 average := sum/number                                                                     {35}

end;                                                                                                    {36}

 

begin                               (*Glavnaia programma*)                             {37}

 write('Skolko rabotnikov na predpriatii?: ');                                          {38}

 readln(mispar);                                                                                   {39}

 tipul_kelet(mispar, sachar, shaot, shiur);                                           {40}

 memuza(mispar, sachar, totzaa);                                                      {41}

 writeln('Srednaia oplata ravna ',totzaa:8:2);                                        {42}

 memuza(mispar, shaot, totzaa);                                                         {43}

 writeln('Srednaee chislo chasov ',totzaa:8:2);                                     {44}

 memuza(mispar, shiur, totzaa);                                                          {45}

 writeln('Srednaia oplata v chas ',totzaa:8:2);                                       {46}

end.                                                                                                     {47}

Объяснение.

{2-7}В  этих  строчках  определяются  глобальные  переменные(Global  Variables)  и  глобальные  типы (Global  Types). Эти  переменные  и  типы  будут  определены (понятны)  в  ходе  всей  программы, и  мы  сможем  их  использовать  в  любой  процедуре  без  дополнительной  декларации.

{37-47}Этот  сегмент  есть  главная  программа, которая  соответствует  главной  блок-схеме. После  ввода  числа  сотрудников(mispar)  обращаемся  к  процедуре  tipul_kelet   в строке {40}.

Процедурная  команда  tipul_kelet. Команда  в  строке  {40}  называется  процедурной (Procedure  Statement). Она  содержит  имя  процедуры  и    скобках)  список  действительных  параметров (Actual  Parametrs) - аргументов  процедуры. Эти  аргументы  подставляются  вместо  формальных  параметров (Formal  Parametrs), которые  объявляются  в  заголовке(декларации)  процедуры. В  этом  примере  действительный  параметр  mispar  заменяет  формальный  параметр  number. Переменную  mispar  нужно  объявить  в  декларации  var (см. строчку {6}). Ее  тип  определен  как  integer,  поэтому  она  может  быть  подставлена  на  место  number  того  же  типа.  

Программист  должен  озаботиться  тем, чтобы  тип  действительного  параметра  был  бы  тем  же, что  и  тип  формального  параметра, на  место  которого  он  подставляется; но  нет  нужды  упоминать  тип  действительной переменной  в самой  процедурной  команде.  Желательно  отметить, что  порядок  параметров  должен  быть  одинаковым  и  в  декларации  процедуры  и  в  процедурной  команде. Проверять  это  нужно  сравнением  действительных  параметров  в  строке  {40}  с  формальными  параметрами  в  строках  {8-10}.  Процедурная  команда  принадлежит  исполнимой  части  программы, она  будет  исполнена  согласно ее имени  и  указанного  в  скобках  списка  действительных  параметров. В  ходе  исполнения  формальные  параметры  будут  заменены  на  текущие  значения  действительных  параметров.   Вместо  действительного  параметра  mispar     можно  было  написать  число  10, выражение  10*mispar + 19  или  любое  другое  арифметическое  выражение, значение  которого  является  целым. Компьютер  вычислит  значение  этого  выражения  в  ходе  исполнения  процедурной  команды  и  передаст  его  переменной  number, используемой  в  процедуре.

Переменная  подобная  number  называется  параметром-значением(Value  Parametr), т.к.  она  принимает  значение  действительного  параметра, сохраняет  его  в  number,  и оно   используется  в  процедуре. Нет  нужды  определять  в  программе  переменную, которая  примет  предельное  значение  number, т.к.  это  значение  не  требуется  в  главной  программе.

Если  перед  number  написано  var(как  перед  average  в строке  {27}),  то  это  означает, что  программа не  должна определять  новую  переменную  под  именем  number, а   будет связывать  с  этим  именем  переменную  mispar - действительный  параметр  из  главной  программы. В  такой  ситуации  действительный  параметр  может  быть  только  переменной  и  не  может  быть  арифметическим  выражением, чтобы  можно  было  обращаться  к  нему  в  процедуре  по имени  number.  Такой  параметр  именуется  параметром-переменной (Variable Parametr), т.к.  его  можно  заменять  действительным  параметром - переменной, а  не  параметром - арифметическим  выражением. Параметры-переменные  используются  не  только, когда  хотят  получить  данные  из  вызывающей  программы (Calling  Program), но  и  когда  хотят  вернуть  в  нее  какие-то  результаты. Компилятор  обрабатывает  передачу  результатов  посредством  действительного  параметра, который  ставится  на  место  параметра-переменной. Объявление  процедуры  tipul_kelet.    {8-10}  это  полная  декларация  процедуры . После  слова  Procedure  и  ее  имени  в  скобках  перечисляются  формальные  параметры, чего  не  было  в  программе  maskorte. Эти  параметры  называются  так, поскольку  они  включены  в  декларацию  процедуры, чтобы  объявить  типы  использумых  в  ней  аргументов. Например, number  есть  первый  параметр, его  тип - integer. Параметр  number  не  объявлен  в  декларации  var, поэтому  он  существует  только  для  указания  типа(т.е.  формально), а  не  в  действительности. При  обращении  к  процедуре  в  строке  {40}   указываются  действительные  параметры, которые  заменят  формальные  согласно  объяснению, данному  ниже.

В  строке  {10}  указаны  три  параметра-переменных,  которые  должны  вернуть  в  главную  программу  три  массива, содержащие  зарплаты, часы работы  и  часовые  тарифы  каждого  из  сотрудников  предприятия. Общая  форма  заголовка(объявления)  процедуры: (Рис. 5    Имя  процедуры   Имя  формального  параметра    Тип).

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

В  строке  {10}  тип  salary,  hours  и  rate  определен  как  arraytype,  который  объявлен  в  главной  программе. Существуют  компиляторы, которые  требуют, чтобы  имена  типов  при  определении  процедуры  ограничивались  одним  словом, т.е.  нельзя  писать      “array[1..10]  of  real”  внутри  декларации(заголовка). В  компиляторе  vax, например, этого  ограничения  нет  и  можно  не  определять  глобальный  тип  arraytype.

После  объвления  (заголовка)  процедуры  идет  тело  процедуры, именуемое  блоком (Block). Блок  может  содержать  все  знакомые  нам  декларации, т.е.: (a)label;    (b)const;    (c)type;   (d)var;    (e)procedure(со  своим  блоком);    (f) им  соответствующие  команды.

Команда  {12}  объявляет  переменную  i. Будучи  объявленной  в  процедуре  она  именуется  локальной(местной)  переменной(Local  Variable), поскольку  ее  использование  ограничено  только  этой  процедурой,  и  она  не  действительна  в  главной  программе. Если  обратиться  к  ней  в  главной  программе, придет  сообщение  о  необъявленной  переменной.

В  строке  {13}  имеется  декларация  дополнительной  процедуры. Возможно  определение  процедуры  внутри  процедуры  с  получением  гнезда  процедур (Nested Procedure). Внешняя процедура  может  вызывать  внутреннюю, но  для  главной  программы  нет  доступа  к  ней.  Если  же  нужно  обеспечить  такой  доступ, следует  объявить  kria_ktiva  в  виде  отдельной  процедуры  и  записать  ее  перед, а  не  внутри  процедуры  tipul_kelet. Процедура  kria_ktiva  заполняет  массивы  hours,  rate  и  salary.  Эти  переменные  передаются  ей  и  возвращаются  обратно  не  в  порядке  параметров, а  согласно  порядка   их  определения  в  процедуре  tipul_kelet, которая  обращается  к  kria_ktiva. В  ней  можно  читать  глобальные  массивы(Global  Arrays)  внутренней  процедуры  kria_ktiva.

{13-20}Внутренняя процедура  kria_ktiva.

{21-24}Исполнимая  часть  процедуры  tipul_kelet.

{25-36}Еще  одна  процедура  того  же  уровня, что  и  tipul_kelet(процедура  kria_ktiva  более  низкого  уровня, чем  tipul_kelet  и  угнездована  в  ней). Можно  представить  взаимоотношения  процедур  одинакового  и  подчиненного  уровня  на  схеме  общей  структуры  процедуры: (Рис. 6    Имя  процедуры    Имена  формальных  параметров  и  их  типы     Тело  программы).

Процедура  kria_ktiva  есть  часть  блока  процедуры  tipul_kelet, поэтому  она  подчинена  ей.  Процедура  memuza  не  является  частью  блока, подчиненного  какой-либо  процедуре. Она  может  повторно  использоваться  в  главной  программе. При  изображении  формы  оператора  декларации(заголовка)  и  блока  процедуры  рекомендуется  писать  слово  procedure  строго  под  словами  program, var и  другими  декларациями  главной  программы. А  объявления  процедуры  не  пишут  под  словом  procedure, а  сдвигают  вправо (см.  строки  {11}  и  {28}). При  наличии  гнезда  процедур  декларация  внутренней  процедуры  не  отличается  от  других  объявлений  внешней  процедуры, она  появляется  под  ними(см. строку  {13}). Имя  процедуры kria_ktiva  записано  под  декларацией  var  внешней  процедуры.

 

7.3.Функции

Иногда  процедуре  поставляется  перечень  значений, а  она  возвращает  единственное  значение, которое  вытекает  из  действий, выполняемых  над  входными  данными. Для  таких  случаев  существует  процедура  особого  вида, именуемая  функцией(Function). Ею  принято  пользоваться, когда: a).число  изменяемых  параметров  ограничено  одним; b).параметр-переменная  является  обычным(скаляром), а  не  массивом.

Отсюда  следует, что  процедура  tipul_kelet  не  может  быть  функцией, поскольку  у  нее  три  параметра-переменных, которые  являются  массивами. Напротив,  у  процедуры  memuza  определен  только  один  простой  параметр-переменная  average, поэтому  ее можно  переписать  как  функцию, которая  здесь  и  представлена:    

function memuza(number: integer; marach: arraytype): real;           {1-2}

var i: integer;                                                                                     {3}

  sum: real;                                                                                         {4}

begin                                                                                                 {5}

 sum := 0;                                                                                           {5a}

 for i := 1 to number do sum := sum + marach[i];                             {6-7}

 memuza := sum/number                                                                   {8}

end;                                                                                                   {9}

Объяснение.

{1-2}Эти  строчки  составляют  заголовок - объявление  функции(Function  Declaration). Оно  подобно  декларации  процедуры  с  заменой  слова  procedure  на  слово  function. Сперва  пишут  слово  function, а  затем  ее  имя(в  примере  memuza). Сразу  замечаем  отсутствие  в  строке  {2}  слов  average: real;, которые  мы  писали  в  процедуре  memuza, но  в  них  нет  нужды. Поскольку  функция  предназначена  только  для случая, когда  результатом  является  одна  простая  переменная, условлено, что  именем  возвращаемой переменной  будет  имя  самой  функции, в  нашем  случае - memuza.  Но  если  memuza  заменяет  average, то  где  находится  описание  типа  переменной(real)? Оно  находится  в  конце  строки {2}  перед  двоеточием, идущим  следом  за  правой  скобкой, закрывающей  список  параметров-значений.

Чтобы  функция  могла  вернуть  значение, ее  имя  должно  появиться  слева  в  команде  присвоения(см. строку  {8}). Имя  функции  memuza  заменило  переменную  average, которая  была  параметром-переменной  в  процедуре. Имя функции  memuza  не  является  обычной  переменной  и  не  может  появиться  справа  в  команде  присвоения  в  декларации  функции. Единственное  назначение  имени  в  указании  адреса  результата, передаваемого  в главную (вызывающую)  программу. Общая  структура  функции:

(Рис. 7    Блок  Имя  функции   Имена  формальных  параметров  и  их  типы     Имя  типа, относящееся  к  имени  функции   Тело  программы).

Использование  функции  немного  отличается  от  использования  процедуры. Процедурная  команда, содержащая  список  действительных  параметров, есть, в  сущности, новая  команда. Поэтому она  стоит  в  программе  сама  по  себе, как  написано  в  строках  {40}, {41}, {43}  и  {45}  программы  maskoretf. Напротив  функция  участвует  в  программе  в  роли  операнда, а  не  в  роли  команды,  поэтому  она  не  может  стоять  сама  по  себе. Имя  функции  можно  писать  справа  в  операторе  присвоения  с  дополнительными  параметрами  или  без  них. Например, используя  функцию  memuza, мы  можем  заменить  строку  {41}  программы  maskoretf (параграф  7.2) на:       tozaa := memuza(mispar, sachar); 

А  можем  написать, если  необходимо, и  такую  команду  присвоения:

                   tozaa := memuza(mispar, sachar)*memuza(mispar, shiur) + 10;

Выражение, в  котором  программа  вызывает  и  задействует  функцию,  называется оператором  функции (Function  Designator). Этот  оператор  содержит  действительные  параметры (вместо  формальных), т.е.  все  так  же, как  у  процедуры. 

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

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

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

Как  еще  один  пример  представим  булеву  функцию, проверяющую, является  ли  некий  год, передаваемый  ей  как  параметр-значение, високосным(meuberet):

function  meuberet(year:  integer): boolean;

begin

  meuberet := (year  mod  4  =  0)  and  (year  mod  100  <>  0)  or  (year  mod  400  =  0)

end;

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

 

7.4.Локальные  и  глобальные  имена (*)

Имя (Name)  есть  любой  объект(Item), который  надо  объявить. В  соответствии  с  этим  следующие  объекты  есть  имена:   a).метки(Labels);   b).постоянные(Constants);  c).типы  данных(Types); d).переменные(Variables); e).процедуры(Procedures); f).функции(Functions); g).постоянные  перечисляемого  типа (Enumerated  Types).

У  каждого  имени  существует:  a).точка  определения(Point  of  Definition) - в  разделе  деклараций  программы; b).точка  применения(Point  of  Application) - в  исполнимой  части  программы. 

Точка  определения  имени  должна  предшествовать  любой  точке  его  применения (кроме  одного  исключения, которое  мы  в  дальнейшем  рассмотрим). Таким  образом  компилятор  может  обслуживать  все  имена  программы  в  ходе  одной  только  ее  компиляции (Compilation). Областью (Region)  имени  является  блок, в  котором  оно  определено.  Интеравалом  (Scope)  имени  является  его  область, за  исключением  всех  областей, в  которых  определено  идентичное  имя. Имя  известно (видимо - visible)  в  своем  интервале, а  в  других  местах  области  оно  неизвестно(Unvisible).

Если  блок  А  содержит  блок  В, то:    

a).Имя, объявленное  в  А, известно  в  В, если  только  такое  же  имя  не  объявлено  в  В. Имя, объявленное  в  А и не объявленное в В, является  глобальным(Global  Name)  в  В.    

b).Имя, объявленное  в  А  и  также  в  В, является  локальным(Global  Name)  в  В.

c).Если  точка  применения  имени, объявленного  в  А, находится  в В, нельзя  объявлять  такое  же  имя  в  В, поскольку  оно  потеряет  в  В  свой  исходный  смысл. 

Эти  правила  распространяются  на  имена, которые  появляются  в  процедурах  и  функциях. В  следующем  примере  мы  представим  использование  указанных  правил (shemot - имена, rishon - первый, sheni - второй, chizoni - внешний, pnimi - внутренний):

program shemot1;  

const   a = 5;

type  b = (c, d, e);

var

  f: b;

 g: integer;

procedure rishon

 const  h = c;

 var

   g: b;

   i: integer;

 begin

   содержимое  rishon

   ::::::::::::::::::::::::::::::::

 end;                                                                                                   

procedure sheni_chizoni;

 type  a = (h, j, k);

  procedure  sheni_pnimi;

  const  h = c;

  var

    b, j: a;

  begin

    содержимое  sheni_pnimi

  end;

 begin

   содержимое  sheni_hizoni

 end;

begin

    содержимое  главной  программы

    ::::::::::::::::::::::::::::::::::::::::::::::::::::::::

end;

Объяснение.

1).Имена  типов, вроде  real  и  integer, определяемые  автоматически, можно  объявлять  еще  раз, чтобы  отменить  их  исходное  определение. Но  так  делать   не  рекомендуется.  2).Целая переменная  g, определенная  в  главной  программе, не  известна  в  rishon, поскольку  она  “спрятана”  за  переменной  с  тем  же  именем  типа  b.  Тип  b  является  глобальным, он  известен  и  в rishon.

3).В  rishon  используем  постоянную  c  типа  b(этот  тип  определен  в  главной  программе), чтобы  объявить  постоянную  h. Если  заново  определить  b  в  rishon, декларация  h  будет  лишена  смысла.

4).Нет  связи  между  переменной  h, определенной  в  rishon,  и  h, объявленной  в  sheni_chizoni, поскольку  они  находятся  в  разных  областях. Переменная h  в  rishon  является  в  ней  локальной, поэтому  используемая  ею  память  освобождается  по  завершению  исполнения  rishon. 

5).Переменная  j  в  процедуре  sheni_pnimi  скрывает  j  из  sheni_chizoni. Тип  a, определенный  в  процедуре  sheni_chizoni,  содержит  значения  k, j, h.  h  и  k  еще  действительны  в  sheni_pnimi, а  j  - нет. Можно  обращаться  к  j  с  помощью  succ(h)  или   pred(k), но  предпочтительнее  вообще  не  пользоваться  типом  a  в  sheni_pnimi, чтобы  сам  программист  или  читатель  не  запутались. Описание  интервала  и  области  различных  имен  в  этой  программе  дается  в  следующей  схеме: (Рис. 8).

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

Желательно  определять  имена  локальными. Это  препятствует  возможности  изменения  значения  глобальной  переменной  в  результате  использования  другой  переменной  с  тем  же  именем. Для  сохранения  значения  переменной  между  запусками  некоей  подпрограммы  можно  пользоваться  параметрами  переменными. Следует  определять    глобальный  тип  переменной  для  описания  типа  формальных  параметров  в  декларации  процедуры  или  функции.  

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

Использование  локальных  переменных. Мы  хотим  узнать  среднее  значение  некоторых  измерений  в  течение  месяца. Каждый  день  производят  от  3  до  10  измерений, получают  из  них  среднее  значение  за  день, а  затем  среднее  из  этих  средних  за  полный  месяц. Представим  решение  в  следующей  программе(rochav - ширина, revachim - интервалы, пробелы, mispar - число, memuza shura - среднее в  строке,       memuza  sofi  - конечное  среднее,   schum - сумма,  mshoch  kav - проведи  линию):

program global_local;                                                                                           {1}

const rochav = 10;                                                                                                {2-3}

      revachim = 5;                                                                                                   {4}

var                                                                                                                         {5}

 j,i,n,mispar: integer;                                                                                               {6}

 memuza_shura, memuza_sofi, schum: real;                                                         {7}

procedure mshoch_kav;                                                                                       {8}

var i: integer;                                                                                                         {9-10}

begin                                                                                                                    {11}

 for  i := 1 to rochav do write('*');                                                                          {12-13}

 writeln                                                                                                                   {14}

end;                                                                                                                       {15}

function memuza(mispar_netunim: integer): real;                                                {16}

var  i: integer;                                                                                                        {17-18}

     schum, mispar: real;                                                                                         {19}

begin                                                                                                                     {20}

 schum := 0;                                                                                                           {21}

 write('Vvedi ',mispar_netunim,' dannih: ');                                                              {22}

 for  i := 1 to mispar_netunim do                                                                           {23}

 begin                                                                                                                     {24}

  read(mispar);                                                                                                         {25}

  schum := schum + mispar;                                                                                   {26}

 end;                                                                                                                       {27}

 readln;                                                                                                                    {28}

 memuza := schum/mispar_netunim;                                                                      {29}

end;                                                                                                                        {30}

begin                                                    (*Glavnaia programma*)                           {31}

 schum := 0;                                                                                                           {32}

 write('Skolko dnei voidut v okonchatelnoe srednee?: ');                                         {33}

 readln(n);                                                                                                               {34}

 for i := 1 to n do                                                                                                    {35}

 begin                                                                                                                    {36}

  write('Skolko izmerenii bilo v den #',i,': ');                                                             {37}

  readln(mispar);                                                                                                    {38}

  memuza_shura := memuza(mispar);                                                                  {39}

  for j := 1 to revachim do writeln;                                                                         {40-41}

  mshoch_kav;                                                                                                       {42}

  writeln(memuza_shura:rochav:2);                                                                       {43}

  mshoch_kav;                                                                                                       {44}

  schum := schum + memuza_shura                                                                     {45}

 end;                                                                                                                      {46}

 writeln;                                                                                                                  {47}

 mshoch_kav;                                                                                                        {48}

 mshoch_kav;                                                                                                        {49}

 memuza_sofi := schum/n;                                                                                    {50}

 writeln('Srednaa srednih ravna ', memuza_sofi:rochav:2);                                    {51}

 mshoch_kav                                                                                                         {52}

end.                                                                                                                       {53}

Пример  прогона:

Skolko dnei voidut v okonchatelnoe srednee?: 30

Skolko izmerenii bilo v den #1: 3

Vvedi  3  dannih:  4   6   7

{Пять  пробельных  строк}

**********

        5.67

**********

Skolko izmerenii bilo v den #2  {И  т.д.  всего  30  раз}

**********

**********

Srednaa srednih ravna  6.57

**********

В  программе  имеются  переменные, объявленные  не  один  раз, например, i, schum, mispar.  В  строке  {40}  нужно  использовать  j, а  не  i, т.к. внешний  цикл  использует  i.  Чтобы  понять  взаимоотношения  переменных, рассмотрим  следующую  иллюстрацию, в  которой  указаны  декларации  переменных  программы  и  границы  их  действия.

Program global_local;                                                                                           

const rochav = 10;                                                                                               

          revachim = 5;                                                                                                   

var                                                                                                                        

 j,i,n,mispar: integer;                                                                                               

 memuza_shura, memuza_sofi, schum: real;

Видимые  переменные:

global_local                                                                                          

rochav,  revachim                                                                                                    

j, i, n, mispar                                                                                              

memuza_shura, memuza_sofi, schum

mshoch_kav, memuza    главной  программы.

procedure mshoch_kav;                                                                                      

var i: integer;                                                                                                        

begin                                                                                                                     :::::::::::::

end;

i  процедуры  mshoch_kav,                                                                                          

rochav,  revachim, j,  n, mispar, memuza_shura, memuza_sofi, schum   главной  программы.

function memuza(mispar_netunim: integer): real;                                                

var  i: integer;                                                                                                       

     schum, mispar: real;                                                                                         

begin                                                                                                                     

 ::::::::::::::::::::::::::::::::

end;

mispar_netunim, i, schum, mispar   функции  memuza,                                                                                          

rochav,  revachim, j,  n, memuza_shura, memuza_sofi    главной  программы.

begin  (*главная  программа*)

  ::::::::::::

end.

 

Обрати  внимание  на  то, что  переменная  schum  равна  положительному  числу  в  конце  исполнения  функции  memuza, но  это  не  влияет  на  значение  schum  в  строке  {45}. И  конечное  значение  i  в  строках   {23-27}  не  влияет  на  его  значение  в  строках  {35-46}, где  оно  отмечает  число  вычисленных  средних.  Также  не  сталкиваются  muspar  в  строке  {38}, здесь  это  число  измерений  в  течение  определенного  дня,  с  mispar  в  строке  {25} - значением  каждого  измерения.

Имена  процедур  здесь  являются  глобальными, областью  имен  mshoch_kav  и  memuza  является  вся  программа. Поэтому  запрещено  изменять  имя  переменной  memuza_sofi  на  memuza, поскольку  последнее  имя  определено  как  имя  функции  в  той  же  области(главная  программа).

 

7.5.Комбинированные  структуры (*)

Можно  разделить  структуру  процедуры  и  функции  на  две  части:

a).Объявление  подпрограммы  или  заголовок.  Объявление  включает  следующие  элементы:   ·слово  procedure  или  function; ·имя  подпрограммы;   ·имена  формальных  параметров  в  скобках;    ·тип  имени  функции  при  определении  последней.

b).Тело  подпрограммы  или  блок.  Тело  подпрограммы, содержащее  исполняемые  команды,  следует  за  заголовком.

Когда  тело  подпрограммы  отделяется  от  ее  декларации, как  это  мы  увидим  ниже,  необходимо  написать  за  заголовком  указание, называемое  директивой(directive).  Общая  форма  объявления  директивы такова: (Рис. 9   Объявление).

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

a).Когда  подпрограмма  некоторого  уровня  вызывает  другую  подпрограмму  более  низкого  уровня. Без  forward  сперва  нужно  писать  подпрограмму  более  низкого  уровня, поскольку  к  ней  обращаются  в  дальнейшем, а  затем  пишут  вызывающую  подпрограмму. Чтобы  поправить  такое  положение, пишут  лишь  заголовок  подпрограммы  более  низкого  уровня, а  за  заголовком - слово  forward. Тело  этой  подпрограммы  можно  написать  позднее  в  программе.

b).Когда  подпрограмма  А  вызывает  В,  и  подпрограмма  В  вызывает  А. В  этом  случае  нужно  упомянуть  имя  одной  из  подпрограмм  перед  написанием  ее  тела. После  этого  пишут  тело  второй  подпрограммы, которая  теперь  может  обратиться  к  первой  с  уже   объявленным  именем. Затем  пишут  тело  подпрограммы, чье  имя  упомянуто  первым.

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

program metziat_rishoni;                                                                                   {1}

type mispar_chiuvi = 1..maxint;                                                                          {2-3}

procedure kria(var mispar: integer); forward;                                                  {4}

(*Procedura chitaet luboe chislo s zapatoi posle tisach ili bez*)                         {5}

function rishoni(p: mispar_chiuvi): boolean; forward;                                      {6}

(*Procedura ustanavlivaet: chislo prostoe ili net*)                                               {7}

procedure kria_vehadpasa;                                                                               {8}

var k: mispar_chiuvi;                                                                                          {9-10}

       j: integer;                                                                                                      {11}

      nimtza: boolean;                                                                                            {12}

begin                                                                                                                  {13-14}

 kria(j);                                                                                                                 {15}

 if not (j > 0) then                                                                                               {16}

 begin                                                                                                                 {16a}

  writeln('Ne goditsa chislo ',j,'<=0. Ili Ctrl-Z posle nesk.probelov');                     {17}

  writeln                                                                                                                {17a}

 end                                                                                                                     {17b}

  else                                                                                                                    {18}

  begin                                                                                                                 {19}

   write('Prostoe chislo, naibolee blizkoe k ',j,' est ');                                             {20}

   if rishoni(j) then writeln(j)                                                                                 {21-22}

    else                                                                                                                 {23}

    begin                                                                                                              {24}

     nimtza := false;                                                                                               {25}

     if odd(j) then k := 2                                                                                        {26-27}

      else k := 1;                                                                                                    {28-29}

     repeat                                                                                                            {30}

      if rishoni(j+k) then                                                                                         {31}

      begin                                                                                                             {32}

       nimtza := true;                                                                                              {33}

       writeln(j+k);                                                                                                   {34}

      end;                                                                                                               {35}

      k := k + 2                                                                                                        {36}

     until nimtza;                                                                                                    {37}

     writeln                                                                                                              {38}

    end                                                                                                                   {39}

 end                                                                                                                      {40}

end;                                                                                                                      {41}

function rishoni;                                                                                                   {42}

var shoresh,mechalek: mispar_chiuvi;                                                                  {43-44}

begin                                                                                                                     {45}

 if p < 4 then rishoni := p > 1                                                                                 {46-47}

  else                                                                                                                      {48}

   if not odd(p) then rishoni := false                                                                      {49-50}

    else                                                                                                                    {51}

    begin                                                                                                                 {52}

     shoresh := round(sqrt(p));                                                                                 {53}

     mechalek := 3;                                                                                                   {54}

     while (mechalek <= shoresh) and (p mod mechalek <> 0) do                        {55}

       mechalek := mechalek + 2;                                                                              {56}

     rishoni := mechalek > shoresh                                                                           {57}

    end                                                                                                                     {58}

end;                                                                                                                        {58a}

procedure kria;                                                                                                      {59}

var ch: char;                                                                                                           {60-61}

       shlili: boolean;                                                                                                  {62}

begin                                                                                                                     {63}

 mispar := 0; (*Konechnoe znachenie dannogo budet v peremennoi “mispar”*)     {64}

 repeat          (*Probeli v nachale vvoda nado propustit*)                                       {65}

  read(ch)                                                                                                                {66}

 until (ch <> ' ') or eof;                                                                                           {67}

 shlili := false;                                                                                                         {67a}

 if (ch = '+') or (ch = '-') then                                                                                  {68}

 begin                                                                                                                     {69}

  shlili := ch = '-';                                                                                                      {70}

  read(ch)          (*Prochti pervuu cifru*)                                                                  {71}

 end;                                                                                                                       {72-74}

 while (ch <> ' ') do                                     (*Chitai simvoli i sobirai chislo*)          {75}

 begin                                                                                                                      {76}

  if (ch >= '0') and (ch <= '9') then                                                                          {77}

   mispar := 10*mispar + ord(ch) - ord('0');                                                              {78}

  read(ch)                                                                                                                 {79}

 end;                                                                                                                        {80}

 if shlili then mispar := -mispar                                                                                {81-82}

end;                                                                                                                          {83}

begin                                                      (*Glavnaia programma*)                           {84}

 writeln('Naberi chislo-probel,chislo-probel...V konze Ctrl-Z. Enter!');                      {84a}

 while not eof do kria_vehadpasa                                                                           {85-86}

end.                                                                                                                         {87}

Прогон  для  примера

Naberi chislo-probel,chislo-probel...V konze Ctrl-Z  i  Enter!

6,632    57  -47   89  +567 ^Z

Prostoe chislo, naibolee blizkoe k  6632  est  6637

::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

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

(1).Проверка, не  является  ли  само  j  простым(строка  {21}).

(2).Проверка, если  j  не  простое, не  является  ли  оно  четным(строка  {26}).

(3).Если  оно  четное, проверяем  на  простое  число  следующее  за  ним нечетное  число   j+1, а  если  нечетное, то  проверяем  на  простое  число  j+2 (строка  {31}).

(4).Если  число, проверенное  на  этапе  (3)  не  простое, продвигаемся  еще  на  две  единицы({36})  и  проверяем  очередное  число  на  простое.  Так  продолжаем, пока  не  натолкнемся  на  простое  число. Аналогичным  образом  можно  было  найти  ближайшее  простое  к  j, но  меньше  его. Для  этого  надо  заменить  в  строках  {31}  и  {34}  выражение  j+k  на  j-k.

Объяснение  функции  rishoni. Все  положительные  числа  меньше  4-х(кроме  1(?)) - простые. Отсюда  булево  значение  выражения  p > 1(когда  p < 4)  отвечает  на  вопрос    о  простом  числе  меньше  4-х(строка  {47}). Если  ответ  равен  true(т.е. имеем  2  или  3), то  число  простое. Если  p ³ 4  и  нечетное, нужно  проверить  все  целые  числа  от  1  до  Φp  и  посмотреть, не  делится  ли  p  на  одно  из  них  без  остатка. p  делится  на  все  целые  числа от  3  до  Φp(строка  {55}). Если  на  одно  из  этих  чисел  оно  делится  без  остатка, выходим  из  цикла. Выход  из него  возможен  в двух  случаях: a).когда  добираемся  до  Φp  и  ни  одно  деление  не  было  без  остатка;  b).когда  до  Φp  не  доходим, но  одно  из  действий деления  было  без  остатка.

Для  проверки  причины выхода  из  цикла(a’ - p  простое,  или  b’ - p  не  простое)  достаточно  действия  в  строке  {57}.   Причина, по  которой  цикл  в  строке  {55}  может  дойти  только  до  Φp, состоит  в  том, что  если  p  не  является  простым, то  его  можно  представить  в  виде  p = a*b.  Есть  два  пути  решения  этого  уравнения:  a). a = b = Φp;  b).один  из  сомножителей  a  или  b  меньше  Φp. Если  p  разложимо, один  из  сомножителей  должен  быть  меньше  или  равен  Φp.  Поэтому, если  при  проверке  не  находится  множитель  меньше  или  равный  Φp, который  делит  p  без  остатка, то  это  объясняется  тем, что  такого  сомножителя  нет, и  p - простое  число.

Перед  телом  функции  в  строке  {42}  написано   function rishoni;  без  указания  параметров. Такая декларация  называется  идентификацией  функции (Function  Identification), она - вроде  заголовка. Она  соответствует  тому, что  полное  описание  уже  один  раз  в  программе  дано  с директивой  forward. И  теперь  достаточно  предварить  тело  функции  ее  идентификатором  без  полного  объявления.  Аналогично  поступили  с  процедурой  kria,  сравни  строки  {4}  и  {59}.

Объяснение  процедуры  kria.  Эта  процедура  реализует  часть  действий, выполняемых  процедурами  write  и  writelen, которые  определены  автоматически. Символы  числа  читаются  как  переменные  типа  char  в  строке  {66}. Если  символ  есть  знак  минус, то  это  принимается  во  внимание(строка  {70}).  Цифры  читаются  слева  направо, каждая  из  них  переводится  из  представления  char  в  ее  числовое  представление  в  соответствии  с  ее  позицией  в  числе. Например, самая  левая  цифра  6  числа  6632  умножается  на  10  трижды, следующая  за  ней  шестерка  умножается  на  10  два  раза, тройка  умножается  на  10  один  раз, а  двойка - ни  разу, т.к.  справа от  нее  нет  цифр. Если  в  числе  есть  запятая, иной  знак  препинания  или  буква, процедура  его  игнорирует  и  продолжает  работать  дальше. Отсюда  следует, что  эта  процедура  не  может  работать  с  вещественными  числами.

 

7.6.Функции  и  процедуры  управления  терминалом

7.6.1.Изменение  размера  курсора

Иногда  хотят  уменьшить  или  увеличить  курсор(cursor), а  иногда - вовсе  убрать  его. Курсор  может  состоять  не  более  чем из 14  строчек  пиксел  на  монохромном  экране  и  из  8  строчек  на  цветном(godel - размер,  saman - курсор, sug - тип):

program cursor;                                                                                          {1}

uses dos, crt;                                                                                               {2}

procedure godel_saman(sug, godel: char);                                                {3}

var regs: registers;                                                                                       {4-5}

    i: integer;                                                                                                   {6}

begin                                                                                                            {7}

 godel := upcase(godel);                                                                                {8}

 if upcase(sug) = 'M'  then i := 6                                                                   {9-10}

  else  i := 0;                                                                                                  {11-12}

 regs.ah := $01;                                                                                             {13}

 case godel of                                                                                               {14}

  'A': begin                                                                                                     {15-16}

        regs.ch := 32;                                                                                        {17}

        regs.cl := 0                                                                                            {18}

       end;                                                                                                       {19}

  'G': begin                                                                                                    {20-21}

        regs.ch := 0;                                                                                          {22}

        regs.cl := 7 + i                                                                                        {23}

       end;                                                                                                        {24}

  'K': begin                                                                                                     {25-26}

        regs.ch := 6 + i;                                                                                     {27}

        regs.cl := 7 + i                                                                                       {28}

       end;                                                                                                       {29}

  end;                                                                                                            {30}

  Intr($10,regs)                                                                                              {31}

end;                                                                                                               {32}

 

begin                                                                                                             {33}

 clrscr;                                                                                                            {34}

 writeln('Bolshoi kursor');                                                                                {35}

 godel_saman('Z','G');                                                                                    {36}

 writeln;      write('Najmi Enter!');     readln;                                                    {37-39}

 

 writeln;      writeln;                                                                                         {40-41}

 writeln('Net kursora');                                                                                    {42}

 godel_saman('Z','A');                                                                                    {43}

 writeln;       write('Najmi Enter!');     readln;                                                   {44-46}

 

 writeln;       writeln;                                                                                       {47-48}

 writeln('Malenkii kursor');                                                                              {49}

 godel_saman('Z','K');                                                                                    {50}

 writeln;        write('Najmi Enter!');       readln;                                                {51-53}

end.                                                                                                               {54}

Объяснение.

{34}Команда  clrscr  очищает  экран  от  всего, что  на  нем  было  прежде.

{36}Процедура  принимает  два  параметра. Первый  из  них  относится  к  типу  экрана,  от  которого  зависит  курсор. Если  пишем  ‘M’, то  полагаем, что  экран  монохромный, а  если  ‘Z’, то  полагаем, что  цветной. Второй  параметр  может  быть  ‘G’ - для  обозначения  “большого  курсора”,  ‘A’ - для  обозначения  “отсутствия  курсора”  и  ‘K’ - для  обозначения  “маленького  курсора”.

Процедура  godel_saman

{8}Функция  upcase  переводит  маленькие  английские  буквы(вроде ‘a’)  в  большие (‘A’). Этот  перевод  позволяет  нам  полагать, что  параметрами  подпрограммы  всегда  будут  большие  буквы.

Размер  курсора  обозначается  при  введении  его  верхнего  предела  в  старший  байт  регистра  CX (т.е. в  CH), а  нижнего  предела  в  младший  байт  регистра  CX (т.е.  в  CL).  Переменная  regs  имеет  тип  registers, определенный  в  Турбо  Паскале, где

registers = record

  case  integer  of

     0: (AX, BX, CX, DX, BP, SI,  DI,  DS, ES, Flags: word);

     1: (AL, AH, BL, BH, CL, CH, DL, DH: byte)

  end;

{15}Здесь  выбирается  сокрытие  курсора. Чтобы  это  сделать, устанавливают  32  в  качестве  верхнего  значения  размера  курсора(нижний  размер  не  имеет  значения).

{20}Здесь  выбран  маленький  курсор. Он  ограничен  строчками  6  и  7  на  цветном  экране, и  строчками  12  и  13 - на  монохромном. Т.е.  он  появляется  как  нижняя  черта   ( _ ). Если  определить  его  между  строчками  1  и  2, он  будет  выглядеть  верхней  черточкой (`   ).

 

7.6.2.Различные  действия  с  монохромным  и  цветным  экранами

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

program colors;                                                                                             {1}

uses crt;                                                                                                         {2}

var i, j: integer;                                                                                               {3-4}

procedure shachor_lavan_proc;                                                                   {5}

begin                                                                                                             {6}

 writeln('Dlia prodoljenia najmi Enter');                                                           {7}

 readln;          clrscr;                                                                                      {8-9}

 window(20, 5, 60, 15);                    lowvideo;                                               {10-11}

 writeln('Belim po chernomu, malaia iarkost. Enter!');                                    {12}

 readln;           highvideo;                                                                               {13-14}

 writeln('Belim po chernomu, visokaia iarkost. Enter!');                                 {15}

 readln;                                                                                                          {16}

 textattr := blink + lightgray + 16*black;                                                         {17}

 writeln('Belim po chernomu, malaia iarkost, mercanie. Enter!');                   {18}

 readln;                                                                                                          {19}

 textattr := blink + white + 16*black;                                                               {20}

 writeln('Belim po chernomu, visokaia iarkost, mercanie. Enter!');                 {21}

 readln;                                                                                                          {22}

 textbackground(lightgray);                                                                            {23}

 (*Nelza izmenat iarkost v sostoianii <reverse video>*)                                 {23a}

 textcolor(black);                                                                                            {24}

 writeln('Chernim po belomu<reverse video>. Enter!');                                  {25}

 readln;                                                                                                          {26}

 textattr := black + 16*lightgray + blink;                                                         {27}

 writeln('Chernim po belomu<reverse video>,mecanie. Enter!');                   {28}

 readln;                                                                                                         {29}

 textcolor(white);                                                                                           {30}

 textbackground(black);                                                                                {31}

 writeln('Black on black.Soobshenie v sleduushei stroke ne vidno');             {32-33}

 textbackground(black);                                                                                {34}

 textcolor(black);                                                                                           {35}

 writeln('Nevidimoe soobshenie');                                                                 {36}

 textcolor(white);                                                                                           {37}

 textbackground(black);                                                                                {38}

 writeln('Soobshenia v predidushei stroke ne vidno. Enter!');                        {39}

 readln;                                                                                                         {40}

 textcolor(blue);                                                                                             {41}

 textbackground(black);                                                                                {42}

 writeln('Pod etim soobsheniem provedena linia,iarcost nizkaia.Enter!');      {43}

 readln;                                                                                                         {44}

 textcolor(lightblue);                                                                                      {45}

 textbackground(black);                                                                                {46}

 writeln('Pod etim soobsheniem provedena linia,iarcost visokaia.Enter!');    {47}

 readln;                                                                                                         {48}

 textattr := blink + blue + 16*black;                                                               {49}

 writeln('Pod soobsh. provedenana linia,iarcost nizkaia,mercanie.Enter!');   {50}

 readln;                                                                                                          {51}

 textattr := blink + lightblue + 16*black;                                                         {52}

 writeln('Pod soobsh. provedena linia,iarcost visokia,mercanie.Enter!');       {53}

 readln;                                                                                                         {54}

end;                                                                                                              {55}

procedure tzevaim_proc;                                                                            {56}

var i, j: integer;                                                                                             {56a}

begin                                                                                                           {57}

 writeln('Dlia prodoljenia dvajdi najmi na Enter');                                          {58}

 readln;                                                                                                        {59}

 textbackground(black);                                                                               {60}

 clrscr;                 window(20, 5, 60, 15);                                                     {61-62}

 for i := 0 to 7 do                                                                                         {63}

  for j := 0 to 15 do                                                                                      {64}

  begin                                                                                                         {65}

   textbackground(i);                                                                                     {66}

   textcolor(j);                                                                                                {67}

   clrscr;                                                                                                        {68}

   writeln('Na fone ',i,' zvet ',j,'     ');                                                                {69}

   writeln('Dla prodoljenia najat na Enter');                                                    {70}

   readln                                                                                                        {71}

  end;                                                                                                           {72}

 for i := 0 to 7 do                                                                                         {73}

  for j := 0 to 15 do                                                                                      {74}

  begin                                                                                                         {75}

   textbackground(i);                                                                                     {76}

   textcolor(j+128);                                                                                        {77}

   clreol;                                                                                                        {78}

   writeln('Na fone ',i,' zvet ',j,' s mercaniem ');                                              {79}

   clreol;                                                                                                        {80}

   writeln('Dla prodoljenia najat na Enter');                                                    {81}

   readln                                                                                                        {82}

  end;                                                                                                           {83}

 textattr := yellow + 16*blue + blink;                                                             {84}

 writeln('Jeltoe migaet na golubom(s pomoshiu perem. textattr. Enter!');     {85}

 readln;                                                                                                         {86}

 textattr := yellow + 16*blue;                                                                         {87}

 writeln('Enter!');                     readln;                                                           {89}

end;                                                                                                             {90}

begin                                                                                                           {91}

 clrscr;                                                                                                          {92}

 writeln('Proverka tipa ekrana');                                                                     {93}

 if mem[$0:$449] = 7 then                                                                           {94}

 begin                                                                                                          {95}

  writeln('MONOHROM');                                                                             {96}

  shachor_lavan_proc;                                                                                 {97}

 end                                                                                                             {98}

  else                                                                                                           {99}

  begin                                                                                                        {100}

   writeln('POLIHROM');                                                                              {101}

   tzevaim_proc;                                                                                          {102}

  end;                                                                                                          {103}

 window(1,1,80,25);   textbackground(black);   textcolor(lightgray);            {103a,b,c}

 clrscr;                                                                                                        {103d}

end.                                                                                                           {104}

Объяснение. Экран  разделен  на  25  строк(1..25), в  каждой  по  80  символьных  позиций(1..80). Компьютер  хранит  все  данные  относительно  экрана  в  специальном  месте  памяти. На  каждый  байт  символа  экрана  в  этой  памяти  отводится  два  байта, второй  содержит  все  свойства  выводимого  символа. Поэтому  для  всего  экрана    (80*25 = 2000 символов)  требуется  память  в  4000  байтов, в  т.ч.  2000  байтов  для  представляемых  на  экране  символов  и  еще  2000  для  описаний  свойств  каждого  из  них. Байт  свойств  выглядит  так:

 7       6      5       4       3        2       1       0

М

Ф

Ф

Ф

Я

С

С

С

          Цвет  фона               Цвет  символа

Представление  каждого  символа  содержит  два  элемента: описание  самого  символа (foreground)  и  его  фона (background). Младшая  часть  байта(биты  0 - 2)  описывают  цвет  символа, а  три  бита  старшей  части  байта(биты 4 - 6)  относятся  к  цвету  его  фона. Эти  три  бита С  и  Ф  позволяют  описать  8  цветов(от  000(0) до 111(7)).  000(0)  обозначают  черный  цвет, 001(1)- синий. Единственное различие  между  описаниями  символа  и  фона  состоит  в  том, что  бит  3  задает  яркость  цвета, а  у  фона  нет  такого  данного. Поэтому  у  фона  8  различных  сочетаний  1  и  0, а  у  символа  таких  сочетаний  16(в  битах  0-3).  Мерцание  задается  “включением”  седьмого  бита.  У  каждого  сочетания  есть  и  число  и  имя. Эти  имена  даны в  следующей  таблице:

const

  black

  blue

  green

  cyan

  red

  magneta

  brown

  lightgray

  darkgray

  lightblue

  lightgreen

  lightcyan

  lightred

  lightmagneta

  yellow

  white

  blink

 

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

=

 

0;

1;

2;

3;

4;

5;

6;

7;

8;

9;

10;

11;

12;

13;

14;

15;

128;

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

1.Для  сокрытия  вывода - когда  фон  и  символ  устанавливаются  одного  цвета, черного.

2.Яркое  письмо - включением  бита  яркости(бит  номер  3).

3.Проведение  линии  под  изображенным  символом  путем  установления  синего  цвета  символа  и  черного  цвета  фона.

4.Представление  символа  в  обратном  виде(reverse) - вместо  белого  символа  на  черном  фоне  возможен  черный  символ  на  белом. К  такому  результату  можно  прийти  установлением  светлосерого (lightgray)  цвета  фона  и  черного  цвета  символа. Возможны  различные  сочетания  упомянутых  качеств  как  мы  видели  в  примере.

Строка  {94}  в  программе  colors: значение  7, находящееся  на  месте  mem($0:$449), обозначает, что  экран - монохромный  и  не  способен  представлять  иные  цвета. Другие  значения  обозначают, что  экран  цветной. Другой  путь  достижения  того  же  результата  состоит  в  задействовании  прерывания  intr($10, regs), которое  мы  уже  использовали  при  изменении  размера  курсора. Ниже  дана  функция, демонстрирующая  этот  способ (sug  masach - тип  экрана):

function sug_masach;                                                                                  {1}

var  regs: registers;                                                                                       {2-3}

begin                                                                                                             {4}

 fill_char(regs, size_of(regs), 0);                                                                    {5}

 reg.AH = $of;                                                                                                {6}

 intr($10, regs);                                                                                              {7}

 case  regs.ALL  of                                                                                       {8}

   1..6:     sug_masach := ‘c’;                    (*CGA*);                                       {9}

   7:         sug_masach := ‘m’;                   (*monochrome*);                          {10}

   13..16: sug_masach := ‘e’;                    (*EGA*);                                       {11}

 end                                                                                                              {11a}

end;                                                                                                              {12}

Объяснение  функции.

{5}Процедура  fill_char  заполняет  переменную  regs(первый  параметр) нулями(третий  параметр). Число  нулей  задается  вторым  параметром size_of(regs). size_of  это  функция, которая  поставляет  число  байтов(размер) своего  параметра(здесь regs).

{6}Заполнение  регистра  AH  перед  прерыванием  $10.

{8}Результаты  прерывания  помещены  в  регистр  AL.

Процедура  shachor_lavan_proc (shachor - черный, lavan - белый)

{10}Процедура  window(x1, y1, x2, y2)  приводит  к  тому, что  все  команды, влияющие  на  экран, такие  как  write, read, clrscr, gotoxy  и  т.п.  не  связываются  со  всем  обычным  экраном, а  относятся  к  его  окну, чей  левый  верхний  угол  находится  в  позиции         (х1, у1),  а  правый  нижний  в  позиции  (х2, у2).

{11}Существует  переменная  длиной  1  байт, именуемая  textattr, в  которой  определены свойства  выводимых  на  этом  этапе  байтов. Ее  биты  можно  изменять  различными  способами. Один  из  них  состоит  в  использовании  процедур  lowvideo  и  highvideo. Первая  функция  гасит  бит  яркости, что  приводит  к  превращению  цветов  с  номерами  8-15  в  тусклые  цвета  с  номерами  0-7. Процедура  highvideo  имеет  обратное  действие. {17}Включать  биты  textattr  можно  используя  постоянные, определенные  выше. Постоянную, предназначенную  символу,  умножаем  на  16, чтобы  изменить  4  левых  бита, отведенных  фону.

{20}Подобно  {17}, но  бит  яркости  включен  для  замены  цвета  символа  lightgray  на  white.

{23}В  процедуре  textbackground  можно  использовать  постоянные  black..lightgray(0..7)  для  указания  цвета  фона. Мы  могли  сделать  это  подстановкой  этих  постоянных, умноженных  на  16, в  переменную  textattr, но  использование  указанной  процедуры  удобнее  и  яснее.

{24}В  процедуре  textcolor  можно  использовать  постоянные  black..white(0..15)  для  указания  цвета  символа. Мы  могли  сделать  это  подстановкой  указанных  постоянных  в  переменную  textattr, но  использование  и  этой  процедуры  удобнее  и  яснее.

{27}Сочетание  мерцания  и  обратного  написания(reverse..video).

{34-35}Превращение  ввода/вывода  в  невидимые  установлением одинакового(черного)  цвета  фона  и  символа.

{41}Окраска  символа  как-бы  в  голубой (blue)  цвет  на  монохромном  экране  приводит  к  появлению  светлосерых  подчеркнутых  снизу  символов.

{45}Окраска  символа  как-бы  в  светлоголубой (lightblue)  цвет  на  монохромном  экране  приводит  к  появлению  белых  подчеркнутых  снизу  символов.

{49}Сочетание  мерцания  и  подчеркивания  под  тусклыми  символами.

{52}Сочетание  мерцания  и  подчеркивания  под  яркими  символами.

Процедура  tzevaim_proc (tzevaim - цвета)

{58}Для  продолжения  надо  дважды  нажать  на  Enter. Первый  раз  из-за  readln; в  59  строке, а  второй, потому  что  указание  на  readln  в  строке  71(см. строки  69-70)  не  видно  из-за  тождества  цвета  символа  и  фона(i = j = 0).

{61)Очистка  всего  экрана.

{62}Установление  окна  в  центре  экрана. Все  сообщения  и  краски  отсюда  и  далее  относятся  исключительно  к  этому  окну, а  остальная  часть  экрана  остается  черной, этот  цвет  фона  установлен  в  строке  {60}.

{63-72}Представление  всех  сочетаний  цвета  фона  и  символа, чтобы  пользователь  мог  выбрать, что  ему  подходит.

{63-64}Значение  i  указывает  на  цвет  фона. Оно  принимает  все  8  возможных  цветов, пронумерованных  от  0  до  7  согласно  приведенной  таблице. Значение  j  задает  цвет  символа, возможны  все  16  цветов, пронумерованных  от  0  до  15  в  той  же  таблице.

{68}Очистка  окна  и  установление  курсора  в  левом  верхнем  его  углу.

{73-83}Еще  одно  представление  всех  сочетаний  цветов  фона  и  мигающих  символов.

{78}Очистка  строки  без  смещения  курсора. Цвет  очищенной  строки  задает  textbackground.

{84}Установление  мерцания  для  некоторого  сочетания  с  помощью  переменной textattr.

{87}Отмена  предыдущего  мерцания.

7.6.3.Установление  дат  и  времен

Для  объяснения, как  устанавливают  дату  и  время, воспользуемся  примером:

program tim_dat;                                                                                      {1}

uses dos, crt;                                                                                            {2}

var shana, chodesh, yom, yom_bashavua,                                               {3-4}

       shaa, daka, shniya, meit_shniya: word;                                              {5}

begin                                                                                                        {6}

 clrscr;                                                                                                       {7}

 getdate(shana, chodesh, yom, yom_bashavua);                                      {8}

 writeln('Den nedeli   Den   Mesac   God');                                                {9}

 writeln(yom_bashavua, yom:14, chodesh:7, shana:8);                             {10}

 write('Dla prodoljenia najmi Enter');      readln;                                          {11-12}

 gettime(shaa, daka, shniya, meit_shniya);                                                {13}

 writeln('Chasi   Minuti   Sekundi   Sotie sekundi');                                    {14}

 writeln(shaa, daka:11, shniya:11, meit_shniya:11);                                  {15}

 write('Dla prodoljenia najmi Enter');      readln;                                         {16-17}

end.                                                                                                          {18}

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

Процедура  getdate({8})  возвращает  год, номер  месяца, номер  дня  в  месяце  и  день  недели. Значение  0  означает  воскресенье, 1 - понедельник, 6 - субботу. Процедура  gettime({13})  возвращает  текущее  время, состоящее  из  часов, минут, секунд  и  сотых  долей  секунды.

7.6.4.Звуки  и  звонки

Существует  две  возможности  издавать  звуки.  Один  состоит  в  использовании  процедуры  sound, а  второй  задействует  звонок. Эти  возможности  показаны  в  следующей  программе:

program sounds;                                                                                   {1}

uses crt;                                                                                                 {2}

begin                                                                                                      {3}

 textbackground(black);                                                                           {4}

 clrscr;                                                                                                     {5}

 writeln('Zvuchanie nekotoroe vremia(420 gerc,1.2 sec)');                      {6}

 sound(420);      delay(1200);    nosound;                                               {7-9}

 writeln('Najmi Enter, chtobi uslishat zvonok');                                        {10}

 readln;                                                                                                    {11}

 writeln(^g);                                                                                             {12}

 write('Dla vozvrashenia v programmu najmi Enter');      readln;             {13-14}

end.                                                                                                       {15}

{7}Издание  звука  процедурой  sound. Параметр  в  скобках  указывает  частоту  в  герцах. С  увеличением  частоты  издается  более  выский  звук. Человеку  доступны  звуки  частототой  от  20  до  20000  герц. Звучание  продолжается  до  его  прерывания  процедурой  nosound(строка  {9}).

{12}Второй  путь  издания  звука  состоит  в  написании  символа  ^g(т.е. значения  07  в  ASCII)  в  операторе  вывода. Такой код  переводится  как  просьба  издать  звук. Это  действительно  происходит, когда  указанный  символ  выводится  на  экран.

7.6.5.Опознание  нажатой  оператором  клавиши

Иногда  нужно  повлиять  на  ход  исполнения  программы(чаще  всего - остановиться  и  продолжить) с  помощью  нажатия  на определенную  клавишу. Программа должна  эту  клавишу  отождествить  и  затем  действовать  согласно  установлениям  программиста. Для  этого  нужно  проверить  “код”  нажатой  клавиши(в  соответствии  с  таблицей  кодов  клавиш). Код  большинства  клавиш  соответствует  обычному  коду  ASCII. Например, код  буквы  ‘A’  есть  65  в  ASCII. В  таблице, представленной  ниже, все  символы, коды  ASCII  которых  находятся  в  пределах  от  32  до  126,  соответствуют  символам  на  клавиатуре.

                          (Расширенная  таблица  ASCII(256 кодов))

Вместе  с  тем  имеются другие  клавиши, присутствующие  на  клавиатуре  или  отсутствующие  на  ней, которые  имеют  расширенный  код, состоящий  из  двух  “под-кодов”. Один  из  них  00, а  второй - некое  число.

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

program get_keys;                                                                       {1}

uses crt;                                                                                        {2}

const Esc = #27;                                                                          {3}

var  c: char;                                                                                  {4-5}

       ascii: integer;                                                                         {6}

begin                                                                                           {7}

 writeln('Najimai na klavishi po odnoi, a dla zavershenia - Esc!'); {8}

 repeat                                                                                         {9}

  c := readkey;                                                                               {10}

  if c = #0 then                                                                             {11}

    writeln('Kod rasshirennii.Vtoraia chast koda ',readkey)            {12}

   else                                                                                          {13}

     writeln(c,' - klavisha prostaia, ee kod ASCII raven ',ord(c));    {14}

 until c = Esc;                                                                             {15}

 writeln('Ustanovlenie najatoi klavishi, naprimer F1');                   {16}

 c := readkey;                                                                               {17}

 if c = #0 then (*prodoljenie na sled. Str. posle tablici*)              {18}

 begin                                                                                         {19}

  c := readkey;                                                                             {20}

  if ord(c) = 59 then writeln('Najata klavisha F1')                        {21-22}

   else writeln('Klavisha F1 ne  najata')                                        {23-24}

 end;                                                                                           {25}

 writeln('Dla prodoljenia najmi Enter');                                         {26}

 readln;                                                                                        {27}

 repeat                                                                                        {28}

  write('Vvedi znachenie ASCII. Na ekrane budet ego simvol. '); {29}

  write('Nol - dla zavershenia: ');                                                  {29a}        

  read(ascii);                                                                                {30}

  if (ascii < 256) and (ascii >= 0) then writeln(chr(ascii))           {31-32}

   else writeln('Nezakonnii kod')                                                  {33}

 until ascii = 0                                                                            {34}

end.                                                                                            {36}

Объяснение.

{10}Ввод  первого  кода.

{11}Если  этот  код  равен  0, то  существует  еще  один  подкод, вводимый  в  строке {12}. В  этой  строке  выводится  сообшение  о  расширенном  коде  и  сам  второй  подкод.

{14}Если  у  символа  нет  расширенного  кода, а  есть  обычный  код, выводится  символ  и  его  значение  в  ASCII.

{16-25}Проверка, нажал  ли  оператор  специальную  клавишу, например  F1. Проверяют, соответствует  ли  клавише  код  0(строка  {18}). И  если  ответ  положительный, проверяют, не  равно  ли  59-и  значение  подкода(это  сочетание  обозначает  F1).

{28-35}Вывод  символа, соответствующего  произвольному  значению  ASCII.

7.6.6.Опознание  специальных  клавиш

Для  ряда  сочетаний  клавиш  не  существует  кода  ASCII  или  расширенного  кода. Следующая  программа  позволяет  обнаруживать  нажатие  таких  клавиш. Булевы  переменные  Alt, Ctrl, Leftshift  и  Rightshift  принимают  значение  true, когда  оператор  нажимает  на  эти  клавиши, и  false, когда  он  отпускает  их.  Переменные   Ins, Capslock, Numlock, Scrolllock  примут  значение  true  при  нажатии  на  эти  клавиши. Оно  остается  в  действии, пока  оператор  повторно  не  нажмет  на  них  для  изменения  положения.

program shift;                                                                                          {1}

uses dos, crt;                                                                                            {2}

var ins, capslock, numlock, scrolllock, alt, ctrl, leftshift,                            {3}

      rightshift: boolean;                                                                               {4}

procedure shiftstatus(var ins, capslock, numlock, scrolllock,                   {5}

                   alt, ctrl, leftshift, rightshift: boolean);                                        {6}

var regs: registers;                                                                                     {7-8}

begin                                                                                                         {9}

 regs.ah := 2;             Intr($16,regs);                                                          {10-11}

 rightshift := (regs.AL and $01) > 0;                                                           {12}

 leftshift := (regs.AL and $02) > 0;                                                             {13}

 ctrl       := (regs.AL and $04) > 0;                                                              {14}

 alt        := (regs.AL and $08) > 0;                                                              {15}

 scrolllock := (regs.AL and $10) > 0;                                                          {16}

 numlock    := (regs.AL and $20) > 0;                                                        {17}

 capslock   := (regs.AL and $40) > 0;                                                         {18}

 ins        := (regs.AL and $80) > 0;                                                             {19}

end;                                                                                                           {20}

begin                                                                                                         {21}

 clrscr;                                                                                                        {22}

 writeln('Dla zavershenia vkluchit  Ins  i  zatem Ctrl');                                 {23}

 repeat                                                                                                       {24}

  shiftstatus(ins, capslock, numlock, scrolllock,                                         {25}

              alt, ctrl, leftshift, rightshift);                                                          {25a}

  gotoxy(1,4);                                                                                              {26}

  writeln('Ins.............',ins,'    ');                                                                    {27}

  writeln('CapsLock........',capslock,'    ');                                                     {28}

  writeln('NumLock.........',numlock,'    ');                                                     {29}

  writeln('ScrollLock......',scrolllock,'    ');                                                     {30}

  writeln('Alt.............',alt,'    ');                                                                      {31}

  writeln('Ctrl............',ctrl,'    ');                                                                    {32}

  writeln('LeftShift.......',leftshift,'    ');                                                           {33}

  writeln('RightShift......',rightshift,'    ');                                                       {34}

 until ins and ctrl;                                                                                      {35}

 write('Press Enter');                                                                                   {36}

 readln                                                                                                        {37}

end.                                                                                                           {38}

 

7.6.7.Запуск  программы  из  другой  программы

Иногда  хотят  запустить  некую  программу  в  ходе  исполнения  другой  программы. Покажем  такую  возможность  в  следующем  примере.

procedure process_handling;                                                                        {1}

begin                                                                                                              {2}

 clrscr;                                                                                                             {3}

 writeln('Dla zapuska najmi Enter');       readln;                                               {4-5}

 exec('sounds.exe','');                                                                                      {6}

 if doserror <> 0 then writeln('DOS error #',doserror)                                     {7-8}

  else writeln('Successful EXEC. Child exit code = ',dosexitcode);                 {9-11}

 writeln('Dla rassmotrenia instrukcii najmi Enter');      readln;                          {12-13}

 exec('\command.com','/C dir *.pas');                                                              {14}

 writeln('Dla perehoda v DOS najmi Enter.Zapusti nekuu programmu.',          {15}

 'Naberi Exit dla vozvrata');    readln;                                                                {15a-16}

 exec('\command.com','')                                                                                  {17}

end;                                                                                                                 {18}

Перед  прогоном  процедуры  в  вызывающей  программе  надо  написать  {#m $4000,0,0},

чтобы  убедиться  в  наличии  необходимой  памяти  для  вызываемой  программы(в  нашем  случае  это  sounds.exe). Необходим  также  “uses  dos”  в  голове  программы.

Объяснение.

{4}Программа  прогоняется  с  помощью  процедуры  exec, которой  требуется  два  параметра. Первый - имя  запускаемой  программы(у  нас  sounds.exe), а  второй - параметры, вводимые  программой(у  нашей  программы  параметров  нет).

{14}Исполнение  управляющей  программы  command.com. Это - первый  параметр  команды  exec .  Второй  параметр  есть  специфическая  команда  DOS, которую  хотим  исполнить. В  этом  примере  команда  “dir *.pas”  должна  напечатать  все  файлы  с  расширением  .pas.

{17}Исполнение  управляющей  программы. Второго  параметра  нет, смысл  этого  в  том, что  мы  можем  запустить  любую команду   DOS  из  этой  команды.

7.6.8.Проверка  версии  и  проверка  состояния  ^c

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

procedure miscellaneous;                                                        {1}

var yn: char;       dosver: word;   cbreak: boolean;                    {2-5}

begin                                                                                        {6}

 clrscr;                                                                                       {7}

 dosver := dosversion;                                                               {8}

 writeln('DOS version: ',lo(dosver),'.',hi(dosver));                       {9}

 writeln('Najmi Enter, chtobi posmotret sostoianie Ctrl-Break');     readln; {10-11}

 getcbreak(cbreak);                                                                   {12}

 if cbreak then                                                                          {13}

 begin                                                                                       {14}

  writeln('Sostoianie Ctrl-Break zadeistvovano - "vklucheno" '); {15}

  writeln('Pogasit sostoianie Ctrl-Break? Otvet'': Y/N');              {16}  

  yn := readkey;                                                                         {17}

  if upcase(yn) = 'Y' then setcbreak(false)                               {18-19}

 end                                                                                         {20}

  else                                                                                       {21}

  begin                                                                                    {22}

   writeln('Sostoanie Ctrl-Break pogasheno');                          {23}

   writeln('Zadeistvovat sostoianie Ctrl-Break? Otvet'': Y/N');  {24}

   yn := readkey;                                                                     {25}

   if upcase(yn) = 'Y' then setcbreak(true)                             {26-27}

  end                                                                                      {28}

end;                                                                                       {29}

Примечание. При  размещении  этой  процедуры  в  программу  следует  включить  команду  uses dos;.

Объяснение.

{8}Функция  dosversion  возвращает  используемую  компьютером  версию  DOS.

{9}В младшем  байте  находится  номер  версии, а  в  старшем  - номер  модификации(например, версия  3, модификация  2,  обозначаются  DOS 3.2).

{12}Проверка  способа реакции  на  нажатие  control-break(^c). Когда  ответ  в  cbreak    равен  true, проверка  выполняется  при  каждом  системном обращении. Если  ответ  false,   то  лишь  при  обращениях  к  вводу/выводу. Оператор  может  изменить  значение  проверки  control-break  в  этом  фрагменте. Чтобы  понаблюдать  за  влиянием  включения  и  гашения  control-break, можно  прогнать  следующий  цикл, который  надо  вставить  в  процедуру  miscellaneous :

   .............................................................................

    cbreak: boolean;                                               {5}

    i,j: longint;

   .............................................................................

  end;                                                                    {28}

  j := 0;

  for i := 1 to 1500000 do

  begin

    inc(j);

    j := i*i - i*5 + 1126*123;

   for j := 1 to 2 do dosver := dosversion;

{Можно  прервать  цикл, если  ^c  задействован: здесь  обращение  к  системной  функции }       

                  ИЛИ

   for j := 1 to 2 do write(' ',j,' ',i);

 {Можно  прерваться  по  ^c  и  если ^c  не  задействован: в  цикле  команда ввода/вывода}

 end

 

7.7.Модули

Понятие  процедуры  расширено  до  понятия  модуля(unit), который  соответствует  понятию  пакет(packages)  языка  АДА(ADA). Ниже  дается схематическое  описание  модуля: (Рис. 10  Заголовок  модуля  Интерфейсная  часть  Исполнительная  часть  Секция  инициализации)

В  заголовке  пишут  слово   unit  и  имя  модуля. В  части, принадлежащей  интерфейсу,    пишут  слово  interface  и  затем  декларации  постоянных(const), типов(type), переменных(var)  и  заголовки  процедур  и  функций. При  обращении  из  программы  к  модулю  можно  использовать  любую  постоянную, тип, переменную, процедуру  и  функцию, определенные  в  его  интерфейсе. Можно  рассматривать  модуль  как  расширение  объявлений  в  программе. Преимущество  модуля  состоит  в возможности  присоединения  этих  расширений  к  любой  программе  подобно  добавлению  к  ней  внешних  процедур(как  с  помощью  подключателя $I, например, $I proc.pas). В  нашем  случае  мы  подключаем  не  одну  процедуру, а  ряд  процедур, и  также  переменные, типы  и  постоянные, которые  можем  использовать  в  вызывающей  программе.

В  исполнительной  части  пишется  слово  implementation, а  затем  объявления  процедур, функций  и  содержимое  процедур  и  функций, чьи  заголовки  были  записаны  в  интерфейсной  части. Все, что  записано  в  интерфейсе, доступно  вызывающей  программе.  Напишем  пример  "модуля", содержащего  процедуру  вычисления  даты  по  еврейскому  календарю  согласно  международной  дате(luach - календарь(здесь), tishrei - месяц  тишрей, loazi - иностранный, ivri - еврейский, shana - год, chodesh - месяц, yom - день).

unit  luach;                                            {1}

interface                                               {2}

uses  crt;                                               {3}

 

const                                                    {4} 

   tishrei = 1;                                          {5}

  ...............

var                                                        {6}

  year, month, day: integer;                    {7}

procedure  loazi_ivri(yr, mon, dy: integer;                          {8}

                                  var  shana, chodesh, yom: integer); {9}

implementation                                   {10}

procedure  loazi_ivri;                           {11}

begin                                                    {12}

  ......................

end;                                                      {13}

  year := 1990;                                      {14}

  month := 1;                                         {15}

  day := 1;                                             {16}

end.                                                     {17}

Объяснение.

{1}Имя  модуля.

{2}Начало  интерфейсной  части.

{3}Модуль  может  обращаться к  другим  модулям.

{4-5}Постоянные, к  которым  можно  обращаться  из  программы, вызывающей  luach.

{8}Полный  заголовок  процедуры, к  которой  можно  обращаться  из  любой  программы, вызывающей  этот  модуль.

{10}Начало  исполнительной  части.

{11}Нет  нужды  в  полном  заголовке  процедуры, поскольку  она  уже  записана  с  заголовком  в  интерфейсной  части.

{14-16}Установление  начальных  значений  глобальных  переменных year, month, day. Программисту  вызывающей  программы  доступно  использование  этих  переменных  при  обращении  к  loazi_ivri  без  придания  им  каких-либо  значений. И  тогда  еврейская  дата  может  быть  вычислена  для  1.1.90. Если  программист  в  программе  придаст   свои  значения  переменным  year, month, day, дата  по  еврейскому  летоисчислению  будет  вычислена  при  написании  в  ней  loazi_ivri(yearr, month, day, shana, chodesh, yom);.

{17}Конец  модуля.

Для обращения  к  этому  модулю  в  программу  следует  добавить uses  luach; , например:

program  calendar;

uses  luach;

............................

Можно  скомпилировать  модули  заранее  и  хранить  их  в  виде  набора  команд  на  машинном  языке. Это  можно  делать  посредством  опции  compile  главного  меню, нужно  лишь  озаботиться  тем, чтобы  любой  модуль, вызываемый  компилируемым  модулем, был  скомпилирован  заранее. Предпочтительнее  пользоваться  опцией  make, которая  автоматически  скомпилирует  все  модули, вызываемые  компилируемым  в  данный  момент  модулем(если  последние  их  версии  не  скомпилированы). Можно  также  пользоваться  опцией  build, которая  всегда  компилирует  все  вызываемые  модули. Компилятор  придает  расширение  имени   .tpu  модулю, переведенному  на  машинный  язык. Использовать  опции  make  или  build  можно  также  и  для  того, чтобы  компилировать  вызывающую  программу  вместе  с  компиляцией  самих  этих  модулей.

 

7.8.Упражнения

7.8.1.Вопросы

(1)Предположим, что  в  главной  программе  написано:

 x := 15;    y := 20;

 targil(x, y);

 writeln(x, y);

Что  будет  напечатано  в  каждом  из  следующих  случаев:

{a}procedure  targil

    (a, b: integer);               

    begin

       a := a + 4;     b := b - 3;

       writeln(a, b)

    end;

{b}procedure  targil

   (var  a: integer; b: integer);

   begin

      a := a + 4;     b := b - 3;

      writeln(a, b)

    end;

{c}procedure  targil

    (var  a, b: integer);

    begin

       a := a + 4;     b := b - 3;

       writeln(a, b)

     end;

(2)Какова  область  и  интервал  каждого  имени  в  следующей  программе(rashi - главный, rishon - первый, sheni - второй, shlishi - третий ):

Program  rashi;

const  e = 15;

type  L = 0..30;

var  s, a, b: integer;

procedure  rishon;

var  s, b, c: integer;

::::::::::::::::::::::::::::::

end; (*Конец rishon *)

procedure  sheni;

type  p = (b, s, e);

var  a, c, d, L : integer;

::::::::::::::::::::::::::::::

  procedure  shlishi;

  var  a, e: integer;

::::::::::::::::::::::::::::

  end; (*shlishi *)

:::::::::::::::::::::::::::::::

end; (*sheni *)

::::::::::::::::::::::::::::::

end; (*rashi *)

 

7.8.2.Программы

(1)Подоходный  налог  на  семейную  пару  с  доходом  до  1000  шекелей  составляет  20% плюс  40%  от  суммы, превышающей  1000 шекелей. Одиночка  платит  10%  с  дохода  до  500  шекелей  и  60%  с  суммы, превышающей  500  шекелей. Напиши  процедуру  для  обоих  этих  случаев  и  используй  ее  в  программе, которая, вычисляя  налог,  вводит  два  данных: ·семейное  положение(n - семейный, b - одиночка); ·доход.

(2)Напиши  программу, которая  вводит  целое  неотрицательное  число  и  печатает  его. А  рядом  она  чертит  из  дефисов(-)  линию, длина  которой  пропорциональна  количеству  цифр  в  числе(это - гистограмма). Рисование  гистограммы надо  оформить  в  виде  процедуры  с  одной  переменной-значением, которая  равна  числу  цифр  в  числе.

(3)Напиши  функцию, которая  вычисляет  прибыль от  продажи  продукции. Данные: цена  изделия(Р), количество  проданных  изделий(С), постоянная  себестоимость  производства(K)  и  переменная  торговая  наценка(М),  приходящиеся  на  одно  изделие. Воспользуйся  этой  функцией  в  программе, которая  печатает  доход  за  разные  дни, где  ввод  есть  число  единиц,  проданных  за  каждый  день. Стоит  напечатать  заголовок  отчетной  таблицы. 

(4)Напиши  функцию  schum(sug, netunim, n) (schum - сумма, sug - вид, netunim - данные) для вычисления  суммы  квадратов  в  n-той  строке  или  n-том столбце  матрицы  netunim. Если  sug = ‘S’, вычисляется сумма  квадратов  в  строке, а  если sug = ‘A’, вычисляется сумма  квадратов  в  столбце. Используй  schum  в  программе, которая  вводит  матрицу  из  3-х  строк  и  4-х  столбцов  и  вычисляет  суммы  квадратов  всех  строк  и  всех  столбцов. Вывести  надо  исходную  матрицу, в  конце  каждой  строки  - сумму  квадратов  ее  элементов. А  под  каждым  столбцом  надо  поместить  его  сумму  квадратов.

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

(6)Напиши  процедуру, параметрами  которой  являются  строка  символов  и  два  числа   n1  и  n2. Она  выдает  подстроку, которая  начинается  с  позиции  n1  исходной  строки  и  имеет  длину  n2 символов.

(7)Напиши  функцию  с  параметрами  X  и  Y. Функция  возвращает  XY.

(8)В  этом  упражнении  четыре  части:

a).Напиши  процедуру  otakim(c, n), которая  n  раз  печатает  символ  с.

b).Используй  эту  процедуру  в  программе, которая  выводит  равнобедренный  треугольник. У  программы  три  переменных: ·высота  треугольника; ·символ, из  которого  состоит  треугольник; ·число  пробелов  перед  вершиной  треугольника. Если  тройка  этих  переменных  такова  (3, ‘*’, 10), то  должен  получиться  такой  треугольник:

{10 пробелов до  вершины}        *

                                                ***

                                               *****

c).Напиши  функцию  tav_achron  с  одним  параметром-переменной  marach_80, который  определяется  как  packed  array[1..80] of  char. Функция  возвращает  порядковый  номер  последнего  символа  в  массиве, который  не  есть  пробел.

d).Напиши  программу, которая  использует  процедуры  otakim  и  tav_achron, чтобы  прочесть  строку, предназначенную  для заголовка. Программа  печатает  символы  до  последнего  непробельного(пробел - blank)  в  строке. Она  центрирует  строку  вывода  на  странице  или  экране  с  80  позициями  в  строке  и  добавляет  по  10  звездочек  с  каждой  стороны.

(9)Упражнение  состоит  из  нескольких  частей:

a).Напиши  функцию  orech(длина),которая  принимает  целое  число  и  возвращает  количество  цифр  в  нем.

b).Напиши  функцию   kra_ad_psik(читай  до  запятой),  которая  читает  строчку  ввода  до  первой  запятой, записывает  ее(без  запятой)  в  массив  длиной  80  и  подсчитывает  число  введенных  до  запятой  символов   Если  eoln  получается  до  достижения  запятой, печатаем  сообщение  об  ошибке  и  с  помощью  команды  goto(которая  разрешена  в  этом  исключительном  случае)  переходим  к  концу  процедуры.

c).Строки  ввода  содержат  два  значения(слева  направо)  name(имя - строка  символов)  и  number(число), разделенные  запятыми. Напиши  программу, которая  будет  печатать  список  имен  и  чисел  таким  образом:

Номера  позиций:

10                                 28

< ----  19  символов  --- >

Содержимое  строк:

YAKOV . . . . . . . . . . .279

AVRAHAM . . . . . . . . .42

GAVRIELA . . . . . . . . . .1

Имя  начинается  в  10  позиции, а  правая  цифра  находится  в  28-й. Используй  процедуру  из  a’  и  b’, а  также  процедуру  otakim  из  предыдущего  упражнения.

(10)В упражнении  несколько  частей:

a).Напиши  процедуру  чтения  восьмеричного  числа  и  перевода  его  в  десятичное.

b).Еще  одну  процедуру  напиши  для  перевода  десятичного  числа  в  восьмеричное.

c).Используя  две  эти  процедуры, напиши  программу, которая  вводит  последовательность  восьмеричных  чисел, переводит  их  в  десятичный  вид, складывает, результат  переводит  в  восьмеричный  вид  и  печатает  его.

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

(12)Это  упражнение  состоит  из  двух  частей:

a).Напиши  функцию, анализирующую  результаты  экзаменов. Данные  ввода: ·n - число  вопросов  на  экзамене;   ·ответы  студента(массив) -  числа  от  1  до  4; ·ключ  ответов(массив) - набор  правильных  ответов. Функция  возвращает  оценку  студента  на  экзамене, которая  вычисляется  согласно  числу  правильных  ответов, поделенному  на  число  вопросов.

b).Напиши  программу, которая  вводит  следующие  данные: ·число  вопросов  и  ключ  ответов(массив);   ·личные  номера  студентов  и  их  ответы  на  все  вопросы.  Программа  печатает  в  верхней  строке  ключ  ответов, а  под  ней - личные  номера  студентов,  их  ответы  и  полученные  отметки.

(13)Напиши  функцию, которая  проверяет, содержится  ли  заданная  цифра  в  заданном  числе. Используй  эту  функцию  в  программе, которая обнаруживает, для  какого  числа  I (от  1  до  100)  цифра  D(от  0  до  9)  входит  и  в  I,   и  в  I2 , и  в I3.

 

Глава  8. Поиск, классификация  и  смешивание

8.1. Предисловие

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

a).Поиск(Searching): сканирование   всех  элементов  массива  для  нахождения  элемента  или  элементов, отвечающих  определенным  условиям  или  равных  некоторому  значению.

b).Классификация(Sorting): упорядочение  элементов  массива  по  их  значениям  в  порядке  возрастания  от  меньшего  к  большему  или  в  порядке  убывания  от  большего  к  меньшему.

c).Смешивание/объединение(Merging): объединение  двух  упорядоченных  массивов  в  один  новый  массив. Этот  массив - также  упорядочен  и  содержит  все  элементы  двух  исходных  массивов.

Элементами  массивов  могут  быть  целые  или  вещественные  числа, символы  или  имена(т.е.  массивы  символов).

 

8.2.Поиск

8.2.1.Линейный  поиск

Существует  два  распространенных  способа  поиска  элементов  в  массиве. Самым  простым  способом  является  линейный  поиск(Linear  Search). Этот  поиск  начинается  с  первого  элемента  массива  и  продвигается  к  его  концу  вплоть  до  обнаружения  нужного  элемента.  Продемонстрируем  в  программе  способ  линейного  поиска:  ищем  телефонный  номер  в  их  перечне  с  неупорядоченными  по  алфавиту  именами (misparei telephone - номера  телефонов,  shem - имя, godel  reshima - размер  перечня, reshimat  misparim - список  номеров,   shem  chaver - имя  приятеля,  nimtza - находится).

program misparei_telephone;                                                                                  {1}

type                                                                                                                          {2}

 shem = string[20];                                                                                                  {3}

 mispar = string[10];                                                                                                {4}

var                                                                                                                            {5}

 i,godel_reshima: 1..100;                                                                                          {6}

 reshimat_misparim: array[1..100] of mispar;                                                          {7}

 reshimat_shemot: array[1..100] of shem;                                                               {8}

 shem_chaver: shem;                                                                                                {9}

 nimtza: boolean;                                                                                                       {10}

begin                                                                                                                        {11}

 write('Zaday razmer spiska: ');                                                                                  {11a}

 readln(godel_reshima);                                                                                             {12}

 writeln('Vvodi v cikle imia(Enter)-nomer(Enter)');                                                      {13}

 for i := 1 to godel_reshima do                                                                                 {14}

 begin                                                                                                                        {15}

  readln(reshimat_shemot[i]);                                                                                      {16}

  readln(reshimat_misparim[i]);                                                                                   {17}

 end;                                                                                                                           {18}

 write('Vvedi imia priatelia: ');                                                                                       {18a}

 while not eof do                                                                                                        {19}

 begin                                                                                                                          {20}

  readln(shem_chaver);                                                                                                {21}

  nimtza := false;              i := 1;                                                                                    {22-23}

  while (i <= godel_reshima) and not nimtza do                                                         {24}

   if (shem_chaver = reshimat_shemot[i]) then nimtza := true                                     {25-26}

   else i := i + 1;                                                                                                            {27-28}

  write(shem_chaver,' ':10);                                                                                          {29}

  if nimtza then writeln(reshimat_misparim[i])                                                              {30-31}

   else writeln('otsutstvuet v spiske!');                                                                           {32-33}

  write('Vvedi imia priatelia ili Ctrl-Z: ')                                                                           {33a}

 end                                                                                                                              {34}

end.                                                                                                                              {35}

Объяснение.

{12-18}Чтение  списка  имен  и  телефонных  номеров.

{19-34}Поиск  номера  приятеля  в  списке  номеров(если  есть).

{24-28}На  первый  взгляд  проще  заменить  эти  строки  на  более  простые  команды:

while (i <= godel_reshima) and (reshimat_shemot[i] <> shem_chaver) do i := i + 1;                                                                                                           

В  этих  командах  поиск  начинается  с  элемента  списка  reshimat_shemot[1],  сравниваемого  с  shem_chaver. Если  имена  не  одинаковы, просмотр  продолжается  до  тех  пор, пока  не  произойдет  одно  из  двух  событий: a).shem_chaver   окажется  равным  reshimat_shemot[i];    b).чтение  списка  завершится.

Преимущество  такого  приема  в  том, что  нет  необходимости  в  переменной nimtza. А  недостаток - в  том, что  он  может  привести  к  ошибке  исполнения  при  использовании  некоторых  компиляторов, если  искомое  имя  не  находится  в  списке.  В  этом  случае  i  может  достигнуть  значения  godel_reshima+1, у  нас  возможно  101, т.е.  за  пределами  массива. Когда  такое  случается, условие 

       (i <= godel_reshima) and (reshimat_shemot[i] <> shem_chaver)  

окажется  равным  false, т.к. первая  его  часть  i <= godel_reshima  уже  не  выполняется, а  во  второй  части  будет  присутствовать  reshimat_shemot[101].

Индекс, который  находится  за  пределами  верной  области(например,  101),  приведет  к  сообщению  об  ошибке. Вопрос  в  том, будет  ли  компилятор  организовывать  проверку  второго  условия(в  операции  and), если  первое  условие  не  выполняется. Поскольку  существуют  компиляторы, которые  в  любом  случае  проверяют  оба  условия, рекомендуется  пользоваться  способом, реализованным  в  теле  программы, а  не  более  коротким  как-будто  бы  способом.

{29-34}Выход  из  цикла  при  нахождении  имени  приятеля  в  списке  имен(nimtza = true, имя  находится  на  i-том  месте  списка).  Еще  одна  возможность  выйти  из  цикла  состоит  в  полном  просмотре  списка  без  нахождения  искомого  имени(nimtza = false,      i  равно  godel_reshima+1). Если  nimtza = true,  печатается  номер  прителя, а  иначе - сообщение, что  его  нет  в  списке.

Процесс  линейного  поиска прост, но  не  эффективен. Если  в  массиве  N  элементов, то  число  сравнений, которые  необходимо  выполнить, в  лучшем  случае  равно  1(искомый  элемент  находится  в  голове  списка)  или  N  в  наихудшем (искомый  элемент  находится  в  конце  списка). Среднее  число  сравнений  равно  (N+1)/2.

8.2.2.Бинарный  поиск

Значительно  эффективнее  бинарный  поиск(Binary  Search). Но  у  него  есть  ограничение: этот  способ  можно  применять, если  массив  упорядочен - по  возрастанию  или  убыванию. А  линейный  поиск, как  мы  видели, годится  и  для  неупорядоченного  массива. Бинарный  поиск  подобен  поиску  в  словаре  или  телефонном  справочнике. Книжка  открывается  посредине. Если,  на  счастье,  мы  открыли  верную  страницу, то  на  ней  находим  нужное  слово  и  завершаем  поиск. Если  значение  искомого  слова  меньше  значения  верхнего  слова  на  открытой  странице, обращаемся  к  средней  странице  первой  части  книжки, т.е. между  началом  ее  и  уже  раскрытой страницей. Если  же  значение  искомого  слова  больше  значения  нижнего  слова  на  открытой  странице, открываем  среднюю  страницу  второй  части  книжки. И  так  продолжаем  деление  на  2  области  страниц, в  которых  может  находиться  разыскиваемое  слово  вплоть  до  нахождения  нужной  страницы.  В  следующем  примере  порядок  классификации - возрастающий, но  этим  способом  можно  пользоваться  и  когда  элементы  упорядочены  по  убыванию.

Пусть  в  примере  ищется  позиция  числа  16  в  возрастающем  списке  из  7  элементов:

Значения

4   9   11   13   16   18   27

Номер элемента  в  списке

1   2    3     4     5      6    7 

Средний  элемент  в  спике  это  13. Т.к.  16  больше  его, переходим  к  середине  интервала  элементов  с  более  высокими(чем  13)  значениями(16, 18, 27)  и  извлекаем  его  срединное  значение(18). Это  число  больше  искомого  значения, поэтому  переходим  к  поискам  среди  элементов  слева, значения  которых  меньше. В  этом  интервале  есть  только  одно  значение  16, которое  равно  разыскиваемому, и  на  этом  проверки  прекращаются. Если  бы  на  этом  месте  стояло  число, например, 15, следовало  прийти  к  заключению, что  элемента  16  нет  в  списке, т.к.  мы  проделали  все  возможные  шаги, но  безуспешно.

В  этом  примере  мы  нашли  искомое  число  после  третьего  сравнения. И  это  наибольшее  возможное  число  сравнений, которое  можно  сделать  в  списке  из  7  элементов. В  перечне  из  15  элементов  делается  до  4  сравнений, т.е  всего  на  одно  больше, чем  в  предыдущем  примере, т.к.  после  первого  деления  получается  два  списка  из  7  элементов  каждый. В  общем  виде  наибольшее  число  сравнений  описывается  следующей  таблицей:

Число  элементов

Наибольшее  число  сравнений

1

2, 3

4, 5, 6, 7

2х-1 .. 2х - 1

1

2

3

х

Если  N = 2x-1, получается, что  x-1 = log2N,  или  x = log2N + 1. С  возрастанием  N  значение  x  не  меняется, пока  не  придем  к  числу  элементов  в  списке N = 2x. В  этот  момент  значение log2N  возрастает  на  1  против  значения, когда  N = 2x-1.  В  итоге  можно  сказать, что  x = trunc(log2N)  есть  наибольшее  число  сравнений  в бинарном  поиске  в  массиве  с  N  элементами.

Опишем  теперь  алгоритм  бинарного  поиска  для  предыдущего  примера  программы  нахождения  номера(по  имени) в  телефонной  книжке. (Рис. 1  Начни   Прочти   Заполни  массивы   Нет   Прочти   Исполни  бинарный  поиск    Да    Заверши).

(Рис. 2   Бинарный  поиск  Напечатай   Не  в  списке  имен  Возвратись   )

(rishon - первый,  achron - последний, godel  reshima - размер  перечня, emtzai - срединный, reshimat  shemot - список  имен,   shem  chaver - имя  приятеля).

Объяснение  блок-схемы.

{2-3}Чтение  списка  имен  и  телефонных  номеров.

{4-6}Структура  dowhile  чтения  имен  приятелей  и  поиска  этих  имен  в  списке. Всякий  раз  обращаемся  к  структуре {6}, содержащей  бинарный  поиск.

{9}В  начале  бинарного  поиска  его  интервалом  служат  все  элементы  массива. Поэтому  rishon  равен  1, а  acharon  есть  godel_reshima.

{10-14}Цикл  по  команде  dountil, содержащей  в  себе  команду  if. Операции  с  {10-13}  должны  составлять  отдельный  фрагмент.  

{10}Нахождение  срединного  элемента  заданного  массива.

{11-12}Если  shem_chaver  меньше  значения  срединного  элемента, важно  узнать  значение  предыдущего, стоящего  на  позиции  emtzai-1.

{13}Если  shem_chaver  больше  значения  срединного  элемента, надо  перейти  в  группу  старших  элементов  массива, причем  значение  rishon  станет  на  1  больще, чем  emtzai. Если  же  shem_chaver  равно  среднему  значению, нужды  в  {13}  нет, но  команда  все-таки  будет  исполнена.

{14}Конец  структуры  dountil. Исполнение  возвращается  к  началу  цикла, если  к  окончательному  решению  еще не  пришли.

{15}Окончательное  решение  получается  тогда, когда(или): a).имя  приятеля  в  списке  найдено - выполняется  блок  {16};   b).значение  rishon  превысило  значение  achron - тогда  выполняется  блок  {17}.

 

8.2.3.Программирование  на  псевдокоде

Алгоритм  поиска  можно  написать  на  псевдокоде(Pseudo  Code), который  во  многих  случаях  заменяет  блок-схемы. Написание  алгоритма  на  псевдокоде  очень  похоже  на  обычное  программирование, но  часть  логических  деталей  пишется  на  нем  в  общем  виде  без  детализации, необходимой  в  программе  на  языке  программирования. 

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

Ниже  дана  программа  бинарного  поиска  имени  в  телефонной  книжке, написанная  на  псевдокоде.

(1) BEGIN

(2)    READ  GODEL_RESHIMA;

(3)    READ  RESHIMAT_SHEMOT;

(4)    READ  RESHIMAT_MISPARIM;

(5)   WHILE  NOT  EOF  DO

(6)   BEGIN

(7)      READ  SHEM_CHAVER;

(8)      REASON ί 1;

(9)      ACHRON ί GODEL_RESHIMA;

(10)    REPEAT

(11)       EMZAI ί (RISHON + ACHRON)/2;

(12)       IF  SHEM_CHAVER < RESHIMAT_SHEMOT[EMTZAI]  THEN

(13)         игнорируй  верхние  значения  с  помощью  уточнения  значения ACRON

(14)       ELSE

(15)         игнорируй  младшие  значения  с  помощью  уточнения  значения RISHON

(16)     UNTIL  завершения  цикла;

(17)     IF  RESHIMAT_SHEMOT[EMTZAI] <> SHEM_CHAVER  THEN

(18)         укажи, что  искомое  имя  не  найдено

(19)     ELSE

(20)         напиши  имя  приятеля  и  его  номер  телефона

(21)  END

(22) END

После  такого  написания  алгоритма  заменяем  операции  (13), (15), (16), (18), (20)  соответствующими