Как прочитать данные из файла в Пролог

Я использую SWI-Prolog для создания проекта Wumpus World. Я должен прочитать местоположение золота, ям и Wumpus из файла.txt, который выглядит следующим образом:

   GOLD 3 2
   WUMPUS 3 3
   PIT 2 1
   PIT 3 4

Где слова идентифицируют объект, первое число идентифицирует положение x объекта, а второе число идентифицирует положение y объекта. Я знаю, как открыть файл и прочитать из него, я просто не знаю, как сказать моей программе, что GOLD 3 2 означает, что золото должно быть расположено в (3, 2).

3 ответа

Решение

Вам нужно открыть файл, прочитать его строки и для каждого разделить строку на термины, которые вы хотите использовать. Затем вы можете поместить термины в некоторую переменную или assert/1 их в динамический дБ. В приведенном ниже примере я добавляю их в базу данных, используя динамический предикат location/3:

:- dynamic location/3.

load(File) :-
    setup_call_cleanup(
         open(File, read, Stream),
         load_loop(Stream),
         close(Stream)
         ).


load_loop(Stream) :-
    read_line_to_string(Stream, String),
    (String == end_of_file ->
         true
     ;
     split_string(String, " ", " ", [ItemS, XS, YS]),
     atom_string(Item, ItemS),
     term_string(X, XS),
     term_string(Y, YS),
     assertz(location(Item, X, Y)),
     load_loop(Stream)
    ).

show_locations :-
    forall(location(Item, X, Y),
           format('~p is at (~d, ~d)~n', [Item, X, Y])).

Предположим, что ваши входные данные были в wumpus.txt, тогда это даст вам:

bash-3.2$ swipl -l wumpus.pl
'GOLD' is at (3, 2)
'WUMPUS' is at (3, 3)
'PIT' is at (2, 1)
'PIT' is at (3, 4)

Решение на основе DCG

Я хотел бы добавить решение на основе DCG к уже существующим решениям.

Преимущества DCG

Есть несколько основных преимуществ использования DCG для этой задачи:

  • Вы можете легко протестировать свой анализатор в интерактивном режиме, без необходимости изменения отдельного файла.
  • Достаточно общий DCG может использоваться для анализа, а также для генерации тестовых данных.
  • Знание этого метода может пригодиться для более сложных задач анализа, которые не соответствуют заранее определенному формату, например CSV.

прелиминарии

Следующий код предполагает настройку:

: - set_prolog_flag (двойные кавычки, символы).

Я рекомендую этот параметр, потому что он делает работу с DCG более читабельной.

Структурный элемент: token//1

Мы начнем с краткого определения значения токена:

жетон (T) ->
        цифра, буква (L)
        token_(Ls),!, % одиночное решение: самое длинное совпадение
        { atom_chars(T, [L|Ls]) }.

alnum(A) -> [A], { char_type(A, alnum) }.

токен _([L|Ls]) ->
        alnum(L), токен_(Ls).
токен _([]) -> [].

Примеры запросов

Вот несколько примеров:

? - фраза (жетон (T), "ЗОЛОТО").
T = "ЗОЛОТО".?- фраза (токен (T), "2").
Т = '2'.?- фраза (жетон (T), "ЗОЛОТО 2").
ложь

Последний пример проясняет, что пробел не может быть частью токена.

Пробелы

Мы считаем пробелами следующие последовательности:

пробелы -> [].
пробелы -> пробелы, пробелы.

пробел -> [S], { char_type(S, пробел)}.

Решение

Следовательно, последовательность токенов, разделенных пробелами:

жетоны ([]) -> [].
токены ([T|Ts]) -> токен (T), пробелы, токены (Ts).

И это все!

Теперь мы можем прозрачно применить эту DCG к файлам, используя дальновидность Ульриха Ноймеркеля library(pio) за:

Вот wumpus.data:

$ cat wumpus.data
ЗОЛОТО 3 2
WUMPUS 3 3
Яма 2 1
Яма 3 4

С помощью phrase_from_file/2 Чтобы применить DCG к файлу, мы получаем:

? - фраза_от_файла (токены (Ts), 'wumpus.data').
Ts = ['GOLD', '3', '2', 'WUMPUS', '3', '3', 'PIT', '2', '1', 'PIT', '3', '4' ].

Из такого списка токенов легко получить необходимые данные, используя, например, снова DCG:

данные ([]) -> [].
данные ([D|Ds]) -> данные_(D), данные (Ds).

данные_ (золото (X,Y)) -> ['GOLD'], координаты (X, Y).
data_(wumpus(X,Y)) -> ['WUMPUS'], координаты (X, Y).
data_(pit(X,Y)) -> ['PIT'], координаты (X, Y).

координаты (X, Y) -> номер_ атома (X), номер_ атома (Y).

atom_number(N) -> [A], { atom_number(A, N) }.

Мы можем использовать эти DCG вместе, чтобы:

  1. токенизировать файл или заданный список символов
  2. анализировать токены для создания структурированных данных.

Пример запроса:

? - фраза_от_файла (токены (Ts), 'wumpus.data'),
   фраза (данные (Дс), Ц).
Ts = ['GOLD', '3', '2', 'WUMPUS', '3', '3', 'PIT', '2', '1' |...],
Ds = [золото (3, 2), вумпус (3, 3), яма (2, 1), яма (3, 4)].

См. Dcg для получения дополнительной информации об этом универсальном механизме.


1 Обратите внимание, что SWI-Prolog поставляется с устаревшей версией library(pio) , который не работает с double_quotes установлен в chars , Используйте версию, предоставленную Ulrich напрямую, если вы хотите попробовать это с SWI-Prolog.

Подумайте об использовании библиотеки (csv) для бесплатного чтения и анализа файла. Я положил ваш пример в файл wumpus.txt:

$ cat wumpus.txt 
GOLD 3 2
WUMPUS 3 3
PIT 2 1
PIT 3 4

В документации библиотеки есть несколько примеров кода, вот минимальный пример, прямо с верхнего уровня:

?- use_module(library(csv)).
true.

?- csv_read_file("wumpus.txt", World, [separator(0' ), functor(location)]),
   forall(member(L, World), assertz(L)).
World = [location('GOLD', 3, 2), location('WUMPUS', 3, 3), location('PIT', 2, 1), location('PIT', 3, 4)].

Важно: способ сказать, что разделитель является пробелом, это добавить опцию location(0' ), Пространство после 0' и перед закрывающей скобкой имеет значение!

Теперь у вас есть таблица в вашей базе данных, location/3, который имеет тип в качестве первого аргумента и координаты в качестве второго и третьего аргумента.

Как вы собираетесь использовать это другой вопрос, я думаю. Прямо сейчас вы можете спросить, "где у меня золото":

?- location('GOLD', X, Y).
X = 3,
Y = 2.

Или "где я есть яма"?

?- location('PIT', X, Y).
X = 2,
Y = 1 ;
X = 3,
Y = 4.
Другие вопросы по тегам