Записи (часть 1)
Автор: |
© Ерёмин А.А., 2008 |
Дневник - по-дневная запись тех поступков и мыслей, о которых записывающий может вспомнить не краснея. |
Номер урока: |
24 |
Введение
В прошлом уроке мы познакомились со множествами - более сложной структурой данных нежели хранение значения одной переменной. Сегодня речь пойдёт о записях.
Запись (англ. 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;
Имя переменной вы, естественно, можете дать любое, просто логично называть всё своими именами.
Вводим анкеты - интерфейс
Для начала давайте создадим небольшой интерфейс, который позволит нам вводить информацию из анкет:
- ФИО - TEdit (Edit1)
- Возраст - TEdit (Edit2)
- Высшее образование - TCheckBox (CheckBox1)
- Владение компьютером - TCheckBox (CheckBox2)
- Владение иностранными языками - TCheckListBox (CheckListBox1)
Компонент TCheckListBox расположен на странице Additional. Это список, аналогичный TListBox, но в каждой строке есть TCheckBox, т.е. строка либо отмечена, либо нет. В свойство Items сразу занесите наши 3 языка, вводя каждый на отдельной строке.
Чтобы было удобно вводить возраст, сделаем вот что. На странице Win32 есть компонент TUpDown - это две кнопки ("вверх-вниз" или "влево-вправо"). Если в свойстве Associate выбрать наше поле ввода Edit2, то кнопки автоматически разместятся рядом с полем и нажатие на них будет автоматически изменять число в поле. В само поле лучше изначально вписать какой-нибудь "реальный" возраст, чтобы не пришлось долго щёлкать, добираясь до нужного значения. Например, можно вписать 25.
В поле Edit1 никакого текста быть не должно. Все галочки в остальных компонентах убраны. Таково изначальное состояние нашего интерфейса.
Осталось добавить кнопку, которая будет выполнять все действия.
Общий вид формы у меня получился таким:
Теперь перейдём к программной части.
Вводим анкеты - программирование
Итак, перейдём к программированию нашей кнопки "Добавить".
Чтобы использовать нашу запись, создадим переменную соответствующего типа:
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;
Ну и конечно комментарии.
Возраст в данном случае лучше взять из этих самых "кнопок-стрелочек" - там есть соответствующее свойство Position. А почему не из Edit2, спросите вы? А вот почему. Во-первых, если брать значение из Edit, нужно преобразовывать его в число функцией StrToInt(). Но это не самое страшное. А вот второе, что придётся делать - контролировать это значение. В Edit запросто можно ввести отрицательное число, да или вообще буквы. Тогда нужно ещё и введёную строку проверять - а число ли это вообще? Зато, имея TUpDown, этих проблем нет. Даже если ввести в Edit буквы, UpDown сохранит своё текущее значение и никаких проблем не будет. Кстати, у UpDown1 можно задать свойство Max - максимальное значение. Изначально там стоит 100. Если не хотите принимать на работу, скажем, людей старше 60, впишите туда 60.
Теперь что касается иностранных языков. Для начала множество лучше сделать пустым, мало ли что? А дальше проверяются строки из списка. Узнать, выбрана строка или нет, можно с помощью свойства Checked, указав в квадратных скобках номер (индекс) строки, которые начинаются с нуля. Если строка выбрана, добавляем во множество соответствующий элемент. Если ни одна строка не выбрана, множество останется пустым.
Оптимизация
Вроде у нас всё хорошо, да вот об оптимизации не стоит забывать. В данном случае получится скорее не оптимизация, а сокращение кода. Помните про оператор 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;
Красивый и при этом самый короткий код.
Заключение
Данные мы получили и занесли их в память. А что с ними делать дальше? Наверное, сохранить их где-то для постоянного хранения. Например, в файле. О том, как это сделать - в следующий раз.
Автор: Ерёмин А.А.
Статья добавлена: 28 сентября 2008
Следующая статья: Обучающий курс. 25. Работа с файлами и каталогами (часть 1) »
Зарегистрируйтесь/авторизируйтесь,
чтобы оценивать статьи.
Статьи, похожие по тематике
- Обучающий курс. 10. Логические выражения
- Обучающий курс. 12. Функции и процедуры. Математические вычисления. Случайные числа
- Обучающий курс. 14. Выбор (ветвления)
- Обучающий курс. 16. Циклы - общее понятие; цикл с параметром
- Обучающий курс. 17. Циклы - цикл с предусловием и цикл с постусловием
- Обучающий курс. 18. Работа со строками и символами
- Обучающий курс. 19. Подпрограммы (часть 1)
- Обучающий курс. 20. Подпрограммы (часть 2)
- Обучающий курс. 21. Подпрограммы (часть 3)
- Обучающий курс. 22. Простые типы данных
- Обучающий курс. 23. Множества
Для вставки ссылки на данную статью на другом сайте используйте следующий HTML-код:
Ссылка для форумов (BBCode):
Быстрая вставка ссылки на статью в сообщениях на сайте:
{{a:124}} (буква a — латинская) — только адрес статьи (URL);
{{статья:124}} — полноценная HTML-ссылка на статью (текст ссылки — название статьи).
Поделитесь ссылкой в социальных сетях:
Комментарии читателей к данной статье
Репутация: нет |
zvygin1964 (18 апреля 2015, 15:41): Удалось использовать:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm1 = class(TForm) Button1: TButton; Image1: TImage; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private { Private declarations } public { Public declarations } end; type // В этом месте правильно? Tcoordinates = record //http://cybern.ru/delphi-types.html X: integer; Y: integer; end; var Form1: TForm1; bmps: array of TBitmap; coordinates: array [1..10000] of Tcoordinates; const bmp_cnt=5;//не более числа файлов в ресурсе или равно implementation {$R *.dfm} {$R Resources.RES} procedure TForm1.FormCreate(Sender: TObject); var i: integer; const res_name='img'; begin SetLength(bmps, bmp_cnt); //SetLength(coordinates,10000); // [Error] Incompatible types for i:=0 to bmp_cnt-1 do begin bmps[i]:=TBitmap.Create; with bmps[i] do begin LoadFromResourceName(HInstance,res_name+IntToStr(i+1)); //img1, img2 .. img30 Transparent:=true; TransParentColor:=Canvas.Pixels[1,1]; end; end; coordinates[1].X:=100; //например для n:=[1] coordinates[1].Y:=180; end; procedure TForm1.Button1Click(Sender: TObject); begin Form1.Image1.canvas.Draw(coordinates[1].X,coordinates[1].Y,bmps[1]); end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); var i: integer; begin for i:=0 to bmp_cnt-1 do begin bmps[i].Free; end; bmps:=nil; // coordinates:=nil; // [Error] Incompatible types: 'Array' and 'Pointer' end; end. |
Репутация: +40 |
Ерёмин А.А. (3 октября 2011, 12:59): Цитата (Никонов Алексей): нужно указывать выше объявления самой записи, иначе получается что множество объявлено ниже Разумеется. Про общее правило, что описание должно быть до первого использования, было в одном из уроков. |
Репутация: нет |
Никонов Алексей (3 октября 2011, 10:12): Немного уточнений:
Эту часть кода Type TForeignLanguages =(flEnglish,flGerman,flFrench); нужно указывать выше объявления самой записи, иначе получается что множество объявлено ниже, а это, следуя основному правилу Delphi -- неправильно. |
Репутация: +40 |
Ерёмин А.А. (9 августа 2010, 22:37): Хватит издеваться. Если нечего написать — лучше не писать вовсе.
|
Репутация: +1 |
antoca (9 августа 2010, 19:40): Иди в школу, если так уж хочется.
|
Репутация: нет |
Тамара (5 февраля 2009, 19:13): Куда здачки пропали? и ДЗ? Очень бы хотелось увидеть вновь!!:)
|
Оставлять комментарии к статьям могут только зарегистрированные пользователи.