Как прочитать данные из файла в Пролог
Я использую 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)
за:
- SICStus Пролог
- или SWI. 1
Вот 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 вместе, чтобы:
- токенизировать файл или заданный список символов
- анализировать токены для создания структурированных данных.
Пример запроса:
? - фраза_от_файла (токены (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.