CapelliC 提供了一个极好的(更好的)答案,而我正忙着在这个怪物上打字。事实上,我最终并没有解决你标题中的问题,因为你传递参数很好。相反,我写了关于assertz/1
and retract/1
。但是,我在创作时自学了相当多的内容,您可能还会发现它提供了丰富的信息。
在您的示例代码中,我们使用谓词savMoney/1
, earn/1
, depPeople/1'. We then have a number of rules that determine values based on these facts. A rule is of the form
:- 声明了 3 个事实。, and which I sometimes read to myself as "<head> is true if <body> is true". We can think of a fact as a rule of the form
:- true , e.g.,
savMoney(30000) :- true.`,我们可以将其读作“如果 true 为 true,30000 是 savMoney”,并且 true 为 true,否则我们都搞砸了。(顺便说一句,“savMoney”是存钱的缩写吗?)
指令的形式为:- <body>.
。这就像一个必须测试的规则才能使程序(或世界)为真(这比准确更令人回味,因为正如您所看到的,当指令失败时,整个程序世界都不是假的,我们只是得到一个警告)。当我们查阅序言文件时,我们向程序世界添加了新的规则和事实,这些甚至可能是不可能的废话陈述,例如a :- \+ a.
“a is true if not-a is true” 1。如果您查询,这种矛盾会导致堆栈溢出?- a.
,但程序会正常加载。但是,指令必须在程序按照遇到的顺序加载时进行评估和解决:
该程序在解释器查询时会抛出堆栈溢出错误。
a :- \+ a.
:- a.
这个程序会抛出一个undefined procedure
错误,因为它被指示在 a 被输入数据库之前证明 a。
:- a.
a :- \+ a.
当我们有类似的指令时:- read(A), savMoney(A).
,它并不是说“将用户输入的值读入 A,然后将 saveMoney 设置为 A”。相反,它说的更像是“如果加载了这个程序,那么 A 是从用户输入读取的值, A 是 savMoney。” 假设您运行程序并在第一个提示符处输入 100(简单的提示符是|
)。怎么了?
- prolog 将变量 A 与 100 统一起来。
- prolog 试图证明 savMoney(100)。
- 它回复
Warning: Goal (directive) failed: user:(read(_G2072),savMoney(_G2072))
。
这是因为,虽然 savMoney(30000) 为真,但 savMoney(100) 不是。指令不断言其主体的内容,它只告诉 prolog 证明这些内容。
您正在尝试做的是允许用户将以前未知的事实断言到数据库中。如 mbratch 所示,这需要使用谓词assertz/1
2。但是,在运行时更改的预测与标准谓词不同。
如果你试图在程序中定义一个重新建立的谓词,你会得到一个错误。例如,查阅包含以下声明的文件:
length(2, y).
你会收到一个错误:
ERROR: /Users/aporiac/myprolog/swi/studies/test.pl:18:
No permission to modify static procedure `length/2'
Defined at /opt/local/lib/swipl-6.2.6/boot/init.pl:2708
这告诉我们 'length/2' 是静态的,并且它已经在 init.pl 文件的第 2708 行定义。
如果您尝试使用 断言静态谓词,也会发生同样的情况assertz/1
。您可以通过assertz(savMoney(100))
在 swipl 中查询来尝试此操作。为了添加关于谓词的新事实或规则,我们必须将谓词声明为动态的。
这是通过dynamic/1
. 为了确保 prolog 知道我们的哪些谓词被视为动态的,我们给它一个像这样的指令3:
:- dynamic savMoney/1.
如果您已将其添加到文件中(在定义谓词之前),则可以查询?- assertz(savMoney(100)).
以将新事实添加到数据库中。现在,如果你查询?- savMoney(X)
,你会得到
X = 30000;
X = 100.
X 现在有两个可能的值,因为我们在数据库中添加了另一个事实。
当然,在您的情况下,您不想继续向 中添加值savMoney/1
,您希望能够更新和替换该值。
这需要retract/1
(如果您认为断言的谓词可能会在某个时候添加不止一次,那么您可以使用retractall/1
清除所有实例)。现在我们可以编写如下规则:
set_saved(Amount) :-
retract( savMoney(_) ),
assertz( savMoney(Amount) ).
set_saved(Amount)
如果savMoney(_)
可以从数据库中收回和删除并且savMoney(Amount)
可以断言新事实,则为真。
我刚刚看到 CapelliC 提供了一个简单的输入界面,以及一个更简洁的问题解决方案,但这是我的示例程序版本,以防它可能提供信息。(我实际上并没有解决添加提示和输入的问题,但是查询,例如 ,可以满足?- set_saved(100)
您的期望)。
:- dynamic [ savMoney/1,
earn/1,
depPeople/1 ].
do(save) :- save_bal(bad).
do(act) :- save_bal(good), inc(good).
do(comb) :- save_bal(good), inc(bad).
save_bal(good) :- savMoney(X), depPeople(Y), Min is Y * 1000, X >= Min.
save_bal(bad) :- not(save_bal(good)).
inc(good) :- earn(Z), depPeople(Y), MinE is 3000 + Y * 400, Z >= MinE.
inc(bad) :- not(inc(good)).
savMoney(30000).
earn(60000).
depPeople(4).
set_saved(Amount) :-
retract( savMoney(_) ),
assertz( savMoney(Amount) ).
set_earned(Amount) :-
retract( earn(_) ),
assertz( earn(Amount) ).
set_people_in_department(Number) :-
retract( depPeople(_) ),
assertz( depPeople(Number) ).
report([Saved, Earned, People]) :-
Saved = savMoney(_) , Saved,
Earned = earn(_) , Earned,
People = depPeople(_), People.
\+/1
是 swi-prolog 中的标准否定运算符并且not/1
是 depreciated。
assert/1
等价于 ,并折旧有利于 ,assertz/1
。asserta/1
将事实或子句断言为手头谓词的第一个实例,而assertz/1
将其断言为最后一个。(参见数据库的手册部分)。
当然,这与我之前建议的指令的解释背道而驰。当您在指令中使用“正常”谓词时,我的解释很合适。但是,大多数情况下,我们会看到用于特殊谓词的指令,例如在模块声明 ( :- module(name, [<list of exported predicates>]
) 或模块导入 ( :- use_module([<list of modules>])
) 中。