我试图在不使用递归的情况下实现因子计算(n!)的解决方案,仅使用prolog的retroaction。例如:
factorial(0, 1).
factorial(1, 1).
factorial(2, 2).
retroaction(X, Y) :-
factorial(K, Y),
not(K = X),
fail.
retroaction(K, Y) :- factorial(K, Y).
使用固定事实 factorial ,如果我调用谓词 retroaction 来发现2的阶乘,我将收到正确答案:
?- retroaction(2, X).
X = 2.
我正在尝试实施的解决方案是:
factorial_ret(Number, _) :-
retractall(factorial(_, _)),
assertz(factorial(0,1)),
factorial(X, Y),
not(X = Number),
NewNumber is X + 1,
Factorial is Y * NewNumber,
retractall(factorial(_, _)),
assertz(factorial(NewNumber, Factorial)),
fail.
factorial_ret(Number, Y) :-
factorial(Number, Y).
如果我尝试获得大于1的阶乘,则不起作用。有人知道为什么吗?
答案 0 :(得分:2)
无!我的良心迫使我un-ask your question。
您寻求的内容是no-no - 即使在imperative-programming - 而且在Prolog中也是如此。
在某些情况下,prolog-assert肯定非常有用;我通常将其视为“万不得已的武器”,而不是“首选武器”。 IMO使用它的可能后果包括:
一个例子吗?得到答案by @CapelliC和by @Boris。 引用@Boris:
我正在写这个答案,因为它比@CapelliC的正确答案少了一些不必要的代码。
让我们运行一些查询并将评估纳入考试!
?- factorialBoris(0,0).
**LOOPS**
?- factorialBoris(0,2).
**LOOPS**
?- factorialBoris(1,2).
**LOOPS**
?- factorialCapelliC(0,0).
**LOOPS**
?- factorialCapelliC(0,2).
**LOOPS**
<强>нет!强>
请不要误会我的意思!我不想以任何方式,形式或形式放下工作和努力by @CapelliC和by @Boris,两个Prolog topusers on SO 。
底线:
在Prolog中编码时使用imperative-programming样式很容易适得其反!
使用prolog-assert严格测试代码比测试logically-pure代码要困难得多 - 甚至 !
如果存在不确定性,请选择明智的路径:只需做正确的事情:)
尽可能保留logical-purity - 不要交换任何东西作为回报。
答案 1 :(得分:1)
正如@lurker指出的那样,你混淆了存储谓词&#39;使用&#39;定义谓词&#39;。你需要factorial / 2定义,所以对retractall(factorial(_,_))
没什么意义。
下面,我使用retract(mem(N, F))
检索临时值并准备数据库以进行最终更新。应始终只有mem / 2的实例。
% replace recursion with a failure driven loop
:- dynamic mem/2.
factorial(X, Y) :-
assert(mem(0, 1)),
repeat,
retract(mem(N, F)),
( X == N % done ?
-> Y = F, % yes, succeed
! % but be sure to avoid 'external' backtracking...
; N1 is N + 1,
F1 is N1 * F,
assert(mem(N1, F1)),
fail % loop: 'goto repeat'
).
请注意,repeat,...,fail
之间分支中的所有内置函数都不可回溯(除了retract / 1之外)。
请注意,此类代码的递归定义要慢得多(并且在多线程SWI-Prolog中不起作用)。
我认为断言/撤回是早期提供给Prolog程序员的,以处理知识库,而不是作为全局变量。有几个Prolog对全局变量有专门的库谓词,因为它们有合法用途:例如,参见memoization。
PS:&#39;追溯&#39;是线性系统稳定性理论中使用的术语,我从未见过它用于编程
编辑可以了解@repeat报告的错误如何解决:只需交换统一和剪切即可。 好吧,我们还需要测试X是一个正整数。真诚地,我认为这对于手头的问题实际上是无关的。
...
( X == N % done ?
-> !, % yes, be sure to avoid further backtracking
Y = F % unify with the computed factorial
; N1 is N + 1,
...
答案 2 :(得分:0)
假设通过“追溯”你实际上是指"memoization",那么解决这个问题的方法之一就是:
只要您找不到所需的阶乘,找到目前为止最大的阶乘,计算下一个,冲洗并重复。
我正在写这个答案,因为它的代码少于其他正确的answer by @CapelliC。
你只需要一个启动事实,即0的阶乘:
然后,您可以使用repeat
循环执行“只要”。然后,如果你总是在预先计算的阶乘的堆栈顶部添加新的阶乘,而你总是只看一次,那么你可以确定你找到了迄今为止最大的阶乘:
:- dynamic factorial/2.
factorial(0, 1).
factorial_memoize(N, F) :-
repeat,
( factorial(N, F0)
-> !, F = F0
; once(factorial(N0, F0)),
succ(N0, N1),
F1 is N1 * F0,
asserta(factorial(N1, F1)),
fail
).
以下是该计划的运作方式:
?- listing(factorial/2).
:- dynamic factorial/2.
factorial(0, 1).
true.
?- factorial_memoize(0, F).
F = 1.
?- listing(factorial/2).
:- dynamic factorial/2.
factorial(0, 1).
true.
?- factorial_memoize(5, F).
F = 120.
?- listing(factorial/2).
:- dynamic factorial/2.
factorial(5, 120).
factorial(4, 24).
factorial(3, 6).
factorial(2, 2).
factorial(1, 1).
factorial(0, 1).
true.
答案 3 :(得分:-1)
您的k
条款中有一个小写的not
。