如何从文件中读取数据到Prolog
How to read data from file into Prolog
我正在使用 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)。
您需要打开文件,阅读其中的每一行,然后将每一行拆分为您要使用的术语。然后,您可以将这些术语放在某个变量中或 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)
考虑使用 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)].
重要:表示分隔符是space字符的方法是添加选项location(0' )
。 0'
之后和右括号之前的 space 很重要!
现在你的数据库中有一个 table,location/3
,它的第一个参数是类型,第二个和第三个参数是坐标。
我想您将如何使用它是另一个问题。现在你可以问,"where do I have gold":
?- location('GOLD', X, Y).
X = 3,
Y = 2.
或者,"where do I have a pit"?
?- location('PIT', X, Y).
X = 2,
Y = 1 ;
X = 3,
Y = 4.
基于 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.
最后一个例子清楚地表明 whitespace 不能是标记的一部分。
空格
我们将以下序列视为空白:
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 试试这个,直接使用 Ulrich 提供的版本。
我正在使用 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)。
您需要打开文件,阅读其中的每一行,然后将每一行拆分为您要使用的术语。然后,您可以将这些术语放在某个变量中或 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)
考虑使用 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)].
重要:表示分隔符是space字符的方法是添加选项location(0' )
。 0'
之后和右括号之前的 space 很重要!
现在你的数据库中有一个 table,location/3
,它的第一个参数是类型,第二个和第三个参数是坐标。
我想您将如何使用它是另一个问题。现在你可以问,"where do I have gold":
?- location('GOLD', X, Y).
X = 3,
Y = 2.
或者,"where do I have a pit"?
?- location('PIT', X, Y).
X = 2,
Y = 1 ;
X = 3,
Y = 4.
基于 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.
最后一个例子清楚地表明 whitespace 不能是标记的一部分。
空格
我们将以下序列视为空白:
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 试试这个,直接使用 Ulrich 提供的版本。