Prolog:如何从控制台读取数据并将其存储到数据库中。遇到错误

时间:2019-01-30 02:25:11

标签: prolog

const replace = ({length}) => Array(length).fill(0);
console.log(replace("testing"));
console.log(replace(['12','a','drink','cookie']));

我想运行此Prolog以声明名称和年龄,但是当我为数字写年龄时,它会显示

update :-
    write("Name?:"),
    read(Name),
    assert(Name),nl,
    write("Age?:"),
    read(Age),
    assert(Age),
    write("Continue(y or n)?:"),
    read(Respond),
    process(Respond).

process(y) :-
     write('Name?:'),
     read(Name),
     assert(Name),nl,
     write("Age?:"),
     read(Age),
     assert(Age),
     repeat,
     write("y or n"),
     read(Respond),
     process(Respond).

 process(n) :- !.

如何解决此问题。

3 个答案:

答案 0 :(得分:2)

问题1

assert/1的错误输入

问题不仅仅在于Age,还包括使用assert的任何输入,例如

?- update.
Name?:Fred
|: .

ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:    [9] assert(_4940)
ERROR:    [8] update at c:/example.pl:8
ERROR:    [7] <user>


?- update.
Name?:Jim.

ERROR: Arguments are not sufficiently instantiated
ERROR: In:
ERROR:    [9] assert(_5826)
ERROR:    [8] update at c:/example.pl:8
ERROR:    [7] <user>

问题是assert/1没有得到事实或规则。
assert/1说:

  

在数据库中添加一个子句(事实或规则)。

请参见facts and rules

在上面的示例中,Fred不是事实,因为它没有以句点(.)结尾。

在上面带有Jim.的示例中,给出了一个句点,但是因为Jim以大写字母开头,所以它不是事实或规则,而是变量。

以数字形式输入年龄时,这又不是事实或规则,而是整数。

问题2

使用read/1表示:

  

从当前输入流中读取下一个Prolog术语,并将其与Term统一。

阅读序言term时,输入必须以句点结尾。

这不仅要求输入是一个术语,而且以.结尾,给定提示,例如Age更加令人困惑。您发现的大多数示例都可以完成您的工作,下面的更正代码可以满足您的要求。

问题3

竞争方式或重复方式。

代码使用两种方式:

  1. 使用repeat/0
  2. 它是recursive,例如

    process(y) :-
      ...
      process(Respond).
    

这使代码难以工作。

问题4

重复的代码,例如

write("Name?:"),
read(Name),
assert(Name),nl,
write("Age?:"),
read(Age),
assert(Age),
write("Continue(y or n)?:"),
read(Respond),
process(Respond).

当一个副本被纠正而另一副本未被纠正时,重复的代码更有可能导致问题。


问题1修复

使输入成为事实,然后再使用assert/1将其存储在数据库中,例如

变量中的值

Name

Age

通过添加functor

将变量转换为事实
name(Name)

age(Age)

assert/1

使用的事实
assert(name(Name))

assert(age(Age))

问题2修复

使用read_string/5,例如

read_string(user, "\n", "\r", End, Name)

这会将输入Name作为字符串读取。现在输入是字符串,而不是Prolog术语,因此不再需要句点。有predicates that operate on strings

问题3修复

使用递归表单并删除repeat/0
这也可以使用repeat/0代替递归。下面已更正的代码使用递归来演示对process/1的更改。

问题4修复

只需重构代码即可。您可以在最后的更正代码中看到它。


现在已修复。

更改1

由于continue的输入不再是术语,例如yn,但为字符串,process的参数必须为字符串,例如

process("y") :-

process("n") :-

更改2

年龄将断言为字符串,但最好断言为整数。

number_string/2可以解决此问题,例如

number_string(Age_n,Age),
assert(age(Age_n))

更改3

user27815在评论中提问:

  

您是否需要切入过程(“ n”):-!。 ?

因为

process(Respond).

没有创建选择点,不需要剪切。


更正的代码:

update :-
     % Respond will be read as a string and not as a term, so it needs "".
    process("y").

process("y") :-
     write('Name: '),
     read_string(user, "\n", "\r", End, Name),
     assert(name(Name)),
     write("Age: "),
     read_string(user, "\n", "\r", End, Age),
     number_string(Age_n,Age),
     assert(age(Age_n)),
     write("Continue: (y or n) "),
     read_string(user, "\n", "\r", End, Respond),
     process(Respond).

 process("n").

示例运行:

?- update.
Name: Fred
Age: 30
Continue: (y or n) y
Name: Jim
Age: 21
Continue: (y or n) n
true.

要检查数据库是否已更新,请使用listing/1

?- listing(name/1).
:- dynamic name/1.

name("Fred").
name("Jim").

true.

?- listing(age/1).
:- dynamic age/1.

age(30).
age(21).

true.

免费的增强功能。

将名称和年龄的事实分开保存并不能保持它们之间的联系完整。更好的解决方案是同时使用personName值的Age事实。

这是必要的修改后的代码。

update :-
     % Respond will be read as a string and not as a term, so it needs "".
    process("y").

process("y") :-
     write('Name: '),
     read_string(user, "\n", "\r", End, Name),
     write("Age: "),
     read_string(user, "\n", "\r", End, Age),
     number_string(Age_n,Age),
     assert(person(Name,Age_n)),
     write("Continue: (y or n) "),
     read_string(user, "\n", "\r", End, Respond),
     process(Respond).

process("n").

示例运行:

?- update.
Name: Fred
Age: 30
Continue: (y or n) y
Name: Jim
Age: 21
Continue: (y or n) n
true.

要检查数据库是否已更新,请使用listing/1

?- listing(person/2).
:- dynamic person/2.

person("Fred", 30).
person("Jim", 21).

true.

在注意到您删除的答案之后。

在删除的答案中,您拥有

?- person(name(N), age(A)).
N = nancy,
A = 22;
N=  steve,
A = 100;
true.

要创建此事实变体所需的更改是

assert(person(name(Name),age(Age_n)))

但是,这可能不是最佳的选择。

在Prolog中,位置通常表示值的含义,例如第一个位置是name,第二个位置是age。在此变体中,通过将函子nameage添加到事实person/2,您可以复制已知知识,但更重要的是,Prolog必须要做大量工作。

例如:

如果事实是person(Name,Age).才能得到NameAge,那么Prolog只需一个统一即可。但是对于person(Name,Age).,Prolog现在需要与person(name(nancy),age(22))统一,然后才能使Namename(nancy)再次统一,而使Age必须与{{ 1}}。您也可以使用age(22),它仅需一个统一,但是现在使您的代码更加冗长。

初次学习Prolog时,这种拐杖会有所帮助,但是当使用较大的数据集时,这会开始影响性能。

已删除答案中的另一项值得注意的事项是,这些人的名字仍然基于使用person(name(Name),age(Age)).,例如read/1nancy。尽管许多Prolog示例都执行此操作,但并不需要保留它们本身,它们可以是字符串。奇怪的是,代码永远不需要在stevenancy上完全匹配,而是总是将它们作为变量中的值引用。将它们保留为字符串的好处是,将它们写出时,它们将正确显示为steveNancy

答案 1 :(得分:1)

这是因为assert不适用于变量。它主张事实或规则;换句话说,assert(something)断言something必须为真。

SWI-Prolog documentation

  

在数据库中添加一个子句(事实或规则)。

整数值不是规则或事实。在这种情况下,它是一个整数,而不是计算为布尔值的值。声明值没有意义。

答案 2 :(得分:1)

我会写一些助手:

read_assert(P,V) :- format('~w ? ',[P]), read(V), A =.. [P,V], assert(A).

?- maplist(read_assert, [name,age], Vs).
name ? capellic.
age ? 99.

Vs = [capellic, 99].

?- name(N).
N = capellic.
相关问题