如何将文件中的数据读入Prolog

时间:2016-10-20 23:01:37

标签: prolog wumpus-world

我正在使用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)。

3 个答案:

答案 0 :(得分:6)

基于DCG的解决方案

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

DCG的优点

使用DCG完成此任务有一些主要优势:

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

预赛

以下代码假设设置:

:- 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一起用于:

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

    ?- 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)] .
    

    有关此多功能机制的详细信息,请参阅

    1 请注意,SWI-Prolog附带library(pio)过时版本,但不适用于double_quotes集合为chars。如果您想尝试使用SWI-Prolog。

    ,请直接使用Ulrich提供的版本

答案 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)