Последовательность действий, требуемых для модификации уже существующего файла, несколько отличается от той, которая необходима для записи в файл или чтения из него. Прежде всего файл должен быть открыт для модификации (т.е. для чтения и записи одновременно). Для этой цели служит предикат openmodify.
Предикат
openmodify(datafilel,"FILE1.DAT")
успешен только в том случае, если файл уже присутствует на диске. Другими словами, файл должен быть создан при помощи предиката openwrite. При этом следует, конечно, помнить, что openwrite создает новый файл даже тогда, когда файл с таким именем уже имеется, затирая при этом все, что в нем находилось.
Содержимое файла можно рассматривать как поток символов. Каждый символ находится в файле на какой-то позиции. Позиция определяется удаленностью от первого символа файла. Таким образом, первый символ в файле занимает нулевую позицию, второй — первую и т. д. Вообразим себе теперь невидимый указатель, который можно сместить на любую позицию файла. Этот указатель определяет позицию, из которой будет прочитан или в которую будет записан очередной символ.
Когда файл открывается для модификации, указатель помещается в начало файла. Турбо-Пролог позволяет смещать указатель при помощи предиката filepos, который будет подробно рассмотрен в разделе, посвященном файлам прямого доступа. В настоящий момент вам требуется знать лишь то, где будет располагаться указатель после вызова openmodify.
Модификацию файла можно разбить на следующие шаги:
1. Открытие файла
openwodify(datafilel."FILE1.DAT")
2. Переадресация вывода в файл
writedevice(datafilel)
3. Запись в файл новых данных.
4. Использование произвольных предикатов и правил, отвечающих целям программы.
5. Закрытие файла
closefile(datafilel)
Примером может служить
openmodify(datafile1."FILE1.DAT"),
wr1tedev1ce(datafilel),
< правила для выборочной записи в файл >,
< любые другие правила или предикаты >
closefile(datafilel).
Дозапись в конец уже существующего файла
Возможность записать новые данные в конец уже существующего файла обеспечивается в Турбо-Прологе предикатом openappend. Когда файл открывается для дозаписи, указатель файла смещается в его конец.
Следующие шаги необходимо предпринять для добавления новых данных в конец файла:
1. Открытие файла
openappend(datafile1."FILE1.DAT")
2. Переадресация вывода в файл
writedevice(datafile1).
3. Дозапись в файл новых данных при помощи соответствующих правил.
4. Использование произвольных предикатов и правил, отвечающих целям программы.
5. Закрытие файла
closefile(datafilel)
Например,
openappend(datafi1e1,"FILE1.DAT"),
writedevice(datafilel).
< любые правила для дозаписи в файл >,
< любые другие правила или предикаты >
closefile(datafile1).
Задание.
Разработать программу для формирования базы данных с клавиатуры и записи в файл. После ввода слова «list», вывести все содержимое файла на экран (таблица.5).
Таблица 5
Вариант | Предметная область | Количество объектов | Количество параметров объекта |
Города | |||
Автомобили | |||
Компьютеры | |||
Дома | |||
Самолеты | |||
Страны | |||
Студенты | |||
Партии | |||
Сотрудники | |||
Вакансии | |||
Операционные системы | |||
Книги | |||
Телефонный справочник | |||
Предприятия | |||
Автобусные маршруты |
Лабораторная работа № 6
Структуры
Структуры данных вместе с сопоставлением, автоматическими возвратами и арифметикой представляют собой мощный инструмент программирования.
База данных может быть представлена на Прологе в виде множества фактов. Например, в базе данных о семьях каждая семья может описываться одним предложением. На рис. 28 показано, как информацию о каждой семье можно представить в виде структуры.
Каждая семья состоит из трех компонент: мужа, жены и детей.
Поскольку количество детей в разных семьях может быть разным, то их целесообразно представить в виде списка, состоящего из произвольного числа элементов. Каждого члена семьи в свою очередь можно представить структурой, состоящей из четырех компонент: имени, фамилии, даты рождения и работы. Информация о работе - это либо «не работает», либо указание места работы и оклада (дохода). Информацию о семье, изображенной на рис.28, можно занести в базу данных с помощью предложения:
семья(
членсемьи(том,фокс,дата(7,май,1950),
работает(bbс, 15200)),
[членсемьи(знн, фокc, дата(9, май, 1951),
неработает),
[членсемьи(пат, фокc, дата(5, май, 1973),
неработает),
членсемьи(джим, фокc, дата(5, май, 1973),
неработает)]).
Рис. 28.Структурированная информация о семье
Тогда база данных будет состоять из последовательности фактов, подобных этому, и описывать все семьи, представляющие интерес для нашей программы. ; В действительности Пролог очень удобен для извлечения необходимой информации из такой базы данных. Здесь можно ссылаться на объекты, не указывая в деталях всех их компонент. Можно задавать только структуру интересующих вас объектов и оставлять конкретные компоненты без точного описания или лишь с частичным описанием. На рис. 29 приведено несколько примеров. Так, в запросах к базе данных можно ссылаться на всех Армстронгов с помощью терма
семья(членсемьи(, армстронг,_, _), _, _)
Символы подчеркивания обозначают различные анонимные переменные, значения которых нас не заботят. Далее можно сослаться на все семьи с тремя детьми при помощи терма:
семья(_, _, [ _, _, _ ])
Чтобы найти всех замужних женщин, имеющих по крайней мере троих детей, можно задать вопрос:
- семья(_,членсемьи(Имя, Фамилия, _, _),[ _, _, _ | ù).
Главным моментом в этих примерах является то, что указывать интересующие нас объекты можно не только по их содержимому, но и по их структуре. Мы задаем одну структуру и оставляем ее аргументы в виде слотов (пропусков).
Рис. 29. Описания объектов по их структурным свойствам;
а — любая семья Армстронгов; б — любая семья, имеющая ровно трех детей; с — любая семья, имеющая по крайней мере три ребенка. Структура (с) дает возможность получить имя в фамилию жены конкретизацией переменных Имя и Фамилия
Можно создать набор процедур, который служил бы утилитой, делающей взаимодействие с нашей базой данных более удобным. Такие процедуры являлись бы частью пользовательского интерфейса. Вот некоторые полезные процедуры для нашей базы данных:
муж(X):- /* X – муж */
семья(X, _, _).
жена(X):— /* X – жена */
семья(_, X, __).
ребенок(X):- /* X – ребенок */
семья(_, _, Дети),
принадлежит(X, Дети).
принадлежит(X, [X | L ].
принадлежит(X, [Y | L ]:-
принадлежит(X, L).
существует(Членсемьи):-
/*Любой член семьи в базе данных*/
муж(Членсемьи);
жена(Членсемьи);
ребенок(Членсемьи).
датарождения(Членсемьи(_, _,Дата, _),Дата).
доход(Членсемьи(_, _, _,работает(_,S)),S).
/* Доход работающего */
доход(Членсемьи(_, _, _, неработает), 0).
/* Доход неработающего */
Этими процедурами можно воспользоваться, например, в следующих запросах к базе данных:
Найти имена всех людей из базы данных:
- существует(членсемьи(Имя, Фамилия, _, _)).
Найти всех детей, родившихся в 1981- году:
- ребенох(X),
датарождения(X, дата(_, _, 1981)).
Найти всех работающих жен:
- жена(членсемьи(Имя,Фамилия, _,работает(_, _))).
Найти имена и фамилия людей, которые не работают и родились до 1963 года:
- существует (членсемьи), (Имя, Фамилия,
дата(,,Год), неработает)),
Год < 1963.
Найти людей, родившихся до 1950 года, чей доход меньше, чем 8000:
- существуете(Членсемьи),
датарождения(Членсемьи, дата(_, _,Год)),
Год < 1950,
доход(Членсемьи, Доход),
Доход < 8000.
Найти фамилии людей, имеющих по крайней мере трех детей:
-семья(членсемьи(_, Фамилия, _, _),_,[,,,| _ ]).
Для подсчета общего дохода семьи полезно определить сумму доходов людей из некоторого списка в виде двухаргументного отношения:
общий(Список _ Людей, Сумма _ их _ доходов)
Это отношение можно запрограммировать так:
общий([], 0). /* Пустой список людей */
общий([ Человек | Список], Сумма):-
доход(Человек, S),
/* S-доход первого человека */
общий(Список, Остальные),
/*Остальные-сумма доходов остальных */
Сумма is S + Остальные.
Теперь общие доходы всех семей могут быть найдены с помощью вопроса:
- семья(Муж, Жена, Дети),
общий([Муж, Жена | Дети], Доход).
Пусть отношение длина подсчитывает количество элементов списка. Тогда мы можем найти все семьи, которые имеют доход на члена семьи, меньший, чем 2000, при помощи вопроса:
- семья(Муж, Жена, Дети),
общий([ Муж, Жена \ Дети], Доход),
длина([ Муж, Жена | Дети], N),
Доход/N < 2000.
Абстракция данных
Абстракцию данных можно рассматривать как процесс 1 организации различных фрагментов информации в единые логические единицы (возможно, иерархически), придавая ей при этом некоторую концептуально осмысленную форму. Каждая информационная единица должна быть легко доступна в программе. В идеальном случае все детали реализации такой структуры должны быть невидимы пользователю этой структуры. Самое главное в этом процессе — дать программисту возможность использовать информацию, не думая о деталях ее действительного представления.
Обсудим один из способов реализации этого принципа на Прологе. Рассмотрим снова пример с семьей. Каждая семья — это набор некоторых фрагментов информации. Все эти фрагменты объединены в естественные информационные единицы, такие, как «член семьи» или «семья», и с ними можно обращаться как с едиными объектами. Предположим опять, что информация о семье структурирована так же, как на рис. 117. Определим теперь некоторые отношения, с помощью которых пользователь может получать доступ к конкретным компонентам семьи, не зная деталей рис. 117. Такие отношения можно назвать селекторами, поскольку они позволяют выбирать конкретные компоненты. Имя такого отношения-селектора будет совпадать с именем компоненты, которую нужно выбрать. Отношение будет иметь два аргумента: первый — объект, который содержит компоненту, и второй - саму компоненту:
отношение_селектор(Объект,Выбранная_компонента)
Вот несколько cелекторов для структуры семья:
муж(семья Муж, _,), Муж).
жена(семья(,Жена,), Жена).
дети(семья(_, _, СписокДетей), СписокДетей).
Можно также создать селекторы для отдельных детей семьи:
первыйребенок(Семья, Первый):-
дети(Семья, [Первый | ù).
второйребенок(Семья, Второй):-
дети(Семья, [_, Второй | ù).
…
Можно обобщить этот селектор для выбора N-го ребенка:
n ребенок(N,Семья, Ребенок):-
дети(Семья, СписокДетей),
n _ элемент(N, СписокДетей, Ребенок)
/* N-й элемент списка */
Другим интересным объектом является «член семьи». Вот некоторые связанные с ним селекторы, соответствующие рис. 28:
имя(членсемьи(Имя,_, _, _), Имя).
фамилия(членсемьи(_, Фамилия,,_), Фамилия).
датарождения(членсемьи(__,_, Дата), Дата).
Какие преимущества мы можем получить от использования отношений-селекторов? Определив их, мы можем теперь забыть о конкретном виде структуры представления информации. Для пополнения и обработки этой информации нужно знать только имена отношений-селекторов и в оставшейся части программы пользоваться только ими. В случае, если информация представлена сложной структурой, это легче, чем каждый раз обращаться к ней в явном виде. В частности, в нашем примере с семьей пользователь не обязан знать, что дети представлены в виде списка. Например, предположим, мы хотим сказать, что, Том Фоке и Джим Фоке принадлежат к одной семье и что Джим - второй ребенок Тома Используя приведенные выше отношения -селекторы, мы можем определить двух человек, назовем их Человек1 и Человек2 и семью. Следующий список целей приводит к желаемому результату:
имя(Человек1, том), фамилия(Человек1, фокc),
/* Человек1 - Том Фокc */
имя(Человек2, джим), фамилия(Человек1, фокc),
/* Человек2 - Джим Фокс */
муж(Семья, Человек1),
второйребенок(Семья, Человек2)
Использование отношений-селекторов облегчает также ипоследующую модификацию программ.
Представьте себе, что мы захотели повысить эффективность программы, изменив представление информации. Все, что нужно сделать для этого, — изменить определения отношений-селекторов, и вся остальная программа без изменений будет работать с этим новым представлением.
Задание.
Разработать базу данных с вложенной структурой, соответствующей заданной предметной области и глубины (таблица 6).
Таблица 6
Вариант | Предметная область | Глубина вложения |
Семья | ||
Структура предприятия | ||
Компьютеры | ||
Структура локальной сети | ||
Студенты | ||
Автомобили | ||
Корабли | ||
Самолеты | ||
Телефонный справочник | ||
Страны | ||
Структура сайтов | ||
Структура книг | ||
Футбольная команда | ||
Квартиры | ||
Структура фирмы |