4

I am using SWI-Prolog to create a Wumpus World project. I am supposed to read the locations of the gold, pits, and the Wumpus from a .txt file that looks like this:

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

Where the words identify the object, the first number identifies the x position of the object, and the second number identifies the y position of the object. I know how to open a file and read from it, I just don't know how to tell my program that GOLD 3 2 means that the gold needs to be located at (3, 2).

4

3 回答 3

6

基于 DCG 的解决方案

我想在现有的解决方案中添加一个基于 DCG 的解决方案。

DCG的优势

使用 DCG 完成此任务有几个主要优势:

  • 您可以轻松地以交互方式测试您的解析器,而无需修改单独的文件。
  • 一个足够通用的 DCG 可用于解析和生成测试数据。
  • 了解这种方法对于更复杂的解析任务可能会派上用场,这些任务不符合 CSV 等预定格式。

预赛

以下代码假定设置:

:- set_prolog_flag(double_quotes, chars)。

我推荐这个设置,因为它使 DCG 的工作更具可读性。

积木:token//1

我们从令牌含义的简短定义开始:

令牌(T)-->
        铝(L),
        token_(Ls),
        !, % 单一解:最长匹配
        { atom_chars(T, [L|Ls]) }。

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

token_([L|Ls]) --> alnum(L),token_(Ls)。
令牌_([])-> []。

示例查询

这里有一些例子:

?- 短语(令牌(T),“黄金”)。
T = '黄金'。

?- 短语(令牌(T),“2”)。
T = '2'。

?- 短语(token(T), "GOLD 2" )。
的。

最后一个示例清楚地表明空格不能成为令牌的一部分。

空白

我们将以下序列视为空白:

空格-> []。
空格——>空格,空格。

空间 --> [S], { char_type(S, space) }。

解决方案

因此,由空格分隔的标记序列是:

令牌([])-> []。
标记([T|Ts]) --> 标记(T)、空格、标记(Ts)。

就是这样!

我们现在可以使用 Ulrich Neumerkel 的远见卓识将这个 DCG 透明地应用于文件library(pio)

这里是wumpus.data

$猫wumpus.data
黄金 3 2
乌姆普斯 3 3
坑 2 1
坑 3 4

使用phrase_from_file/2将 DCG 应用于文件,我们得到:

?-phrase_from_file(令牌(Ts),'wumpus.data')。
Ts = ['GOLD','3','2','WUMPUS','3','3','PIT','2','1','PIT','3','4' ] .

从这样的令牌列表中,很容易得出必要的数据,例如再次使用 DCG:

数据([])-> []。
数据([D|Ds])-> 数据_(D),数据(Ds)。

数据_(黄金(X,Y))-> ['黄金'],坐标(X,Y)。
数据_(wumpus(X,Y))-> ['WUMPUS'],坐标(X,Y)。
数据_(坑(X,Y))-> ['PIT'],坐标(X,Y)。

坐标(X,Y)-> atom_number(X),atom_number(Y)。

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

我们可以一起使用这些 DCG 来:

  1. 标记文件或给定的字符列表
  2. 解析令牌以创建结构化数据。

示例查询:

?-phrase_from_file(tokens(Ts), 'wumpus.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)]

有关这种通用机制的更多信息,请参阅


1请注意,SWI-Prolog 附带一个过时的版本library(pio),它不适用于double_quotes设置为 chars. 如果您想在 SWI-Prolog 中试用,请直接使用 Ulrich 提供的版本。

于 2016-10-22T08:29:08.117 回答
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,它的第一个参数是类型,第二个和第三个参数是坐标。

我猜你将如何使用它是另一个问题。现在你可以问,“我哪里有金子”:

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

或者,“我哪里有坑”?

?- location('PIT', X, Y).
X = 2,
Y = 1 ;
X = 3,
Y = 4.
于 2016-10-21T08:13:51.283 回答
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)
于 2016-10-21T02:45:12.303 回答