Обучающий курс. 24. Записи (часть 1)Источник: delphi
ВведениеВ прошлом уроке мы познакомились со множествами - более сложной структурой данных нежели хранение значения одной переменной. Сегодня речь пойдёт о записях. Запись (англ. record) - это сложный тип данных, позволяющие объединить данные разных типов. Запись можно назвать наиболее общим сложным типом данных. Название "запись" появилось из тех соображений, что данные разного типа можно встретить в таблицах: в каждой строке записаны сразу несколько разных значений. Таким образом, одна запись соответствует одной строке данных: она имеет несколько полей, каждое из которых хранит своё значение. ПримерДопустим, мы хотим хранить информацию о людях, заполнивших анкету на поступление на работу. Нас интересуют: ФИО человека, возраст, образование (среднее/высшее), владение компьютером, владение иностранными языками. Все эти данные мы можем объединить в запись. Дальнейшее повествование будет основываться на этом примере. Описание записиЧтобы работать в программе с записями, нужно сначала описать соответствующий тип данных. Запись описывается следующим образом: type имя_типа_записи = record {поля записи} end; В блоке полей описание идёт точно так же, как описание переменных: указывается имя поля, далее через двоеточие тип данных этого поля. Сами поля разделяются точкой с запятой. Подходите ответственно к выбору типов данных для полей записи. Если при описании переменной она хранится в единственном экземпляре, то записи, как правило, используются для хранения некоторого набора данных (таблицы с несколькими строками, образно говоря). Это значит, что каждое из полей будет храниться в памяти столько раз, сколько строк данных у вас будет. Если в таблице 1000 строк, то объём занимаемой памяти будет немалым, да и скорость работы с данными может заметно уменьшиться из-за выбора избыточных типов данных. Для нашего примера: type TPerson = record Name: String; Age: Byte; Education,PC: Boolean; Foreign: set of TForeignLanguages; end; Разберём поля по порядку. Для ФИО вполне подходит текстовая строка типа String. 255 символов нам хватит сполна. Для хранения возраста целесообразно выбрать тип данных Byte (число от 0 до 255). Не думаю, что на работу будут устраиваться люди, возраст которых превышает 255 :-) Использовать тип Integer в данном случае нецелесообразно - мало того, что у него максимальное значение превышает 32.000, да ещё и отрицательные числа поддерживаются. Для поля "образование" выбран логический тип данных. Условимся, что True - это высшее образование, False - высшего нет (т.е. среднее). PC - владение компьютером, здесь всё понятно. Для хранения иностранных языков здесь используется множество. Ведь человек может знать несколько языков, не так ли? Описание типа данных, на основе которого построено множество, следующее: type TForeignLanguages = (flEnglish, flGerman, flFrench); Как видно, это перечислимый тип данных с тремя возможными значениями. Если вам мало английского, немецкого и французского, можете добавить в список и другие. Так как свойство Foreign нашей записи - множество, то мы сможем легко хранить информацию, например, о том, что человек знает и английский, и немецкий. Удобно, не правда ли? Если человек не знает никаких иностранных языков, множество будет пустое. Конечно, можно было бы добавить в список что-то вроде flNone для указания отсутствия иностранных языков, но зачем усложнять себе жизнь? Итак, наша запись готова. Чтобы работать с ней в программе, разместить её следует в глобальном разделе type. Примеры других записейСуществует множество готовых записей в Delphi, которые позволяют хранить некоторые часто встречающиеся данные. TPoint - запись с двумя полями - X и Y - служит для хранения координат точки. Описана эта запись следующим образом: TPoint = record X: Longint; Y: Longint; end; TRect - позволяет хранить данные о прямоугольной области. Свойства Left, Top, Right и Bottom отвечают за координаты сторон прямоугольника, а координаты TopLeft и BottomRight типа TPoint - указывают на левую верхнюю и правую нижнюю вершины соответственно (эти вершины позволяют однозначно определить прямоугольник на плоскости, стороны которого параллельны осям координат). Однако в один момент времени используется только один набор из этих свойств - либо 4 координаты, либо 2 точки. О том, как это делается, мы поговорим позже. Ещё один пример - хранение даты. Дату можно разбить на число, месяц и год, и хранить каждое значение отдельно: TDate = record Day: 1..31; Month: 1..12; Year: 1900..2100; end; Работа с записямиЧтобы обратиться к конкретному полю записи, применяется стандартная конструкция, с которой вы давно знакомы: имя_переменной.название_поля Т.е. имя переменной или объекта и его поле разделяются точкой. Чтобы работать с записью, нужно определить переменную, тип которой - наша запись. До этого мы всего лишь описали нашу запись, но на самом деле никаких значений ещё нигде нет. Описание происходит стандартным образом: var Person: TPerson;
Имя переменной вы, естественно, можете дать любое, просто логично называть всё своими именами. Вводим анкеты - интерфейсДля начала давайте создадим небольшой интерфейс, который позволит нам вводить информацию из анкет:
Компонент TCheckListBox расположен на странице Additional. Это список, аналогичный TListBox, но в каждой строке есть TCheckBox, т.е. строка либо отмечена, либо нет. В свойство Items сразу занесите наши 3 языка, вводя каждый на отдельной строке. Осталось добавить кнопку, которая будет выполнять все действия. Общий вид формы у меня получился таким: Теперь перейдём к программной части. Вводим анкеты - программированиеИтак, перейдём к программированию нашей кнопки "Добавить". Чтобы использовать нашу запись, создадим переменную соответствующего типа: var Person: TPerson;
Теперь у нас есть один экземпляр нашей записи и в него можно смело записывать данные. Никаких трудностей возникнуть не должно: procedure TForm1.Button1Click(Sender: TObject); var Person: TPerson; begin Person.Name:=Edit1.Text; Person.Age:=UpDown1.Position; Person.Education:=CheckBox1.Checked; Person.PC:=CheckBox2.Checked; Person.Foreign:=[]; if CheckListBox1.Checked[0] then Person.Foreign:=Person.Foreign + [flEnglish]; if CheckListBox1.Checked[1] then Person.Foreign:=Person.Foreign + [flGerman]; if CheckListBox1.Checked[2] then Person.Foreign:=Person.Foreign + [flFrench]; end; Ну и конечно комментарии. ОптимизацияВроде у нас всё хорошо, да вот об оптимизации не стоит забывать. В данном случае получится скорее не оптимизация, а сокращение кода. Помните про оператор with? Да-да, тот самый, что позволяет вынести имя объекта за скобку? Вот его и применим, вынеся Person вперёд: procedure TForm1.Button1Click(Sender: TObject); var Person: TPerson; begin with Person do begin Name:=Edit1.Text; Age:=UpDown1.Position; Education:=CheckBox1.Checked; PC:=CheckBox2.Checked; Foreign:=[]; if CheckListBox1.Checked[0] then Foreign:=Foreign + [flEnglish]; if CheckListBox1.Checked[1] then Foreign:=Foreign + [flGerman]; if CheckListBox1.Checked[2] then Foreign:=Foreign + [flFrench]; end; end; Уже лучше, согласитесь? Но всё равно не идеально. В частности, строки проверки иностранных языков выглядят как-то нелепо - они мало отличаются друг от друга. К тому же, что делать, если в списке будет 100 языков? 100 раз писать одно и то же? Нет, конечно. Единственная загвоздка в данном случае может возникнуть только в том, как преобразовать номер строки (0, 1, 2) в значение типа данных (flEnglish, flGerman, flFrench)? Эти значения ведь нельзя указать в виде строки... А решение простое. Если вспомним, что перечислимые типы данных упорядочены, а каждому "текстовому" значению просто ставится в соответствие число, начиная с нуля, проблема мигом решится. Итак, нам нужно просто привести типы данных, а именно - число к типу TForeignLanguages. Дальше элементарно - заводим цикл по строкам и "на лету" формируем требуемое множество. Итак, окончательный вариант: procedure TForm1.Button1Click(Sender: TObject); var Person: TPerson; I: Byte; begin with Person do begin Name:=Edit1.Text; Age:=UpDown1.Position; Education:=CheckBox1.Checked; PC:=CheckBox2.Checked; Foreign:=[]; for I := 0 to CheckListBox1.Items.Count-1 do if CheckListBox1.Checked[I] then Foreign:=Foreign + [TForeignLanguages(I)]; end; end; Красивый и при этом самый короткий код. ЗаключениеДанные мы получили и занесли их в память. А что с ними делать дальше? Наверное, сохранить их где-то для постоянного хранения. Например, в файле. О том, как это сделать - в следующий раз. |