В настоящей главе рассматриваются циклы, управляемые откатом, факты-переменные и внутренние базы данных. Приводятся средства, которые используются для создания, хранения и изменения состояний внутренних баз данных. Рассматриваются примеры работы с файлами.
Внутренние базы данных в языке Пролог состоят из последовательности фактов. Эти факты могут добавляться или удаляться во время исполнения программы, обрабатывается база данных в оперативной памяти компьютера. Поэтому внутреннюю базу данных называют динамической. Предикаты, которые определяются в базе данных, объявляются в разделе (class) facts.
Факты-переменные являются аналогами глобальных переменных в процедурных языках программирования. Они объявляются следующим образом:
class facts
счетчик : positive := 0.
список : string* := [].
После знака двоеточия указывается домен факта-переменной, справа от знака := начальное значение. Для изменения значений фактов-переменных используется оператор присваивания :=. Значения можно изменять следующим образом:
счетчик := счетчик + 1, список := ["Маша" | список]
С помощью факта-переменной подсчитать количество мужчин можно так:
class facts
counter : positive := 0.
clauses
run():-
мужчина(¬_),
counter := counter + 1,
fail;
write(counter),
_ = readLine().
Рассмотрим циклы, которые управляются только с помощью отката. В них будет выполняться перебор следующих фактов:
мужчина("Иван").
мужчина("Павел").
мужчина("Петр").
женщина("Мария").
женщина("Анна").
Цикл fail неоднократно использовался ранее. Он устроен следующим образом:
<действие с возвратом>, fail.
Например, рассмотрим цикл:
run():-
женщина(X),
write(X), nl
fail;
_ = readLine().
В этом цикле один за другим печатаются имена всех женщин:
Мария Анна
С помощью этой же конструкции можно реализовать вложенные циклы. Рассмотрим, например, цикл:
run():-
женщина(X),
nl,
мужчина(Y),
write(X, " - ", Y, "\t"),
fail;
_ = readLine().
В этом цикле для очередной женщины перебираются все мужчины, затем идет откат к следующей женщине. Результат выглядит следующим образом:
Мария - Иван Мария - Павел Мария - Петр Анна - Иван Анна - Павел Анна - Петр
Цикл foreach, также как и предыдущий цикл, не может использоваться без точек возврата. Он имеет вид:
foreach <действие с возвратом> do <процедура> end foreach.
Например:
run():-
foreach женщина(X) do
write(X), nl
end foreach,
_ = readLine().
Циклы fail и foreach завершаются, когда не остается точек возврата.
В приведенной ниже программе в цикле fail выводятся сведения обо всех произведениях, а в цикле foreach — только об авторах произведений, которые хранятся в четных по номеру записях.
class facts
book: (integer Номер, symbol Автор, symbol Название).
clauses
book(1, "Толстой", "Война и мир").
book(2, "Чехов", "Вишневый сад").
book(3, "Пушкин", "Евгений Онегин").
book(4, "Достоевский", "Преступление и наказание").
run():-
write("Авторы и их произведения\n\n"),
book (_, Author, Title),
write(Author, " - ", Title), nl,
fail;
nl, % вывод авторов книг из четных по номеру записей
foreach book(N, Author, _), N mod 2 = 0 do
write(Author), nl
end foreach,
_ = readLine().
Упражнение 1. Организуйте в программе "Книги" (см. листинг 4.1) такой цикл, чтобы он прекращался по достижении книги с номером 3.
В отличие от двух предыдущих видов циклов цикл repeat может использоваться и тогда, когда в повторяемом действии есть откат, и тогда, когда его нет. Он устроен следующим образом:
repeat() <действие> <условие выхода из цикла>.
Определение предиката repeat имеет вид:
class predicates
repeat: () multi.
clauses
repeat().
repeat():-
repeat().
Этот предикат ничего не делает, кроме того, что ставит точки возврата, потенциально бесконечное число раз. Условие выхода из цикла должно быть истинным только в момент самого выхода. В остальное время оно вызывает откат. Если в разделе <действие> нет точек возврата, то откат идет к предикату repeat.
Рассмотрим цикл:
печать():-
С = varM::new(0), % счетчик
repeat(),
C:value := C:value + 1, % увеличение счетчика на 1
nl, % переход на новую строку
foreach женщина(X) do % печать имен женщин
write(X, "\t")
end foreach,
C:value = 3, % условие выхода из цикла
!.
печать().
Объект класса varM хранит изменяемое значение value. В этом цикле выполняется троекратный вывод на печать имен всех женщин:
Мария Анна Мария Анна Мария Анна
В следующей программе приводится пример repeat-цикла, который используется для вывода символов из буфера ввода [2, 18]. После запуска программы нужно ввести какой-либо текст (слово) и нажать клавишу Enter. После этого текст будет выведен посимвольно, с небольшой паузой перед выводом каждого символа. Символы преобразуются в верхний регистр.
clauses % наберите слово, нажмите Enter
run():-
std::repeat(),
Char = readChar(), % считывание символа из буфера ввода
UpperCaseChar = string::charUpper(Char),
programControl::sleep(500), % 1000 = 1 сек
write(UpperCaseChar),
Char = '\n', % условие выхода из цикла
!,
clearInput(), % очищает буфер ввода
_ = readLine();
succeed().
Предикат readChar считывает символ (char) из буфера ввода. В отличие от предиката readLine он не очищает буфер ввода. Символы домена char заключаются в одинарные кавычки. Предикат charUpper переводит символ в верхний регистр. Предикат sleep прерывает исполнение программы на заданное время, которое указывается в миллисекундах (1000 миллисек = 1 сек).
Динамическая внутренняя база данных состоит из фактов, поэтому ее еще называют базой фактов. Она может загружаться из файла на диске и записываться в файл. Ниже рассматриваются предикаты, предназначенные для работы с базой данных.
Внутренняя база данных объявляется следующим образом:
class facts - rel
супруг: (string Муж, string Жена).
База данных имеет имя rel. Имена внутренних баз данных принадлежат домену factDB. База данных может не иметь имени. В имплементации одного и того же класса может быть несколько безымянных внутренних баз данных. Но если требуется загружать базу данных из файла или сохранять ее в файле, она должна иметь имя.
Предикат asserta добавляет факт в начало базы данных:
asserta(супруг(Муж, Жена)).
Предикаты assert и assertz добавляют факт в конец базы данных:
assert(супруг(Муж, Жена)).
В добавляемых фактах должны быть определены значения всех полей.
Предикат retract удаляет первый факт, который унифицируется с аргументом этого предиката. Этот предикат принимает значение ложь, если факт, который требуется удалить, отсутствует в базе данных.
Предикат retract может возвращать значения, хранящиеся в удаляемых фактах. Например, цикл
retract(супруг(Муж, _)), write(Муж), nl, fail.
удаляет все факты для предиката супруг, при этом печатаются имена мужей.
Предикат retractAll удаляет все факты, которые унифицируются с аргументом этого предиката. Этот предикат всегда успешный, значения аргументов он не возвращает:
retractAll(супруг(Муж, _)).
Предикат retractFactDb удаляет все записи из базы данных:
retractFactDb(rel).
Сохранение в файл фактов базы данных выполняет предикат save:
file::save("family.txt", rel)
Если указанного файла нет, то он будет создан.
Предикат consult загружает факты базы данных из файла, при этом они добавляются к текущему состоянию базы данных:
file::consult("family.txt", rel)
Предикат reconsult заменяет текущее состояние базы данных фактами из файла:
file::reconsult("family.txt", rel)
Следующая программа — это пример работы с внутренней базой данных, изменения в которую вносятся во время исполнения программы посредством диалога с пользователем. Для организации диалога применяется repeat-цикл. Выход из цикла производится только при выборе пользователем команды "Выход". Для обработки ошибок используется конструкция try-catch.
Программа представляет собой телефонную записную книжку. Пользователь может просмотреть текущее состояние базы данных, а также добавить или удалить сведения об именах и номерах телефонов. Особое внимание уделяется сообщениям об изменениях в базе данных.
class facts - phones
phone: (string Имя, string НомерТелефона).
class predicates
load: (string FileName).
maindialog: ().
act: (string НомерДействия) determ.
add: (string Name, string Phone).
delete: (string Name).
delete: (string Name, string Phone).
ask: (string Name [out], string Phone [out]) determ.
ask: (string Name [out]) determ.
printDb: ().
clauses
load(FileName):-
file::existFile(FileName),
!,
try file::consult(FileName, phones) % обработка ошибок
catch Error do
writef("Error %. Unable to load the database from "
"the file %\n", Error, FileName)
end try;
succeed().
maindialog():-
std::repeat(),
write("\nВыберите действие:\n",
"\t1. Просмотр базы данных\n",
"\t2. Добавить запись\n",
"\t3. Удалить запись\n",
"\t4. Выход\n"),
X = readLine(),
act(X),
!.
maindialog().
act("1"):- !,
write("\nВсе записи\n"),
printDb(),
fail.
act("2"):- !,
write("\nДобавление записи"),
ask(Name, Phone),
add(Name, Phone),
fail.
act("3"):- !,
write("\nУдаление записей. Выберите действие:\n",
"\t3.1 Удаление одной записи\n",
"\t3.2 Удаление всех записей\n"),
X = readLine(),
act(X).
act("3.1"):- !,
printDb(),
ask(Name, Phone),
delete(Name, Phone),
fail.
act("3.2"):- !,
ask(Name),
delete(Name),
fail.
act("4"):- !, write("\nВыход.\n").
act(_):-
X = readLine(),
act(X).
printDb():-
phone(Name, Phone),
writef(" %-20%\n", Name, Phone),
fail;
succeed().
ask(Name, Phone):-
ask(Name),
write("Введите номер: "),
Phone = string::trim(readLine()).
ask(Name):-
write("\nВведите имя: "),
Name = string::trim(readLine()),
Name <> "".
add(Name, Phone):-
phone(Name, Phone),
!,
writef("Запись % - % уже есть в базе данных\n",
Name, Phone);
assert(phone(Name, Phone)),
writef("Запись '% - %' добавлена\n", Name, Phone).
delete(Name):-
not(phone(Name, _)),
!,
writef("Записей для '%' нет\n", Name);
retractall(phone(Name, _)),
writef("Записи для '%' удалены\n", Name).
delete(Name, Phone):-
retract(phone(Name, Phone)),
!,
writef("Запись '% - %' удалена\n", Name, Phone);
writef("Записи '% - %' нет в базе данных\n", Name, Phone).
run():-
setConsoleTitle("Телефоны"),
FileName = "phones.txt",
load(FileName),
maindialog(),
file::save(FileName, phones),
_ = readLine().
Файл "phones.txt" создается в директории Exe проекта. Предикат setConsoleTitle устанавливает заголовок окна консоли, предикат writef выполняет форматированный вывод. Предикат trim удаляет пробельные символы в начале и в конце строки. Предикат existFile проверяет, существует ли указанный файл.
Упражнение 2. Добавьте в программу про телефоны (листинг 4.3) поиск номеров телефонов по имени и поиск имени по номеру телефона.
Рассмотрим пример работы с файлами в языке Visual Prolog. В приведенной ниже программе на вход подается имя текстового файла. Определяется способ его кодировки, Unicode или нет, а затем создаются два ANSI-файла. В один из них записываются четные по номеру строки из исходного файла, а в другой — нечетные.
class facts
n : positive := 0.
class predicates
type: (string FileName, stream::mode Mode) procedure (i,o).
rewrite: (string, stream::mode, string File1, string File2)
determ (i,o,i,i).
clauses
type(FileName, stream::unicode()):-
file::isUnicode(FileName),
!.
type(_, stream::ansi(ansi())).
rewrite(FileName, Mode, FileName1, FileName2):-
file::existFile(FileName),
type(FileName, Mode),
Input = inputStream_file::openFile(FileName, Mode),
F1 = outputStream_file::create8(FileName1),
F2 = outputStream_file::create8(FileName2),
std::repeat(),
Str = Input:readLine(),
n := 1 - n,
if n = 1 then
F1:write(Str), F1:nl
else
F2:write(Str), F2:nl
end if,
Input:endOfStream(),
!,
Input:close(),
F1:close(),
F2:close().
run():-
FileName = "mytest.txt", File1 = "test1.txt", File2 = "test2.txt",
rewrite(FileName, Mode, File1, File2),
writef("FileName %\nMode: %\nЧисло строк = %\n",
FileName, Mode, n),
writef("Нечетные строки в файле %, а четные в файле %\n",
File1, File2),
write(file::readString(FileName, _)), % счит. файл в string
fail;
_ = readLine().
Предикат isUnicode проверяет, используется ли для файла кодировка символов Юникод. Конструктор openFile открывает файл для чтения, предикат close закрывает файл. Предикат create8 создает ANSI-файл для записи. Предикат endOfStream проверяет, достигнут ли конец файла. Предикат readString считывает файл в строку.
В системе Visual Prolog имеются средства, которые позволяют использовать в консольных приложениях цвет, события мыши, события нажатия клавиши и др. Программа, приведенная ниже, создает в окне консоли цветную полосу, состоящую из "квадратов" (шириной в два пробельных символа), цвет которых генерируется случайным образом в процессе repeat-цикла. Задействовано 15 цветов, черный цвет не используется. Перед появлением каждого "квадрата" делается небольшая пауза. Цикл прекращается после щелчка мыши, когда курсор находится в произвольном месте окна консоли (рис. 4.1).
(рис 4.1) Разноцветная полоса; цвета генерируются сучайным образом
В комментарии указано, как остановить цикл посредством нажатия определенной клавиши.
open core, console, console_native
class predicates
getAttribute: (unsigned16 ЦветФона, unsigned16 ЦветТекста)
-> unsigned16.
clauses
getAttribute(BgColor, TextColor) =
bit::bitOr(bit::bitLeft(BgColor, 4), TextColor).
class predicates
stop: () determ.
process: ().
clauses
stop():-
L = getEventQueue(),
% key(_, _, _, _, 'k', _) = list::getMember_nd(L),
mouse( _, _, 1, _, 0) = list::getMember_nd(L),
!.
process():-
std::repeat(),
N = 1 + math::random(15),
setTextAttribute(bit::bitLeft(N, 4)), % или (N * 16)
write(" "), % два пробельных символа
programControl::sleep(100),
stop(),
!.
process().
run():-
setLocation(coord(20, 10)),
setTextAttribute(getAttribute(3, 15)),
write("Движение прекратится по щелчку мыши"),
programControl::sleep(3000), % 3 сек
clearOutput(), % очищает буфер вывода
setLocation(coord(0, 1)),
process(),
_ = readLine().
Предикат getEventQueue возвращает для окна консоли список текущих событий. Предикат getMember_nd недетерминированно возвращает произвольный элемент списка. Предикат setTextAttribute устанавливает в консоли атрибуты текста. Предикат setLocation помещает курсор в точку с заданными координатами. Началом координат в окне консоли является его левый верхний угол, ось абсцисс направлена вправо, а ось ординат — вниз. Единицей измерения по оси абсцисс является ширина одного символа (все они имеют одинаковую ширину), а по оси ординат — высота строки. Предикат random генерирует целое неотрицательное случайное число. Если его аргумент равен R, то генерируется число в пределах от 0 до R – 1 включительно.
Создайте базу данных "Мое расписание", в которой хранятся факты для описания отношения расписание вида:
расписание(время(пт, 1),
предмет("Интеллектуальные системы", лекция),
преп("Аншаков О.М.", профессор), место(416, 2)).
Факты должны содержать следующие сведения: день недели и номер пары; предмет — название и вид занятия (лекционное и т. д.); фамилия и инициалы преподавателя, его должность; аудитория и корпус.
Найдите ответы на следующие вопросы.
n вопросов. Приводятся варианты ответов, среди которых один правильный. Тестируемый должен указать номер правильного ответа. За каждый правильный ответ ему начисляется m баллов. Результатом теста является сумма полученных баллов.В настоящей главе рассматриваются циклы, управляемые откатом, факты-переменные и внутренние базы данных. Приводятся средства, которые используются для создания, хранения и изменения состояний внутренних баз данных. Рассматриваются примеры работы с файлами.
Внутренние базы данных в языке Пролог состоят из последовательности фактов. Эти факты могут добавляться или удаляться во время исполнения программы, обрабатывается база данных в оперативной памяти компьютера. Поэтому внутреннюю базу данных называют динамической. Предикаты, которые определяются в базе данных, объявляются в разделе (class) facts.
Факты-переменные являются аналогами глобальных переменных в процедурных языках программирования. Они объявляются следующим образом:
class facts
счетчик : positive := 0.
список : string* := [].
После знака двоеточия указывается домен факта-переменной, справа от знака := начальное значение. Для изменения значений фактов-переменных используется оператор присваивания :=. Значения можно изменять следующим образом:
счетчик := счетчик + 1, список := ["Маша" | список]
С помощью факта-переменной подсчитать количество мужчин можно так:
class facts
counter : positive := 0.
clauses
run():-
мужчина(¬_),
counter := counter + 1,
fail;
write(counter),
_ = readLine().
Рассмотрим циклы, которые управляются только с помощью отката. В них будет выполняться перебор следующих фактов:
мужчина("Иван").
мужчина("Павел").
мужчина("Петр").
женщина("Мария").
женщина("Анна").
Цикл fail неоднократно использовался ранее. Он устроен следующим образом:
<действие с возвратом>, fail.
Например, рассмотрим цикл:
run():-
женщина(X),
write(X), nl
fail;
_ = readLine().
В этом цикле один за другим печатаются имена всех женщин:
Мария Анна
С помощью этой же конструкции можно реализовать вложенные циклы. Рассмотрим, например, цикл:
run():-
женщина(X),
nl,
мужчина(Y),
write(X, " - ", Y, "\t"),
fail;
_ = readLine().
В этом цикле для очередной женщины перебираются все мужчины, затем идет откат к следующей женщине. Результат выглядит следующим образом:
Мария - Иван Мария - Павел Мария - Петр Анна - Иван Анна - Павел Анна - Петр
Цикл foreach, также как и предыдущий цикл, не может использоваться без точек возврата. Он имеет вид:
foreach <действие с возвратом> do <процедура> end foreach.
Например:
run():-
foreach женщина(X) do
write(X), nl
end foreach,
_ = readLine().
Циклы fail и foreach завершаются, когда не остается точек возврата.
В приведенной ниже программе в цикле fail выводятся сведения обо всех произведениях, а в цикле foreach — только об авторах произведений, которые хранятся в четных по номеру записях.
class facts
book: (integer Номер, symbol Автор, symbol Название).
clauses
book(1, "Толстой", "Война и мир").
book(2, "Чехов", "Вишневый сад").
book(3, "Пушкин", "Евгений Онегин").
book(4, "Достоевский", "Преступление и наказание").
run():-
write("Авторы и их произведения\n\n"),
book (_, Author, Title),
write(Author, " - ", Title), nl,
fail;
nl, % вывод авторов книг из четных по номеру записей
foreach book(N, Author, _), N mod 2 = 0 do
write(Author), nl
end foreach,
_ = readLine().
Упражнение 1. Организуйте в программе "Книги" (см. листинг 4.1) такой цикл, чтобы он прекращался по достижении книги с номером 3.
В отличие от двух предыдущих видов циклов цикл repeat может использоваться и тогда, когда в повторяемом действии есть откат, и тогда, когда его нет. Он устроен следующим образом:
repeat() <действие> <условие выхода из цикла>.
Определение предиката repeat имеет вид:
class predicates
repeat: () multi.
clauses
repeat().
repeat():-
repeat().
Этот предикат ничего не делает, кроме того, что ставит точки возврата, потенциально бесконечное число раз. Условие выхода из цикла должно быть истинным только в момент самого выхода. В остальное время оно вызывает откат. Если в разделе <действие> нет точек возврата, то откат идет к предикату repeat.
Рассмотрим цикл:
печать():-
С = varM::new(0), % счетчик
repeat(),
C:value := C:value + 1, % увеличение счетчика на 1
nl, % переход на новую строку
foreach женщина(X) do % печать имен женщин
write(X, "\t")
end foreach,
C:value = 3, % условие выхода из цикла
!.
печать().
Объект класса varM хранит изменяемое значение value. В этом цикле выполняется троекратный вывод на печать имен всех женщин:
Мария Анна Мария Анна Мария Анна
В следующей программе приводится пример repeat-цикла, который используется для вывода символов из буфера ввода [2, 18]. После запуска программы нужно ввести какой-либо текст (слово) и нажать клавишу Enter. После этого текст будет выведен посимвольно, с небольшой паузой перед выводом каждого символа. Символы преобразуются в верхний регистр.
clauses % наберите слово, нажмите Enter
run():-
std::repeat(),
Char = readChar(), % считывание символа из буфера ввода
UpperCaseChar = string::charUpper(Char),
programControl::sleep(500), % 1000 = 1 сек
write(UpperCaseChar),
Char = '\n', % условие выхода из цикла
!,
clearInput(), % очищает буфер ввода
_ = readLine();
succeed().
Предикат readChar считывает символ (char) из буфера ввода. В отличие от предиката readLine он не очищает буфер ввода. Символы домена char заключаются в одинарные кавычки. Предикат charUpper переводит символ в верхний регистр. Предикат sleep прерывает исполнение программы на заданное время, которое указывается в миллисекундах (1000 миллисек = 1 сек).
Динамическая внутренняя база данных состоит из фактов, поэтому ее еще называют базой фактов. Она может загружаться из файла на диске и записываться в файл. Ниже рассматриваются предикаты, предназначенные для работы с базой данных.
Внутренняя база данных объявляется следующим образом:
class facts - rel
супруг: (string Муж, string Жена).
База данных имеет имя rel. Имена внутренних баз данных принадлежат домену factDB. База данных может не иметь имени. В имплементации одного и того же класса может быть несколько безымянных внутренних баз данных. Но если требуется загружать базу данных из файла или сохранять ее в файле, она должна иметь имя.
Предикат asserta добавляет факт в начало базы данных:
asserta(супруг(Муж, Жена)).
Предикаты assert и assertz добавляют факт в конец базы данных:
assert(супруг(Муж, Жена)).
В добавляемых фактах должны быть определены значения всех полей.
Предикат retract удаляет первый факт, который унифицируется с аргументом этого предиката. Этот предикат принимает значение ложь, если факт, который требуется удалить, отсутствует в базе данных.
Предикат retract может возвращать значения, хранящиеся в удаляемых фактах. Например, цикл
retract(супруг(Муж, _)), write(Муж), nl, fail.
удаляет все факты для предиката супруг, при этом печатаются имена мужей.
Предикат retractAll удаляет все факты, которые унифицируются с аргументом этого предиката. Этот предикат всегда успешный, значения аргументов он не возвращает:
retractAll(супруг(Муж, _)).
Предикат retractFactDb удаляет все записи из базы данных:
retractFactDb(rel).
Сохранение в файл фактов базы данных выполняет предикат save:
file::save("family.txt", rel)
Если указанного файла нет, то он будет создан.
Предикат consult загружает факты базы данных из файла, при этом они добавляются к текущему состоянию базы данных:
file::consult("family.txt", rel)
Предикат reconsult заменяет текущее состояние базы данных фактами из файла:
file::reconsult("family.txt", rel)
Следующая программа — это пример работы с внутренней базой данных, изменения в которую вносятся во время исполнения программы посредством диалога с пользователем. Для организации диалога применяется repeat-цикл. Выход из цикла производится только при выборе пользователем команды "Выход". Для обработки ошибок используется конструкция try-catch.
Программа представляет собой телефонную записную книжку. Пользователь может просмотреть текущее состояние базы данных, а также добавить или удалить сведения об именах и номерах телефонов. Особое внимание уделяется сообщениям об изменениях в базе данных.
class facts - phones
phone: (string Имя, string НомерТелефона).
class predicates
load: (string FileName).
maindialog: ().
act: (string НомерДействия) determ.
add: (string Name, string Phone).
delete: (string Name).
delete: (string Name, string Phone).
ask: (string Name [out], string Phone [out]) determ.
ask: (string Name [out]) determ.
printDb: ().
clauses
load(FileName):-
file::existFile(FileName),
!,
try file::consult(FileName, phones) % обработка ошибок
catch Error do
writef("Error %. Unable to load the database from "
"the file %\n", Error, FileName)
end try;
succeed().
maindialog():-
std::repeat(),
write("\nВыберите действие:\n",
"\t1. Просмотр базы данных\n",
"\t2. Добавить запись\n",
"\t3. Удалить запись\n",
"\t4. Выход\n"),
X = readLine(),
act(X),
!.
maindialog().
act("1"):- !,
write("\nВсе записи\n"),
printDb(),
fail.
act("2"):- !,
write("\nДобавление записи"),
ask(Name, Phone),
add(Name, Phone),
fail.
act("3"):- !,
write("\nУдаление записей. Выберите действие:\n",
"\t3.1 Удаление одной записи\n",
"\t3.2 Удаление всех записей\n"),
X = readLine(),
act(X).
act("3.1"):- !,
printDb(),
ask(Name, Phone),
delete(Name, Phone),
fail.
act("3.2"):- !,
ask(Name),
delete(Name),
fail.
act("4"):- !, write("\nВыход.\n").
act(_):-
X = readLine(),
act(X).
printDb():-
phone(Name, Phone),
writef(" %-20%\n", Name, Phone),
fail;
succeed().
ask(Name, Phone):-
ask(Name),
write("Введите номер: "),
Phone = string::trim(readLine()).
ask(Name):-
write("\nВведите имя: "),
Name = string::trim(readLine()),
Name <> "".
add(Name, Phone):-
phone(Name, Phone),
!,
writef("Запись % - % уже есть в базе данных\n",
Name, Phone);
assert(phone(Name, Phone)),
writef("Запись '% - %' добавлена\n", Name, Phone).
delete(Name):-
not(phone(Name, _)),
!,
writef("Записей для '%' нет\n", Name);
retractall(phone(Name, _)),
writef("Записи для '%' удалены\n", Name).
delete(Name, Phone):-
retract(phone(Name, Phone)),
!,
writef("Запись '% - %' удалена\n", Name, Phone);
writef("Записи '% - %' нет в базе данных\n", Name, Phone).
run():-
setConsoleTitle("Телефоны"),
FileName = "phones.txt",
load(FileName),
maindialog(),
file::save(FileName, phones),
_ = readLine().
Файл "phones.txt" создается в директории Exe проекта. Предикат setConsoleTitle устанавливает заголовок окна консоли, предикат writef выполняет форматированный вывод. Предикат trim удаляет пробельные символы в начале и в конце строки. Предикат existFile проверяет, существует ли указанный файл.
Упражнение 2. Добавьте в программу про телефоны (листинг 4.3) поиск номеров телефонов по имени и поиск имени по номеру телефона.
Рассмотрим пример работы с файлами в языке Visual Prolog. В приведенной ниже программе на вход подается имя текстового файла. Определяется способ его кодировки, Unicode или нет, а затем создаются два ANSI-файла. В один из них записываются четные по номеру строки из исходного файла, а в другой — нечетные.
class facts
n : positive := 0.
class predicates
type: (string FileName, stream::mode Mode) procedure (i,o).
rewrite: (string, stream::mode, string File1, string File2)
determ (i,o,i,i).
clauses
type(FileName, stream::unicode()):-
file::isUnicode(FileName),
!.
type(_, stream::ansi(ansi())).
rewrite(FileName, Mode, FileName1, FileName2):-
file::existFile(FileName),
type(FileName, Mode),
Input = inputStream_file::openFile(FileName, Mode),
F1 = outputStream_file::create8(FileName1),
F2 = outputStream_file::create8(FileName2),
std::repeat(),
Str = Input:readLine(),
n := 1 - n,
if n = 1 then
F1:write(Str), F1:nl
else
F2:write(Str), F2:nl
end if,
Input:endOfStream(),
!,
Input:close(),
F1:close(),
F2:close().
run():-
FileName = "mytest.txt", File1 = "test1.txt", File2 = "test2.txt",
rewrite(FileName, Mode, File1, File2),
writef("FileName %\nMode: %\nЧисло строк = %\n",
FileName, Mode, n),
writef("Нечетные строки в файле %, а четные в файле %\n",
File1, File2),
write(file::readString(FileName, _)), % счит. файл в string
fail;
_ = readLine().
Предикат isUnicode проверяет, используется ли для файла кодировка символов Юникод. Конструктор openFile открывает файл для чтения, предикат close закрывает файл. Предикат create8 создает ANSI-файл для записи. Предикат endOfStream проверяет, достигнут ли конец файла. Предикат readString считывает файл в строку.
В системе Visual Prolog имеются средства, которые позволяют использовать в консольных приложениях цвет, события мыши, события нажатия клавиши и др. Программа, приведенная ниже, создает в окне консоли цветную полосу, состоящую из "квадратов" (шириной в два пробельных символа), цвет которых генерируется случайным образом в процессе repeat-цикла. Задействовано 15 цветов, черный цвет не используется. Перед появлением каждого "квадрата" делается небольшая пауза. Цикл прекращается после щелчка мыши, когда курсор находится в произвольном месте окна консоли (рис. 4.1).
(рис 4.1) Разноцветная полоса; цвета генерируются сучайным образом
В комментарии указано, как остановить цикл посредством нажатия определенной клавиши.
open core, console, console_native
class predicates
getAttribute: (unsigned16 ЦветФона, unsigned16 ЦветТекста)
-> unsigned16.
clauses
getAttribute(BgColor, TextColor) =
bit::bitOr(bit::bitLeft(BgColor, 4), TextColor).
class predicates
stop: () determ.
process: ().
clauses
stop():-
L = getEventQueue(),
% key(_, _, _, _, 'k', _) = list::getMember_nd(L),
mouse( _, _, 1, _, 0) = list::getMember_nd(L),
!.
process():-
std::repeat(),
N = 1 + math::random(15),
setTextAttribute(bit::bitLeft(N, 4)), % или (N * 16)
write(" "), % два пробельных символа
programControl::sleep(100),
stop(),
!.
process().
run():-
setLocation(coord(20, 10)),
setTextAttribute(getAttribute(3, 15)),
write("Движение прекратится по щелчку мыши"),
programControl::sleep(3000), % 3 сек
clearOutput(), % очищает буфер вывода
setLocation(coord(0, 1)),
process(),
_ = readLine().
Предикат getEventQueue возвращает для окна консоли список текущих событий. Предикат getMember_nd недетерминированно возвращает произвольный элемент списка. Предикат setTextAttribute устанавливает в консоли атрибуты текста. Предикат setLocation помещает курсор в точку с заданными координатами. Началом координат в окне консоли является его левый верхний угол, ось абсцисс направлена вправо, а ось ординат — вниз. Единицей измерения по оси абсцисс является ширина одного символа (все они имеют одинаковую ширину), а по оси ординат — высота строки. Предикат random генерирует целое неотрицательное случайное число. Если его аргумент равен R, то генерируется число в пределах от 0 до R – 1 включительно.
Создайте базу данных "Мое расписание", в которой хранятся факты для описания отношения расписание вида:
расписание(время(пт, 1),
предмет("Интеллектуальные системы", лекция),
преп("Аншаков О.М.", профессор), место(416, 2)).
Факты должны содержать следующие сведения: день недели и номер пары; предмет — название и вид занятия (лекционное и т. д.); фамилия и инициалы преподавателя, его должность; аудитория и корпус.
Найдите ответы на следующие вопросы.
n вопросов. Приводятся варианты ответов, среди которых один правильный. Тестируемый должен указать номер правильного ответа. За каждый правильный ответ ему начисляется m баллов. Результатом теста является сумма полученных баллов.Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.