4833

Типы данных, определяемых программистом

Лекция

Информатика, кибернетика и программирование

Типы данных, определяемых программистом. Цель: Научить студентов создавать приложения, работающие с типами данных программиста. Задачи: Воспитательная: работа над собой. Учебная: создание приложений. Развивающая: развитие внимательности. План заняти...

Русский

2012-11-27

353.5 KB

7 чел.

Типы данных, определяемых программистом.

Цель:

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

Задачи:

Воспитательная: работа над собой.

Учебная: создание приложений.

Развивающая: развитие внимательности.

План занятия.

  1.  Организационный момент.
  2.  Изучение нового материала.
  3.  Контрольные вопросы.
  4.  Резюме.
  5.  Домашнее задание.

Изучение нового материала.

   До этого момента в программах использовались стандартные типы данных: integer, Real, Char, string и Boolean. Вместе с тем, язык Delphi позволяет программисту определить свой собственный тип данных, а затем данные этого типа использовать в программе. Объявляемый программистом новый тип данных базируется на стандартных типах или на типах, созданных программистом ранее. Тип, определенный программистом, может быть отнесен к:

  •  перечисляемому;
  •  интервальному;
  •  составному типу данных (записи).

Перечисляемый тип

   Определить перечисляемый тип – это значит перечислить все значения, которые может принимать переменная, относящаяся к данному типу. В общем виде объявление перечисляемого типа выглядит так:

Тип =( Значение1, Значение2, ... Значение i)

где:

  •  тип – имя перечисляемого типа данных;
  •  Значение i – символьная константа, определяющая одно из значений, которое может принимать переменная типа Тип.

   Примеры:

   TDayOfWeek = (MON,TUE,WED,THU,FRI,SAT,SUN);

   TColor = (Red,Yellow,Green);

   После объявления типа можно объявить переменную, относящуюся к этому типу, например:

type

   TDayOfWeek = (MON,TUE,WED,THU, FRI,SAT,SUN) ;

var

   ThisDay, LastDay: TDayOfWeek;

   Помимо указания значений, которые может принимать переменная, описание типа задает, как значения соотносятся друг с другом. Считается, что самый левый элемент списка значений является минимальным, а самый правый — максимальным. Для элементов типа DayOfWeek справедливо:

   MON < TUE < WED < THU < FRI < SAT < SUN

   Свойство упорядоченности элементов перечисляемого типа позволяет использовать переменные перечисляемого типа в управляющих инструкциях, например, так:

if (Day = SAT) OR (Day = SUN) then

   begin

       { действия, если день – суббота или воскресенье }

   end;

   Приведенную инструкцию можно записать и так:

if Day > FRI then begin

   { действия, если день – суббота или воскресенье }

end;

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

type

   TDayOfWeek = (MON, TUE, WED, THU, FRI, SAT, SUN) ;

var

   ThisDay: TDayOfWeek;

begin

   ThisDay:=1;

   if ThisDay = 6 then

       begin

       { блок инструкций }

       end;

инструкция ThisDay:= 1; ошибочна, т. к. переменная ThisDay принадлежит к определенному программистом перечисляемому типу TDayOfWeek, а константа, значение которой ей присваивается, принадлежит к целому типу (integer). В условии инструкции if тоже ошибка.

   Можно утверждать, что объявление перечисляемого типа – это сокращенная форма записи объявления именованных констант. Например, приведенное выше объявление типа TDayOfWeek равносильно следующему объявлению:

const

   MON=0;

   TUE=1;

   WED=2;

   THU=3;

   FRI=4;

   SAT=5;

   SUN=6;

Интервальный тип

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

Тип = НижняяГраница..ВерхняяГраница;

   где:

  •  тип – имя объявляемого интервального типа данных;
  •  НижняяГраница – наименьшее значение, которое может принимать переменная объявляемого типа;
  •  верхняяГраница – наибольшее значение, которое может принимать переменная объявляемого типа.

   Примеры:

   TIndex = 0 .. 100;

   TRusChar = 'А' .. 'я';

   В объявлении интервального типа можно использовать именованные константы. В следующем примере в объявлении интервального типа TIndex использована именованная константа HBOUND:

const

   HBOUND=100;

type

   TIndex=l..HBOUND;

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

type

   TIndex =1 .. 100;

var

   tab1 : array[TIndex] of integer;

    i:TIndex;

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

type

   TMonth = (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec);

   TSammer = Jun.. Aug;

Запись

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

   Запись – это структура данных, состоящая из отдельных именованных компонентов разного типа, называемых полями.

   Объявление записи

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

Имя = record

   Поле_1 : Тип_1;

   Поле_2 : Тип_2;

   Поле_К : Тип_К;

end;

   где:

  •  Имя – имя типа "запись";
  •  record – зарезервированное слово языка Delphi, означающее, что далее следует объявление компонентов (полей) записи;
  •  поле_i и тил_i – имя и тип i-го компонента (поля) записи, где i=1, ..., k;
  •  end – зарезервированное слово языка Delphi, означающее, что список полей закончен.

   Примеры объявлений:

type

   TPerson = record

       f_name: string[20];

       i_name: string[20];

       day: integer;

       month: integer;

       year: integer;

       address: string[50];

   end;

TDate = record

   day: integer;

   month: integer;

   year: integer;

end;

   После объявления типа записи можно объявить переменную-запись (или просто запись), например:

var

student : TPerson;

birthday : TDate;

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

ShowMessage('Имя: ', student.f_name + #13 + 'Адрес: ', student.address);

выводит на экран содержимое полей f_name (имя) и address (адрес) переменной-записи student.

   Иногда тип переменной-записи объявляют непосредственно в разделе объявления переменных. В этом случае тип записи указывается сразу за именем переменной, через двоеточие. Например, запись student может быть объявлена в разделе var следующим образом:

student: record

   f_name:string[20];

   i_name:string[20];

   day:integer;

   month:integer;

   year:integer;

   address:string[50];

end;

   Инструкция with

   Инструкция with позволяет использовать в тексте программы имена полей без указания имени переменной-записи. В общем виде инструкция with выглядит следующим образом:

with Имя do

   begin

       ( инструкции программы }

   end;

   где:

  •  имя имя переменной-записи;
  •  with – зарезервированное слово языка Delphi, означающее, что далее, до слова end, при обращении к полям записи имя, имя записи можно не указывать.

   Например, если в программе объявлена запись

student:record // информация о студенте 

       f_name: string[30]; // фамилия 

       i_name: string[20]; // имя 

       address: string[50]; // адрес 

   end;

и данные о студенте находятся в полях Edit1, Edit2 и Edit3 диалогового окна, то вместо инструкций

student.f_name := Editl.text;

student.i_name := Edit2.text;

student.address := Edit3.text;

можно записать:

with student do

   begin

       f_name := Edit1.text;

       i_name := Edit2.text;

       address := Edit3.text;

   end;

   Ввод и вывод записей в файл

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

type

   ТРеrson = record

   f_riame: string [20] ;

   l_name: string[20];

   address: string[50];

end;

var

   f: file of TPerson;

объявляют файл, компонентами которого являются записи типа TPerson.

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

Вывод записи в файл

   Рассмотрим программу, которая записывает в файл введенные пользователем данные о результатах соревнований, формируя, таким образом, простую базу данных. Исходные данные вводятся в поля диалогового окна и сохраняются в файле, компонентами которого являются записи типа TMedal.

   Для ввода фамилии спортсмена применяется поле редактирования (компонент Edit). Для ввода названия вида спорта и страны используются компоненты ComboBox (комбинированный список). Компонент ComboBox, значок которого находится на вкладке Standard, дает возможность ввести данные либо непосредственно в поле ввода-редактирования, либо путем выбора из списка, который появляется в результате щелчка на кнопке раскрывающегося списка.

Таблица 1. Свойства компонента ComboBox

Свойство

Определяет

Name

Имя компонента. Используется для доступа к свойствам компонента

Text

Текст, находящийся в поле ввода-редактирования

Items

Элементы раскрывающегося списка

DropDownCount

Количество отображаемых элементов в раскрытом списке

Left

Расстояние от левой границы компонента до левой границы формы

Top

Расстояние от верхней границы компонента до верхней границы формы

Height

Высоту компонента (поля ввода-редактирования)

Width

Ширину компонента

Font

Шрифт, используемый для отображения элементов списка

ParentFont

Признак наследования свойств шрифта родительской формы

   Список, который появляется в результате щелчка на кнопке раскрытия списка, может быть сформирован как в процессе разработки формы приложения, так и во время работы программы. Чтобы сформировать список во время разработки формы, нужно в окне Object Inspector выбрать свойство Items, щелкнуть на кнопке активизации редактора списка строк (кнопка с тремя точками) и ввести элементы списка.

   Полный текст программы приведен в листинге 1.

Листинг 1. Добавление записей в файл

unit apprec_;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls;

type

   TForm1 = class(TForm)

       Label1: TLabel;

       Label2: TLabel;

       Label3: TLabel;

       Edit1: TEdit;                             // спортсмен

       ComboBox1: TComboBox;     // страна

       ComboBox2: TComboBox;     // вид спорта

       RadioGroup1: TRadioGroup;  // медаль

       Button1: TButton;                   // кнопка Добавить

       Label5: TLabel;

       Label4: TLabel;

       procedure FormActivate(Sender: TObject);

       procedure FormClose(Sender: TObject; var Action: TCloseAction);

       procedure Button1Click(Sender: TObject);

   private

       { Private declarations }

   public

       { Public declarations }

    end;

   // тип медали

   TKind = (GOLD, SILVER, BRONZE);

   // запись файла

   TMedal=record

       country: string[20];    //  страна

       sport:   string[20];      //  вид спорта

       person:  string[40];    //  спортсмен

       kind:    TKind;            //  медаль

   end;

var

   Form1: TForm1;

   f: file of TMedal; // файл записей – база данных

implementation

{$R *.DFM}

// активация формы

procedure TForm1.FormActivate(Sender: TObject);

var

   resp : word; // ответ пользователя

begin

   AssignFile(f, 'a:\medals.db');

   {$I-}

   Reset(f);  // открыть файл

   Seek( f, FileSize(f)); // указатель записи в конец файла

   {$I+}

   if IOResult = 0 then button1.enabled:=TRUE // теперь кнопка Добавить доступна

       else

           begin

           resp:=MessageDlg('Файл базы данных не найден.' +

                                          'Создать новую базу данных?', mtInformation,[mbYes,mbNo],0);

           if resp = mrYes then

               begin

                   {$I-}

                   rewrite(f);

                   {$I+}

           if IOResult = 0 then button1.enabled:=TRUE

               else ShowMessage('Ошибка создания файла БД.');

               end;

           end;

end;

// щелчок на кнопке Добавить

procedure TForm1.Button1Click(Sender: TObject);

var

   medal: TMedal;

begin

   with medal do

   begin

       country := ComboBox1.Text;

       sport   := ComboBox2.Text;

       person  := Edit1.Text;

       case  RadioGroup1.ItemIndex of

          0: kind := GOLD;

          1: kind := SILVER;

          2: kind := BRONZE;

       end;

   end;

   write(f, medal); // записать содержимое полей записи в файл

end;

// завершение работы программы

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

    CloseFile( f );  // закрыть файл

end;

end.

   Процедура TForm1.FormActivate открывает файл базы данных для добавления. Здесь следует обратить внимание на то, как это реализовано. Процедуру AppendFile, которая открывает файл для добавления в конец, использовать нельзя, т. к. файл не является текстовым. Поэтому файл сначала открывается процедурой Rewrite в режиме перезаписи, а затем процедура Seek устанавливает указатель чтения/записи в конец файла. Параметром процедуры seek является функция Fiiesize, значение которой равно размеру файла (в байтах).

   Процедура TForm1.Button1Click, которая запускается нажатием кнопки Добавить (Button1), выполняет непосредственное добавление записи в файл. Поля country и sport добавляемой записи заполняются из свойства Text комбинированных списков Страна (comboBox1) и Вид спорта (ComboBox2).

   Поле person формируемой записи заполняется из поля ввода Спортсмен (компонент Edit1), а содержимое поля medal определяется выбранной кнопкой компонента RadioGroup1.

   Процедура TForm1. FormClose закрывает файл базы данных поскольку тип TMedal используется двумя процедурами (TForm1.FormActivate и TForm1.Button1Сlick), то его описание помещено в раздел type модуля формы. Объявление файловой переменной f по этой же причине помещено в раздел объявления переменных модуля формы.

   В приведенном варианте программы предполагается, что списки стран и названий видов спорта формируются при помощи редактора списка строк во время разработки формы. Вместе с тем, список можно сформировать во время разработки программы. Для этого надо к свойству items применить метод Add. Например, список стран может быть сформирован при помощи следующих инструкций (их нужно поместить в процедуру Tform1.FormActivate):

Form1.ComboBox1.Item.Add('Россия');

Form1.ComboBox1.Item.Add('Австрия');

Form1.ComboBox1.Item.Add('Германия');

Form1.ComboBox1.Item.Add('Франция');

Чтение записи из файла

Рассмотрим программу, демонстрирующую процесс чтения и обработки записей файла. Программа Чтение записей из файла открывает файл, сформированный программой Добавление записи в файл, и, в зависимости от того, какой из переключателей все или выбрать – установлен, выводит список медалей, выигранных соответственно представителями всех стран или страны, название которой введено в поле Страна. Для вывода результата чтения из файла используется компонент Memo1. В табл. 2 приведены значения свойств компонентов формы. Так как компонент Memo1 предназначен только для просмотра информации, то свойству Readonly (только чтение, просмотр) присвоено значение True. Свойство scrollBars (полосы прокрутки) компонента Memo позволяет задавать отображаемые полосы прокрутки. По умолчанию свойству scrollBars присвоено значение ssNone, т. е. полосы прокрутки не отображаются. В рассматриваемом примере выводится вертикальная полоса, поэтому свойству ScrollBars присвоено значение ssVertical.

Таблица 2. Значения свойств компонентов

Свойство

Значение

RadioButton1 . Checked

True

Label1 .Enabled

False

ComboBox1 . Enabled

False

Memo1 . Readonly

True

Memo1. ScrollBars

ssVertical

   Для ввода названия страны используется компонент ComboBox1, что позволяет задавать имя не только прямым вводом названия, но и выбором из списка. Список стран нужно сформировать во время создания формы путем присвоения значения свойству items. Чтобы сразу после запуска программы список выбора страны был недоступен (т. к. выбран переключатель все группы Показать), свойству Enabled компонентов ComboBox1 и Label1 во время создания формы нужно присвоить значение False.

   Список ввода-выбора названия страны (ComboBox1) становится доступным в результате выбора во время работы программы переключателя выбрать. Процедура обработки события Onclick на переключателе RadioButton2 делает доступным поле ComboBox1.

Листинг 2. Чтение записей из файла

unit rdrec_;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type

   TForm1 = class(TForm)

       RadioButton1: TRadioButton; // переключатель Все

       RadioButton2: TRadioButton; // переключатель Выбрать

       ComboBox1: TComboBox;    // комбинированный список для ввода названия страны

       Memo1: TMemo;                   // поле вывода записей, удовлетворяющих условию запроса

       Button1: TButton;                  // кнопка ОК

       GroupBox1: TGroupBox;

       Label1: TLabel;             // текст Страна

       procedure Button1Click(Sender: TObject);

       procedure RadioButton2Click(Sender: TObject);

      procedure RadioButton1Click(Sender: TObject);

   private

       { Private declarations }

   public

       { Public declarations }

   end;

var

   Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);

type

   // тип медали

   TKind = (GOLD,SILVER,BRONZE);

   // запись файла

   TMedal = record

       country:string[20];

       sport:string[20];

       person:string[40];

       kind:TKind;

   end;

var

   f: file of TMedal;  // файл записей

   rec: TMedal;        // запись, прочитанная из файла

   n: integer;            // количество записей, удовлетворяющих запросу

   st: string[80];

begin

   AssignFile(f,'a:\medals.db');

   {$I-}

   Reset(f);  // открыть файл для чтения

   {$I-}

   if IOResult <> 0 then

       begin

          ShowMessage('Ошибка открытия файла БД.');

          Exit;

       end;

   // обработка БД

   if RadioButton2.Checked then

       Memo1.Lines.Add('*** ' + ComboBox1.Text + ' ***');

   n := 0;

   Memo1.Clear; // очистить список поля Memo

   while not EOF(f) do

       begin

           read(f, rec); // прочитать запись

           if  RadioButton1.Checked or (rec.country = ComboBox1.Text) then

           begin

               n := n + 1;

               st := rec.person + ', ' + rec.sport;

               if RadioButton1.Checked then

                 st := st + ', '+ rec.country;

              case rec.kind of

                 GOLD:  st := st+ ', золото';

                 SILVER:st := st+ ', серебро';

                 BRONZE:st := st+ ', бронза';

             end;

             Memo1.Lines.Add(st);

         end;

     end;

    CloseFile(f);

     if n = 0 then

        ShowMessage('В БД нет запрашиваемой информации.');

end;

// переключатель Выбрать

procedure TForm1.RadioButton2Click(Sender: TObject);

begin

   Label1.Enabled := True;

   ComboBox1.Enabled := True;   // теперь поле Страна доступно

   ComboBox1.SetFocus;              // курсор в поле Страна

end;

// переключатель Все

procedure TForm1.RadioButton1Click(Sender: TObject);

begin

   Label1.Enabled := False;

   ComboBox1.Enabled := False;   // теперь поле Страна не доступно

end;

end.

   Процедура TForm1.Button1Click открывает файл и последовательно считывает находящиеся в нем записи. Содержимое записи добавляется в поле Memo1, если прочитанная запись удовлетворяет условию запроса, т. е. содержимое поля country совпадает с названием страны, введенным  пользователем в поле редактирования компонента ComboBox1, или если выбран переключатель RadioButton1. Информация в поле Memo добавляется инструкцией Memo1.Lines.Add(st), которая является инструкцией применения метода Add (Добавить) к компоненту Memo1.

Динамические структуры данных

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

   Указатели

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

   Указатель, как и любая другая переменная программы, должен быть объявлен в разделе объявления переменных. В общем виде объявление указателя выглядит следующим образом:

Имя: ^ Тип;

   где:

  •  имя – имя переменной-указателя;
  •  Тип – тип переменной, на которую указывает переменная-указатель;

   значок ^ показывает, что объявляемая переменная является указателем.

   Примеры объявления указателей:

   p1: ^integer;

   р2: ^real;

   В приведенном примере переменная p1 – это указатель на переменную типа integer, a p2 – указатель на переменную типа real. Тип переменной, на которую ссылается указатель, называют типом указателя. Например, если в программе объявлен указатель р: ^integer, то говорят: ^р – указатель целого типа" или "р – это указатель на целое".

   В начале работы программы переменная-указатель "ни на что не указывает". В этом случае говорят, что значение указателя равно NIL. Зарезервированное слово NIL соответствует значению указателя, который ни на что не указывает. Идентификатор NIL можно использовать в инструкциях присваивания и в условиях. Например, если переменные pi и р2 объявлены как указатели, то инструкция

   p1 := NIL;

устанавливает значение переменной, а инструкция

   if р2 = NIL then ShowMessage('Указатель р2 не инициализирован!');

проверяет, инициализирован ли указатель р2.

   Указателю можно присвоить значение – адрес переменной соответствующего типа (в тексте программы адрес переменной – это имя переменной, перед которым стоит оператор @). Ниже приведена инструкция, после выполнения которой переменная р будет содержать адрес переменной n.

   р := @n;

   Помимо адреса переменной, указателю можно присвоить значение другого указателя при условии, что они являются указателями на переменную одного типа. Например, если переменные p1 и р2 являются указателями типа integer, то в результате выполнения инструкции

   p2 := p1;

переменные pi и р2 указывают на одну и ту же переменную.

   Указатель можно использовать для доступа к переменной, адрес которой содержит указатель. Например, если р указывает на переменную 1, то в результате выполнения инструкции

   р^ : = 5;

значение переменной i будет равно пяти. В приведенном примере значок ^ показывает, что значение пять присваивается переменной, на которую указывает переменная-указатель.

Динамические переменные

   Динамической переменной называется переменная, память для которой выделяется во время работы программы. Выделение памяти для динамической переменной осуществляется вызовом процедуры new. У процедуры new один параметр – указатель на переменную того типа, память для которой надо выделить. Например, если р является указателем на тип real, то в результате выполнения процедуры new(p); будет выделена память для переменной типа real (создана переменная типа real), и переменная-указатель р будет содержать адрес памяти, выделенной для этой переменной. У динамической переменной нет имени, поэтому обратиться к ней можно только при помощи указателя.

   Процедура, использующая динамические переменные, перед завершением своей работы должна освободить занимаемую этими переменными память или "уничтожить динамические переменные". Для освобождения памяти, занимаемой динамической переменной, используется процедура Dispose, которая имеет один параметр – указатель на динамическую переменную.

   Например, если р – указатель на динамическую переменную, память для которой выделена инструкцией new(p), то инструкция dispose (р) освобождает занимаемую динамической переменной память. Следующая процедура демонстрирует создание, использование и уничтожение динамических переменных.

Листинг 3. Создание, использование и уничтожение динамических переменных

procedure TForm1.Button1Click(Sender: TObject); var

   p1,p2,p3: ^Integer; // указатели на переменные типа integer 

begin

   // создадим динамические переменные типа integer

   // (выделим память для динамических переменных)

   New(p1);

   New(p2);

   New(p3);

   р1^ := 5;

   р2^ := 3;

   р3^ := р1^ + р2^;

   ShowMessage('Сумма чисел равна ' + IntToStr(р3^));

   // уничтожим динамические переменные

   // (освободим память, занимаемую динамическими переменными)

   Dispose(p1);

   Dispose(р2);

   Dispose(р3);

end;

   В начале работы процедура создает три динамические переменные. Две переменные, на которые указывают p1 и р2, получают значение в результате выполнения инструкции присваивания. Значение третьей переменной вычисляется как сумма первых двух.

Списки

   Указатели и динамические переменные позволяют создавать сложные динамические структуры данных, такие как списки и деревья.

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

type

   TPStudent = ^TStudent; // указатель на переменную типа TStudent 

   // описание типа элемента списка

   TStudent = record

       surname: string[20]; // фамилия 

       name: string[20];     // имя 

       group: integer;          // номер группы 

       address: string[60]; // домашний адрес 

       next: TPStudent;      // указатель на следующий элемент списка 

end;

var

   head: TPStudent; // указатель на первый элемент списка 

   Добавлять данные можно в начало, в конец или в нужное место списка. Во всех этих случаях необходимо корректировать указатели. На рис. изображен процесс добавления элементов в начало списка. После добавления второго элемента в список head указывает на этот элемент

   Следующая программа формирует список студентов, добавляя фамилии в начало списка. Данные вводятся в поля редактирования диалогового окна программы и добавляются в список нажатием кнопки Добавить (Button1).

Листинг 4. Добавление элемента в начало динамического списка

unit dlist1_;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type

   TForm1 = class(TForm)

       Label1: TLabel;

       Label2: TLabel;

       Label3: TLabel;

       Edit1: TEdit; // фамилия 

       Edit2: TEdit; // имя 

       Button1: TButton; // кнопка Добавить 

       Button2: TButton; // кнопка Показать 

       procedure ButtonlClick(Sender: TObject);

       procedure Button2Click(Sender: TObject);

   private

       { Private declarations }

   public

       { Public declarations }

   end;

var

   Form1: TForm1;

implementation

{$R *.DFM)

type

   TPStudent=^TStudent; // указатель на тип TStudent 

   TStudent = record

       f_name:string[20]; // фамилия 

       l_name: string[20]; // имя 

       next: TPStudent; // следующий элемент списка 

   end;

var

head: TPStudent; // начало (голова) списка 

// добавить элемент в начало списка

procedure TForml.Button1Click(Sender: TObject);

var

   curr: TPStudent; // новый элемент списка 

begin

   new(curr); // выделить память для элемента списка 

   curr^.f_name := Edit1.Text;

   curr^.1_name := Edit2.Text;

   // добавление в начало списка

   curr^.next := head;

   head := curr;

   // очистить поля ввода

   Edit1.text:=' ';

   Edit2.text: = ' ' ;

end;

// вывести список 

procedure TForml.Button2Click(Sender: TObject);

var

   curr: TPStudent; // текущий элемент списка 

   n:integer; // длина (кол-во элементов) списка 

   st:string; // строковое представление списка 

begin 

   n := 0;

   st := ' ';

   curr := head; // указатель на первый элемент списка 

   while curr <> NIL do begin

       n := n + 1;

       st := st + curr^.f_name + ' ' + curr^.1_name + #13;

       curr := curr^.next;  // указатель на следующий элемент 

   end;

   if n <> 0 then ShowMessage('Список:' + #13 + st)

       else ShowMessage('В списке нет элементов.');

end;

end.

   Добавление элемента в список выполняет процедура TForm1.Button1Click, которая создает динамическую переменную-запись, присваивает ее полям значения, соответствующие содержимому полей ввода диалогового окна, и корректирует значение указателя head. Вывод списка выполняет процедура TForm1.Button2Click, которая запускается нажатием кнопки Показать. Для доступа к элементам списка используется указатель curr. Сначала он содержит адрес первого элемента списка. После того как первый элемент списка будет обработан, указателю curr присваивается значение поля next той записи, на которую указывает curr. В результате этого переменная curr содержит адрес второго элемента списка. Таким образом, указатель перемещается по списку. Процесс повторяется до тех пор, пока значение поля next текущего элемента списка (элемента, адрес которого содержит переменная curr) не окажется равно NIL.

Упорядоченный список

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

Добавление элемента в список

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

   

   Следующая программа формирует список, упорядоченный по полю Фамилия. Данные вводятся в поля редактирования (Edit1 и Edit2) и нажатием кнопки Добавить (Button1) добавляются в список таким образом, что список всегда упорядочен по полю Фамилия.

Листинг 5. Добавление элементов в упорядоченный список 

unit dlist2_;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type

   TForm1 = class(TForm)

       Label1: TLabel;

       Label2: TLabel;

       Button1: TButton;

       Button2: TButton;

       Label3: TLabel;

       Edit1: TEdit;

       Edit2: TEdit;

       procedure ButtonlClick(Sender: TObject);

       procedure Button2Click(Sender: TObject);

       procedure FormActivate(Sender: TObject);

   private

       { Private declarations }

   public

       { Public declarations }

   end;

var

   Form1: TForm1;

implementation

($R *.DFM}

type

   TPStudent = ^TStudent; //указатель на тип TStudent 

   TStudent = record

       f_name:string[20]; // фамилия 

       l_name:string[20]; // имя

       next: TPStudent; // следующий элемент списка 

   end;

var

   head: TPStudent; // начало (голова) списка 

// добавить элемент в список

procedure TForm1.Button1Click(Sender: TObject);

var

   node: TPStudent; // новый узел списка 

   curr: TPStudent; // текущий узел списка 

   pre: TPStudent; // предыдущий, относительно curr, узел 

begin

   new(node); // создание нового элемента списка 

   node^.f_name:=Edit1.Text; // фамилия 

   node^.l_name:=Edit2.Text; // имя

   // добавление узла в список

   // сначала найдем в списке подходящее место для узла

   curr:=head;

   pre:=NIL;

   { Внимание! 

   Если приведенное ниже условие заменить

   на (node. f_name>curr^. f__name) and (curr<>NIL) ,

   то при добавлении первого узла возникает ошибка времени

   выполнения, т. к. curr = NIL и, следовательно,

   переменной curr. ^name нет!

   В используемом варианте условия ошибка не возникает, т. к.

   сначала проверяется условие (curr <> NIL), значение которого

   FALSE, и второе условие в этом случае не проверяется.

   }

   while (curr<> NIL) and (node.f_name > curr^.f_name) do

   begin

   // введенное значение больше текущего

   pre:= curr;

   curr:=curr^.next; // к следующему узлу 

   end;

   if pre = NIL then

       begin

       // новый узел в начало списка

       node^. next: =head;

       head:=node;

       end

   else

      begin

      // новый узел после pre, перед curr 

       node^.next:=рre^.next;

       рrе^.next:=node;

       end;

   Edit1.text:=' ';

   Edit2.text:=' ';

   Edit1.SetFocus;

end;

// отобразить список 

procedure TForm1.Button2Click(Sender: TObject);

var

   curr: TPStudent; // текущий элемент списка 

   n:integer; // длина (кол-во элементов) списка 

   st:string; // строковое представление списка 

begin

   n:=0;

   st: = ' ';

   curr:=head;

   while curr <> NIL do

       begin n:=n+l;

       st:=st+curr^.f_name+' '+curr^.l_name+#13;

       curr:=curr^.next;

       end; 

   if n <> 0 then ShowMessage('Список: '+#13+st)

       else ShowMessage('В списке нет элементов.');

end;

// начало работы программы 

procedure TForm1.FormActivate(Sender: TObject);

begin

   head:=NIL; // список пустой 

end;

end.

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

   Вывод списка выполняет процедура TForml.Button2Сlick, которая запускается нажатием кнопки Показать. После запуска программы и ввода нескольких фамилий, например, в такой последовательности: Иванов, Яковлев, Алексеев, петров, список выглядит так, как показано на рис.

   Удаление элемента из списка

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

   Поскольку узел является динамической переменной, то после исключения узла из списка занимаемая им память должна быть освобождена. Освобождение динамической памяти, или, как иногда говорят, "уничтожение переменной", выполняется вызовом процедуры dispose. У процедуры dispose один параметр — указатель на динамическую переменную. Память, занимаемая этой динамической переменной, должна быть освобождена. Например, в программе

var

   р: ^integer;

begin

   new(p);

   { инструкции программы }

   dispose(p);

end

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

   Следующая программа позволяет добавлять и удалять узлы упорядоченного списка. Процедуры добавления узла в список и вывода списка, а также объявление типа узла списка ничем не отличаются от соответствующих процедур рассмотренной ранее программы Упорядоченный динамический список 2, поэтому они здесь не приводятся. Удаление узла из списка выполняет процедура TForm1.Button3Click, которая запускается нажатием кнопки Удалить (Buttons). .

Листинг 6. Удаление узла из списка

unit dlist3_;

interface

uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;

type

   TForm1 = class(TForm)

       Label1: TLabel;

       Label2: TLabel;

       Button1: TButton;

       Button2: TButton;

       Label3: TLabel;

       Edit1: TEdit;

       Edit2: TEdit;

       Button3: TButton;

       procedure Button1Click(Sender: TObject);

       procedure Button2Click(Sender: TObject);

       procedure FormActivate(Sender: TObject);

       procedure Button3Click(Sender: TObject);

    private

       { Private declarations }

    public

       { Public declarations }

   end;

var

   Form1: TForm1;

implementation

{$R *.DFM}

type

   TPStudent=^TStudent; //указатель на тип TStudent

   TStudent = record

   f_name:string[20];  // фамилия

   l_name:string[20];  // имя

   next: TPStudent;    // следующий элемент списка

   end;

var

   head: TPStudent;  // начало (голова) списка

procedure TForm1.Button1Click(Sender: TObject);

var

   node: TPStudent; // новый узел списка 

   curr: TPStudent; // текущий узел списка 

   pre: TPStudent; // предыдущий, относительно curr, узел 

begin

   new(node); // создание нового элемента списка 

   node^.f_name:=Edit1.Text; // фамилия 

   node^.l_name:=Edit2.Text; // имя

   // добавление узла в список

   // сначала найдем в списке подходящее место для узла

   curr:=head;

   pre:=NIL;

  { Внимание! 

   Если приведенное ниже условие заменить

   на (node. f_name>curr^. f__name) and (curr<>NIL) ,

   то при добавлении первого узла возникает ошибка времени

   выполнения, т. к. curr = NIL и, следовательно,

   переменной curr. ^name нет!

   В используемом варианте условия ошибка не возникает, т. к.

   сначала проверяется условие (curr <> NIL), значение которого

   FALSE, и второе условие в этом случае не проверяется.

   }

   while (curr<> NIL) and (node.f_name > curr^.f_name) do

   begin

   // введенное значение больше текущего

   pre:= curr;

   curr:=curr^.next; // к следующему узлу 

   end;

   if pre = NIL then

       begin

       // новый узел в начало списка

       node^. next: =head;

       head:=node;

       end

   else

      begin

      // новый узел после pre, перед curr

       node^.next:=рre^.next;

       рrе^.next:=node;

       end;

   Edit1.text:=' ';

   Edit2.text:=' ';

   Edit1.SetFocus;

end;

// отобразить список 

procedure TForm1.Button2Click(Sender: TObject);

var

   curr: TPStudent; // текущий элемент списка 

   n:integer; // длина (кол-во элементов) списка 

   st:string; // строковое представление списка 

begin

   n:=0;

   st: = ' ';

   curr:=head;

   while curr <> NIL do

       begin n:=n+l;

       st:=st+curr^.f_name+' '+curr^.l_name+#13;

       curr:=curr^.next;

       end; 

   if n <> 0 then ShowMessage('Список: '+#13+st)

       else ShowMessage('В списке нет элементов.');

end;

procedure TForm1.Button2Click(Sender: TObject);

var

   curr: TPStudent; // текущий элемент списка 

   n:integer; // длина (кол-во элементов) списка 

   st:string; // строковое представление списка 

begin

   n:=0;

   st: = ' ';

   curr:=head;

   while curr <> NIL do

       begin n:=n+l;

       st:=st+curr^.f_name+' '+curr^.l_name+#13;

       curr:=curr^.next;

       end; 

   if n <> 0 then ShowMessage('Список: '+#13+st)

       else ShowMessage('В списке нет элементов.');

end;

// начало работы программы 

procedure TForm1.FormActivate(Sender: TObject);

begin

   head:=NIL; // список пустой 

end;

procedure TForm1.FormActivate(Sender: TObject);

begin

   head:=NIL; // список пустой 

end;

login

// щелчок на кнопке Удалить

procedure TForm1.Button3Click(Sender: TObject);

var

   curr:TPStudent; // текущий проверяемый узел

   pre: TPStudent; // предыдущий узел

   found:boolean;  // TRUE – узел, который надо удалить, если в списке

begin

   if head = NIL then

       begin

           MessageDlg('Список пустой!',mtError,[mbOk],0);

           Exit;

       end;

   curr:=head;   // текущий узел – первый узел

   pre:=NIL;     // предыдущего узла нет

   found:=FALSE;

   // найти узел, который надо удалить

   while (curr <> NIL) and (not found) do

       begin

           if (curr^.f_name = Edit1.Text) and (curr^.l_name = Edit2.Text) then 

               found:=TRUE // нужный узел найден

              else  // к следующему узлу

                  begin

                     pre:=curr;

                     curr:=curr^.next;

                  end;

       end;

        if found then

            begin

            // нужный узел найден

            if MessageDlg('Узел будет удален из списка!', mtWarning,[mbOk,mbCancel],0) <> mrYes

                then Exit;

            // удаляем узел

            if pre = NIL then  head:=curr^.next      // удаляем первый узел списка

                else pre^.next:=curr.next;

            Dispose(curr);

            MessageDlg('Узел' + #13 + 'Имя:'+Edit1.Text + #13 + 'Фамилия:' + Edit2.Text + #13 +

                                 'удален из списка.', mtInformation,[mbOk],0);

           end

    else // узла, который надо удалить, в списке нет

        MessageDlg('Узел' + #13 + 'Имя:' + Edit1.Text + #13 + 'Фамилия:' + Edit2.Text + #13 +

                             'в списке не найден.', mtError,[mbOk],0);

   Edit1.Text:=' ';

   Edit1.Text:=' ';

   Edit1.SetFocus;

end;

end.

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

Контрольные вопросы

  1.  К какому типу данных относится тип, определяемый программистом?
  2.  Запись перечисляемого типа.
  3.  Запись интервального типа.
  4.  Как записывается запись?
  5.  Как объявляется указатель?
  6.  Динамическая переменная.
  7.  Список.

Резюме

  1.  Тип, определенный программистом, может быть отнесен к перечисляемому; интервальному; составному типу данных (записи).
  2.  Определить перечисляемый тип – это значит перечислить все значения, которые может принимать переменная, относящаяся к данному типу. В общем виде объявление перечисляемого типа выглядит так: Тип =( Значение1, Значение2, ... Значение i), где: тип – имя перечисляемого типа данных; Значение i – символьная константа, определяющая одно из значений, которое может принимать переменная типа Тип.
  3.  Интервальный тип является отрезком или частью другого типа, называемого базовым. В качестве базового обычно используют целый тип данных (integer). При объявлении интервального типа указываются нижняя и верхняя границы интервала, т. е. наименьшее и наибольшее значение, которое может принимать переменная объявляемого типа. В общем виде объявление интервального типа выглядит так: Тип = НижняяГраница..ВерхняяГраница; где: тип – имя объявляемого интервального типа данных; НижняяГраница – наименьшее значение, которое может принимать переменная объявляемого типа; верхняяГраница – наибольшее значение, которое может принимать переменная объявляемого типа.
  4.  Запись – это структура данных, состоящая из отдельных именованных компонентов разного типа, называемых полями. Как любой тип, создаваемый программистом, тип "запись" должен быть объявлен в разделе type. В общем виде объявление типа "запись" выглядит так: Имя = record; Поле_1 : Тип_1;  Поле_2 : Тип_2;  Поле_К : Тип_К;  end; где: Имя – имя типа "запись"; record – зарезервированное слово языка Delphi, означающее, что далее следует объявление компонентов (полей) записи; поле_i и тил_i – имя и тип i-го компонента (поля) записи, где i=1, ..., k; end – зарезервированное слово языка Delphi, означающее, что список полей закончен.
  5.  Указатель – это переменная, значением которой является адрес другой переменной или структуры данных. Указатель, как и любая другая переменная программы, должен быть объявлен в разделе объявления переменных. В общем виде объявление указателя выглядит следующим образом: Имя: ^ Тип; где: имя – имя переменной-указателя; Тип – тип переменной, на которую указывает переменная-указатель; значок ^ показывает, что объявляемая переменная является указателем.
  6.  Динамической переменной называется переменная, память для которой выделяется во время работы программы. Выделение памяти для динамической переменной осуществляется вызовом процедуры new. У процедуры new один параметр – указатель на переменную того типа, память для которой надо выделить. У динамической переменной нет имени, поэтому обратиться к ней можно только при помощи указателя. Процедура, использующая динамические переменные, перед завершением своей работы должна освободить занимаемую этими переменными память или "уничтожить динамические переменные". Для освобождения памяти, занимаемой динамической переменной, используется процедура Dispose, которая имеет один параметр – указатель на динамическую переменную.
  7.  Указатели и динамические переменные позволяют создавать сложные динамические структуры данных, такие как списки и деревья. Каждый элемент списка (узел) представляет собой запись, состоящую из двух частей. Первая часть -  информационная. Вторая часть отвечает за связь со следующим и с предыдущим элементом списка. Для того чтобы программа могла использовать список, надо определить тип компонентов списка и переменную-указатель на первый элемент списка.

Домашнее задание   

Выполнить примеры из лекций.

PAGE  122


 

А также другие работы, которые могут Вас заинтересовать

31363. МОНИТОРИНГ КОММЕРЧЕСКИХ БАНКОВ В СОВРЕМЕННОЙ РОССИИ 296.5 KB
  Анализ функционирования и взаимодействия этих систем дает основания делать некоторые выводы и позволяет сформулировать основные проблемы, решение которых будет способствовать совершенствованию системы налоговых отношений и контроля, а также позволит повысить результативность применения норм налогового права в части налога на прибыль, взимаемого с коммерческих банков.
31364. Влияние визуальной самоподачи образа «я» на конфликтность субъекта общения 8.86 MB
  На современном этапе развития психологии необходимо получение новых научных знаний о роли семиотических компонентов внешнего облика в возникновении и развитии межличностных конфликтов, выявление тех особенностей самоподачи субъекта, которые являются «конфликтогенными». Изучение проблемы не только углубит общенаучное понимание механизмов возникновения конфликтности, но и позволит осуществлять профилактику, коррекцию и поиск путей конструктивного разрешения конфликтов на невербальном уровне, подойти по новому к поиску путей коррекции лиц с повышенной конфликтностью.
31365. Процесс глобализации и национальная экономика 1.18 MB
  Влияние глобализации на позицию страны в системе мирохозяйственных связей. Место страны в системе мирохозяйственных связей: концептуальные положения. Глобальная конкурентоспособность страны: концептуальные основы. Цикл жизнедеятельности страны как концептуальная основа анализа ее глобальной конкурентоспособности.
31366. ПУТЬ БОРИСА ПАСТЕРНАКА К “ДОКТОРУ ЖИВАГО” 1.05 MB
  Другие темы в лирике и в романе. Сравнительный анализ образной парадигмы в лирике и в романе. Сравнительный анализ парадигмы мотивов в лирике и в романе. Многие положения €œОхранной грамоты€ встречаются в романе.
31367. Стратегическое управление фирмой в кризисных условиях 751.5 KB
  Они должны быть достаточно хорошо знакомы с деятельностью компании чтобы знать какие изменения вносить в стратегию. Ещё одно преимущество заключающееся в поощрении активного управления а не в простом реагировании на внешние факторы приводит к тому что новаторские стратегии могут стать ключом к улучшению результатов деятельности компании в долгосрочном плане. Из истории бизнеса известно что высоких результатов добивались обычно компании инициативные и ведущие а не те которые просто реагировали на изменившиеся условия или защищались....
31368. ФИЛОСОФИЯ БЕЗОПАСНОСТИ 414 KB
  ПОЛИКАРПОВ ФИЛОСОФИЯ БЕЗОПАСНОСТИ эссе Ответственный редактор д. Философия безопасности. В эссе рассматривается одна из практически не разработанных проблем современного философского и научного знания – философские основы безопасности жизнедеятельности человека и социума. Автор на основе богатого материала анализирует различные виды опасности и безопасности военную экономическую социальную психологическую информационную и др.
31369. ОСНОВНЫЕ ПРИЕМЫ ФОРМИРОВАНИЯ ОДНОЛЕТНИХ БОБОВО-ЗЛАКОВЫХ АГРОЦЕНОЗОВ В ЛЕСОСТЕПИ СРЕДНЕГО ПОВОЛЖЬЯ 2.79 MB
  Использовались: ячмень овес вика горох одновидовые посевы и смеси. В Тарской сельскохозяйственной опытной станции Омской области высевали овес ячмень горох вику. Колоскина 1979 хорошим компонентом вики в ряде районов являются подсолнечник ячмень суданская трава. Объектами исследований были люпин узколистный люпин желтый вика яровая пшеница яровая ячмень и овес.
31370. НАЛОГОВАЯ СИСТЕМА КАК ИНСТИТУТ СОЦИАЛЬНОГО УПРАВЛЕНИЯ 988 KB
  ИНСТИТУЦИОНАЛЬНЫЙ ПОДХОД К ИССЛЕДОВАНИЮ НАЛОГОВОЙ СИСТЕМЫ В СОЦИОЛОГИИ УПРАВЛЕНИЯ. Теоретическое исследование налоговой системы как института социального управления. Функциональная структура налоговой системы как института социального управления. СТАНОВЛЕНИЕ И РАЗВИТИЕ НАЛОГОВОЙ СИСТЕМЫ КАК ИНСТИТУТА СОЦИАЛЬНОГО УПРАВЛЕНИЯ.
31371. МЕТОДОЛОГИЯ СТАТИСТИЧЕСКОГО ИССЛЕДОВАНИЯ ДЕЯТЕЛЬНОСТИ ДИВЕРСИФИЦИРОВАННЫХ КОРПОРАТИВНЫХ ОБЪЕДИНЕНИЙ в россии 3.47 MB
  Исследование влияния изменения фактора прибыльности продукции ДКО Исследование влияния изменения фактора материалоемкости продукции Исследование влияния изменения фактора трудоемкости продукции Исследование влияния изменения фактора амортизациеемкости продукции