Программирование в среде Visual Prolog. Учебное пособие
.pdfцель read считается выполненной или невыполненной в зависимости от результата унификации.
Основнойпредикатвывода– write (X). Поискрешениядляэтойцели приводит к записи терма Х в текущий выходной поток, определяемый операционной системой. Обычно это данные, выводимые на терминал. Ни read, ни write не дают альтернативных решений при возврате.
Обычно в предикате read в качестве аргумента используется переменная, которая приобретает значение первого терма в текущем входном потоке. Сопоставление переменной Х с каким-либо значением, определенным вне программы, не входит в логическую модель, так как каждое новое обращение к процедуре read(X) будет выполняться (возможно) с различными значениями X.
Предикат read проводит синтаксический анализ следующего терма
впотоке ввода. Если найдены синтаксические ошибки, то на терминале печатается сообщение об ошибке и предпринимается попытка прочесть следующий терм.
Внелогическая природа предикатов read и write различна. Если
впрограмме заменить все обращения к предикату write на обращения к всегда выполняемому предикату true, то семантика программы не изменится. Для предиката read это не так.
Различные версии Пролога содержат различные системозависимые вспомогательные предикаты.
Полезным вспомогательным предикатом является предикат writeln(Xs), аналогичный соответствующей команде языка Паскаль. Поиск решения для цели writeln(Xs) приводит к печати списка термов Xs в виде выходной строки. Предикат описан в примере 6.1. В ней использован системный предикат пl, вызывающий переход к выводу с новой строки.
Пример 6.1. Вывод списка термов.
Writeln([X | Xs]) :-write(X), writeln(Xs). Writeln([ ]) :- nl.
Строка литер заключается в апострофы. Например, решение цели writeln ([ 'Значение Х равно', X]) приводит к печати текста
Значение Х равно 3
если переменная Х сопоставлена с числом 3.
31
ВПрологе можно выполнять и более общие операции со строками литер, обрабатывая строки как списки кодов литер. Если, например, используются коды ASCII, то список [80,114,111,108,111,103] соответствуетстроке«Prolog». КодASCII для литерыРравен80 длялитерыr – 114 и т. д. Строка литер, заключенная в кавычки, является другим способом обозначения списка, состоящего из кодов ASCII. В нашем примере список можно задать с помощью строки «Prolog». Такие строки являются более удобной синтаксической записью подобных списков. Обработка строк может производиться с помощью стандартных методов работы со списками.
Системный предикат name(X, Y) используется для преобразования названия константы в строку литер и обратно. Цель name(X,Y) выполнена, если X – атом, а Y – список кодов ASCII, соответствующих литерам атома X, например, цель name (log,[108, 111, 103])? выполнена.
Ввод-вывод может выполняться не только на уровне термов с помощьюпредикатовread иwrite, ноинаболеенизкомуровне– науровне литер. Основной предикат вывода на уровне литер имеет вид put(N), он помещает литеру, соответствующую коду ASCII для N, в текущий выходной поток. Основной предикат ввода get (X), сопоставляющий аргумент Х с кодом ASCII первой литеры во входном потоке.
Впримере 6.2 задается вспомогательный предикат read_wordlist (Words), читающий список слов Words. В построении программы использован предикат gel. Слова могут быть разделены произвольным числом пробелов (код ASCII для пробела – 32) и могут состоять из любого числа строчных и прописных букв и символов подчеркивания. Последовательность слов завершается точкой.
Пример 6.2. Чтение списка слов read_word_list(Ws):-
get(C), read_word_list(C, Ws).
read_word_list(C,[W |Ws]):- word_char(C), read_word(C,W,C1), read_word_list(C1,Ws). read_word_list(C,Ws) :- fill_char(C),
get(Cl),
32
read_word_list(C1,Ws). read_word_list(C,[ ]) :- end_of_words_char(C).
read_word(C,W,C1) :-
word_chars (C, Cs, С1), name(W.Cs). word_chars(C, [С | Cs],C0):- word_char(C),
!,
get(C1),
word chars (C1,Cs, C0). word_chars(C,[ ],C):- not word_char(C).
word_char(C) 97 C,C 122. % строчные буквы
word char(C) 65 C, С 90. % прописные буквы word_char(95). % подчеркивание
fillchar(32). % пробел
end of words_char(46). % точка
Предикат read_word_list читает литеру С и обращается к процедуре read_word_list(C,Words). В этой процедуре выполняется одно из трех действий в зависимости от значения литеры С. Если С – литера, входящая в слово (прописная или строчная буква либо символ подчеркивания), то обрабатывается следующее слово и далее рекурсивно обрабатывается последовательность оставшихся слов. Второе действие состоит в игнорировании пробелов, т. е. считывается следующая литера,
иработа программы рекурсивно продолжается. Если наконец встретилась литера, обозначающая конец последовательности слов, то программа завершает работу и возвращает список слов.
Существенно, что всегда программа сначала читает литеру, а затем уже проверяет, что следует делать. Если это полезная литера, например литера слова, то она должна быть включена в слово. Иначе литеры могут быть потеряны при возврате. Рассмотрим следующий цикл ввода
иобработки:
process([ ]):-get(C), end_of_words_char(C).
process ([W | Words]):-get(C), word _char(C), get_word(C, W), process (Words).
Если первая литера слова не удовлетворяет предикату end_of_words_char, то первое предложение не выполнится, а второе предложение приведет к вводу следующей литеры.
33
Возвращаясь к примеру 6.2, заметим, что предикат read_words (C,W,C1) читает слово W, начинающееся с текущей литеры С, и возвращает литеру, следующую за словом – С1. Список литер, образующих слово, находитсяспомощьюпроцедурыword_chars/3 (аргументытеже, что и у процедуры read_words). Слово строится по списку литер с помощью системного предиката name. Процедура words_chars также обладает свойством опережающего просмотра одной литеры, поэтому литеры не теряются.
Такие предикаты, как fill_char/1 и word_char/1, упрощают представление данных в Прологе.
6.2. РАБОТА С ФАЙЛАМИ В ПРОЛОГЕ
Обычно файлом называют именованную (т. е. имеющую имя) совокупность данных, записанных на диске. Файл состоит из компонентов (элементов). При чтении или записи файловая переменная перемещается к очередному компоненту и делает его доступным для обработки. Рассмотрим, как можно работать с файлами в Прологе.
Чтобы работать с файлами вVisual Prolog, нужно сначала объявить тип файла в разделе domains, например:
file = students_file; teachers_file
В данном случае нами объявлены два идентификатора:
1)students_file
–идентификатор файла, содержащего записи о студентах;
2)teachers_file
–идентификатор файла, содержащего записи о преподавателях. Типы файлов могут использоваться, например, для задания в про-
грамме различных символических имен бинарным и текстовым файлам или файлам, отличающимся содержимым (в нашем случае – файл с данными студентов и файл с данными преподавателей). Это необходимо для того, чтобы в программе можно было одновременно открывать несколько файлов, обращаяськнимприэтомчерезидентификаторы(символические имена).
Для того чтобы начать работать с файлом, его необходимо открыть с помощью одной из функций открытия файла. При открытии физический файл на диске связывается с одним из идентификаторов (символических имен), объявленных в разделе domains.
34
Существуют следующие функции открытия файла:
1)openread(Id, Path) – открывает файл для чтения. Если файл невозможно открыть, система даст ошибку:
PROGRAM ERROR. Module:C:\WINDOWS\TEMP\GOAL$000.PRO Pos:445
Message:7002 File not found
2)openwrite(Id, Path) – создает и открывает файл для записи. Если файл уже открыт другим процессом для записи (следовательно, заблокирован операционной системой), система даст ошибку. Если файл уже существует, он будет очищен;
3)openappend(Id, Path) – создает и открывает файл для добавления записей в конец файла. Предикат выполняет те же действия, что и предикат openwrite, но если файл уже существует, не удаляет его содержимое, а устанавливает каретку записи на конец файла;
4)openmodify(Id, Path) – создает и открывает файл одновременно для чтения и записи. После открытия каретка для чтения и записи установлена на начало файла (при выполнении записи старое содержимое файла будет удалено). Если файл не существует, он будет создан.
Чтобыизбежатьошибкиприотсутствиифайлавпредикатеopenread, нужнопроверитьналичиесоответствующегофайланадискеспомощью предиката existfile(Path).
После открытия файла для того, чтобы записывать в него или считывать из него данные, нужно задать этот файл в качестве текущего устройства ввода-вывода. Сделать это можно с помощью предикатов
writedevice(Id) readdevice(Id).
После их вызова все запросы на ввод-вывод (например, вызовы предикатов readInt, write, nl) будут переадресованы соответствующему файлу. Если после работы с файлом потребуется вывести сообщения на экран или запросить ввод с клавиатуры, нужно переключить этими же функциями ввод-вывод на соответствующие устройства stdin, stdout, stderr.
После того как работа с файлом завершена, его нужно закрывать функцией
closefile(Id).
35
Пример 6.3
Программа, считывающая список целых чисел из файла. domains
file = text_input_file list_integer = integer* predicates
file_numbers_to_list(file, list_integer, list_integer) clauses
file_numbers_to_list(File, Buffer, Buffer):- eof(File), !.
file_numbers_to_list(File, Buffer, List):- readint(Number), !,
file_numbers_to_list(File, [Number|Buffer], List). file_numbers_to_list(_File, _Buffer, _List):- write("bad file"), nl, fail.
goal
FileName = "/home/rrrfer/input.txt", openread(text_input_file, FileName), !, readdevice(text_input_file), file_numbers_to_list(text_input_file, [], List), closefile(text_input_file).
Предикат file_numbers_to_list использует метод накопляющего параметра – результат накапливается во втором аргументе (буфере). Считывание продолжается до тех пор, пока в файле содержатся данные. Когда будет достигнут конец файла, предикат eof(File) завершится успешно, предикат file_numbers_to_list вернет накопленный результат. Если файл содержит что-то кроме целых чисел, предикат readint вернет сообщение об ошибке, оно будет выведено на экран.
Важно, что предикаты ввода в Visual Prolog считывают данные с клавиатуры или текстового файла до символа перевода строки или концафайла, поэтому есличиславфайлезаписанычерезпробел, работа предиката ввода завершится неудачей. Исходный файл для этой программыдолженсодержатьпоодномучислувкаждойстроке. Есличисла в файле содержатся в ином порядке или вам требуется обработать отдельные слова файла, можно считать строки целиком с помощью функ-
ции readln.
36
6.3.ПРЕДИКАТЫ РАБОТЫ СО СТРОКАМИ
Вотличие от предикатов ввода-вывода предикаты обработки строк полностью соответствуют логической модели Пролога. Каждый из предикатов может решать как прямые, так и обратные задачи.
Ниже перечисляются основные предикаты обработки строк.
1) frontchar (Строка,Символ,ОстальнаяСтрока) (string,char, string) -
Предикат позволяет сформировать строку (ОстальнаяСтрока), которая содержит символы исходной строки (Строка) начиная с символа (Символ). Посколькупараметрымогутбытькаквходными, такивыходными, этот предикат может использоваться довольно широко для работы со строками.
Пример 6.4
Некоторые возможные варианты использования этого предиката. Goal: frontchar("VPROLOG",CH,REST) % выделение пеpвого сим-
вола и подстpоки за ним
CH=V, REST=PROLOG 1 Solution
__________________________________________________________
Goal: frontchar("VPROLOG",'V',REST) % выделение подстpоки по-
сле буквы T REST=PROLOG 1 Solution
__________________________________________________________
Goal: frontchar(X,'V',"PROLOG") % фоpмиpование стpоки X=VPROLOG
1 Solution
2) fronttoken (Строка,Лексема,ОстальнаяСтрока) (string,string, string) -
Предикатпозволяетгибкоработатьслексемами, вкачествекоторых могут быть имена (в соответствии с синтаксисом Пролога) или числа (целые или вещественные), или специальные символы.
37
Пример 6.5
Работа предиката fronttoken.
Goal: fronttoken("all boys do fine",TOK,REST) TOK=all, REST= boys do fine
1 Solution
_________________________________________
Goal: fronttoken("22all boys do fine",TOK,REST) TOK=22, REST=all boys do fine
1 Solution_________________________________________
Goal: fronttoken("-22.66all boys do fine",TOK,REST) TOK=-, REST=22.66all boys do fine
1 Solution
3)frontstr (Длина,ВходнаяСтрока,НачальнаяСтрока, ОстальнаяСтрока)
(integer, string, string, string)
Предикат позволяет формировать из (ВходнаяСтрока) строки (НачальнаяСтрока) заданной длины и (ОстальнаяСтрока).
Пример 6.6
Работа предиката frontstr.
Goal: frontstr(3,"All boys do fine",STR1,STR2) STR1=All, STR2= boys do fine
1 Solution
_________________________________________
Goal: frontstr(0,"All boys do fine",STR1,STR2) STR1=, STR2=All boys do fine
1 Solution
__________________________________________
Goal: frontstr(20,"All boys do fine",STR1,STR2) No Solution
4) concat (Строка1,Строка2,Строка3) (string, string,string)
Предикат реализует сцепление строк, т. е. операцию Строка3 = Строка1 + Строка2, а также pазличные функции, связанные со сцеплением.
38
Пример 6.7
Работа предиката concat.
Goal: concat("aaa","bbb",X) X=aaabbb
1 Solution
__________________________________
Goal: concat("aaa","bbb","aaabbb") True
___________________________________
Goal: concat(X,"bbb","aaa----bbb") X=aaa----
1 Solution
___________________________________
Goal: concat("aaa",X,"aaa----bbb") X=----bbb
1 Solution
5) str_len (Строка,Длина) (string,integer)
В зависимости от типа параметров предикат выполняет разные функции, связанные с длиной строки.
Пример 6.8
Работа предиката str_len.
Goal: str_len("abc",Len) % какова длина стpоки? Len=3
1 Solution
________________________________________
Goal: str_len("abc",3) % длина стpоки pавна 3? True
6) isname (ПараметрСтрока) (string)
Предикат осуществляет проверку параметра-строки на соответствие синтаксису Пролога.
39
Пример 6.9
Работа предиката isname. Goal: isname("Dan") True
Goal: isname("2Leo") False
Практическая работа
Встроенные предикаты Пролога. Предикаты ввода-вывода, пре-
дикаты обработки строк
Содержание работы
1.Прочитать указания к работе.
2.Выполнить индивидуальное задание.
3.Оформить отчет.
4.Защитить работу.
Индивидуальные задания
1.Дан текстовый файл, разделенный на строки. Выравнивание строки заключается в том, что между ее отдельными словами дополнительно вносятся пробелы так, чтобы длина строки стала равной заданной длине (требуемая длина не меньше исходной), а последнее слово строки сдвинулось к ее правому краю. Написать программу выравнивания строк данного текста.
2.Дан символьный файл F. Записать в файл G с сохранением порядка следования те символы файла F:
а) которым в этом файле предшествует буква а; б) вслед за которыми в этом файле идет буква а.
3.Дан символьный файл F. Группы символов, разделенные пробелами (одним или несколькими) и не содержащие пробелов внутри себя, будем называть словами. Удалить из файла все однобуквенные слова, лишние пробелы. Результат записать в файл G.
4.Дан символьный файл F. Найти самое длинное слово среди слов, вторая буква которых есть е; если таких слов несколько, то найти
40
