我正在使用SWI-Prolog创建一个Wumpus World项目。我应该从.txt文件中读取金币,凹坑和Wumpus的位置,如下所示:
GOLD 3 2
WUMPUS 3 3
PIT 2 1
PIT 3 4
在单词标识对象的情况下,第一个数字标识对象的x位置,第二个数字标识对象的y位置。我知道如何打开文件并从中读取,我只是不知道如何告诉我的程序GOLD 3 2意味着黄金需要位于(3,2)。
答案 0 :(得分:6)
我想在已有的解决方案中添加基于DCG的解决方案。
使用DCG完成此任务有一些主要优势:
以下代码假设设置:
:- set_prolog_flag(double_quotes, chars).
我建议使用此设置,因为它可以使DCG更具可读性。
token//1
我们首先简要介绍令牌的含义:
token(T) --> alnum(L), token_(Ls), !, % single solution: longest match { atom_chars(T, [L|Ls]) }. alnum(A) --> [A], { char_type(A, alnum) }. token_([L|Ls]) --> alnum(L), token_(Ls). token_([]) --> [].
以下是一些例子:
?- phrase(token(T), "GOLD"). T = 'GOLD'. ?- phrase(token(T), "2"). T = '2'. ?- phrase(token(T), "GOLD 2"). false.
最后一个例子清楚地表明空白不能成为令牌的一部分。
我们将空白视为以下序列:
spaces --> []. spaces --> space, spaces. space --> [S], { char_type(S, space) }.
因此,由空格分隔的标记序列是:
tokens([]) --> []. tokens([T|Ts]) --> token(T), spaces, tokens(Ts).
我们现在可以透明地将此DCG应用于文件,使用Ulrich Neumerkel的远见卓识library(pio)
:
以下是wumpus.data
:
$ cat wumpus.data GOLD 3 2 WUMPUS 3 3 PIT 2 1 PIT 3 4
使用phrase_from_file/2
将DCG应用于文件,我们得到:
?- phrase_from_file(tokens(Ts), 'wumpus.data'). Ts = ['GOLD', '3', '2', 'WUMPUS', '3', '3', 'PIT', '2', '1', 'PIT', '3', '4'] .
从这样的令牌列表中,很容易派生出必要的数据,例如再次一个DCG:
data([]) --> []. data([D|Ds]) --> data_(D), data(Ds). data_(gold(X,Y)) --> ['GOLD'], coords(X, Y). data_(wumpus(X,Y)) --> ['WUMPUS'], coords(X, Y). data_(pit(X,Y)) --> ['PIT'], coords(X, Y). coords(X, Y) --> atom_number(X), atom_number(Y). atom_number(N) --> [A], { atom_number(A, N) }.
我们可以将这些DCG一起用于:
示例查询:
?- phrase_from_file(tokens(Ts), 'wumpus.data'), phrase(data(Ds), Ts). Ts = ['GOLD', '3', '2', 'WUMPUS', '3', '3', 'PIT', '2', '1'|...], Ds = [gold(3, 2), wumpus(3, 3), pit(2, 1), pit(3, 4)] .
有关此多功能机制的详细信息,请参阅 dcg 。
1 请注意,SWI-Prolog附带library(pio)
的过时版本,但不适用于double_quotes
集合为chars
。如果您想尝试使用SWI-Prolog。
答案 1 :(得分:1)
考虑使用library(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
,其中Type为第一个参数,坐标为第二个和第三个参数。
我猜你将如何使用这是另一个问题。现在你可以问,"我在哪里有黄金":
?- location('GOLD', X, Y).
X = 3,
Y = 2.
或者,"我在哪里有一个坑"?
?- location('PIT', X, Y).
X = 2,
Y = 1 ;
X = 3,
Y = 4.
答案 2 :(得分:0)
您需要打开文件,阅读其中的行,并为每个文件将行拆分为您要使用的术语。然后,您可以将这些术语放在某个变量中,或者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)