Как и в любом другом языке программирования, одним из наиболее сложных аспектов является ввод вывод данных. Обработка ввода вывода в Пролог не более сложна чем допустим в Hugs. Мы рассмотрим достаточно много, чтобы уметь читать символы из файла. Если вы заинтересованы, то можете почитать о других средствах работы с вводом-выводом из SWI-Prolog мануала.
5.1. Ввод
Для того, чтобы файловый ввод выполнялся в контексте логического программирования, давайте начнем с высокого уровня, и увидим, сможем ли мы выяснить, что для этого нужно. Для начала, мы считаем удобным использовать для доступа к символам список Пролог. Это означает, что нам нужен предикат, который будет принимать имя файла (как входной параметр) и затем унифицировать второй параметр со списком символов в файле.
file(X,Y) :- ..... %% X – это имя файла
%% Y – список символов из файла
К примеру, у нас есть файл test.data. Мы ожидаем от запроса
?- file(’test.data’, D).
что он приведет к тому, что в D унифицируется со списком символов из файла, и эти символы будут напечатаны.
Конечно, перед тем как мы сможем выполнить запрос, мы должны закончить определение предиката file. Как это сделать? Интуитивно, file (X, Y) должен быть истинен, если мы можем открыть файл X, и в Y хранится результат чтения данных из файла. Давайте запишем это
file(X, Y) :- open(X, In), %% привяжем внутреннее имя ’In’ к X
readAll(In, Y).
Выглядит нормально – open – это встроенный предикат, который откроет нужный файл и унифицирует In с файловым дескриптором. Идея в том, что открыв файл, мы сможем использовать этот дескриптор при чтении данных.
Что насчет readall? Мы хотим, чтобы этот предикат унифицировал Y со списком символов из файла, на который ссылаются как на In. Определение open скоро будет, но давайте сначала посмотрим на readall. Идея в том, чтобы взять символы из одного списка (из файла) и поместить их в другой список (чтобы унифицироваться с Y) только если все символы в порядке. Это рекурсивный процесс и мы можем увидеть, как он ложиться в следующий шаблон:
readAll([C|Rest], [C|Answer]) :- %% Если С в порядке
...., % Делаем то-то
readAll(Rest, Answer).
readAll([C|Rest], Answer) :- %% Если нет
...., % То то.
readAll(Rest, Answer).
Но в данном случае первый аргумент – это файловый дескриптор, это означает что он на самом деле не список и мы не можем использовать сопоставление по образцу. Напротив, мы используем другой встроенный предикат – getO, который считывает один символ и двигает указатель файла к следующему символу (никогда этого не увидите). Чтение этого символа будет представлено как одно из наших условий.
Перед тем как привести определение readAll, приведем определения, которые понадобятся вам из стандартной встроенной библиотеки SWI-Prolog.
open/4: Предикат принимает 4 параметра и может быть вызван следующим образом:
open(X, read, In, [eof action(eof code)], где X – имя файла, read – константа, которая определяет, как файл будет использоваться. In – это внутренний файловый дескриптор и eof_action(eof_code) – это список опций (он может быть длиннее), который задает, что когда мы достигаем конца файла, должно быть возвращено значение –1.
get0/2: Этот предикат принимает два параметра
get0(In, C)
где In – это внутренний файловый дескриптор, а C унифицируется со следующим символом в файле… следующим – важная вещь, потому что есть некий внутренний механизм для того, чтобы продвигаться по файлу. Каждый раз get0 унифицирует скрытый файловый указатель по которому движется вперед. get0(In, C) не приводит к унификации С со следующим символом в файле, но ASCII кода следующего символа. Это означает, что наш список будет на самом деле состоять из ASCII кодов а не самих символов(т.е, они будут показывать целые значения, а не символы)
close/1: Предикат close принимает файловый дескриптор и унифицируется закрывая файл.
Задача 6
В этой задаче вы будете реализовывать предикат readAll с применением описанных выше встроенных предикатов. Выше мы обсуждали структуру предиката file и использовали встроенный предикат open. Эта структура работает, но теперь вам необходимо использовать open/4 предикат. Для того, чтобы начать, вы можете попробовать решить следующую мини-задачу. Вместо того, чтобы получать все символы в файле, просто напишите readlAll так, чтобы он получал первый символ в файле. Следующее определение должно работать.
file(X, Y) :- open(X, read, In,[eof_action(eof_code)]),
readAll(In, Y).
readAll(In,C) :- get0(In, C).
Есть вещи, которые эта штука не делает, например она не проверяет валидность С и не закрывает файл. Но если вы поместите это в файл filein.pl, то сможете выполнить такой запрос:
file(’filein.pl’,C).
Когда это сработает, помните, что вы получите ASCII код а не символ.
Перед тем, как начать решать общую задачу есть две вещи, которые вы должны рассмотреть. Первое, мы хотим быть уверены, что только те символьные коды допустимы, которые валидны. Разумным способом обрабатывать эту ситуацию – это определение предиката legal/1 и фактов и правил, которые какие символы валидны, а какие нет. Второе, когда достигается конец файла, get0(C) унифицируется со значением –1. В этом случае, никакой символ не будет положен в список. Это потребует два разных случая (не забывайте закрыть файл после получения конца файла)
Наконец, помните, что способом думанья о правилах для предиката это - “левая сторона истинна, когда истинна правая сторона” – т.е, какие условия (справа) приведут к истинности условий слева?
Теперь вы можете задуматься о реализации предиката readAll. Когда закончите вы должны протестировать его введя запрос основанный на предикате file, как указано в начале раздела. [Подсказка: определите два предиката – legal/1 как описано выше, и eof_char/1 который истинен только для одного значения – ‘-1’.]
Помните, что файл этой программы должен быть в файле filein.pl. Теперь, когда вы захотите использовать эти предикаты, вы можете включить следующую строку в первой строке программного файла
:- consult(filein).
Эта строка создает запрос, который разрешается до того, как остальная программа считывается.
Комментариев нет:
Отправить комментарий