假设我要找出小于某个最大值的5的所有因子的和。我用递归的方式来做,因为这样看起来最简单。这是我的代码:
isFactor(X):-
Y is X mod 5,
Y = 0.
sumAll(Number, Result):-
sumAll(Number, 0, Result).
sumAll(Number, RunningTotal, Result):-
(isFactor(Number) ->
NextTotal is RunningTotal + Number;
NextTotal is RunningTotal),
NextNumber is Number - 1,
(NextNumber > 0 ->
mulSum(NextNumber, NextTotal, NextResult);
NextResult is RunningTotal),
number(NextResult) -> % this test is so that the interpreter
write(NextResult), nl; % doesn't print out a bunch of extra stuff
true. % (the internal IDs of each binding of
% NextResult maybe?) after the answer.
现在,这工作了(也就是说,它打印正确的总和),但我有点恼火,我不知道如何安排代码,以便执行
| ?- sumAll(10, X).
将X
绑定到10
,而不是在最后打印'10'并断言'yes'。
我的直觉是如果NextNumber > 0
(第13行)是真的,以某种方式将Result
重新绑定到NextResult
,但我怀疑这只是多年的Python编程试图断言自己。
是否有一种方法可以"返回"目标的结果,一直到这里的嵌套递归?还是我想错了?
对于一个简单的东西来说,这太复杂了。要对一个列表中所有能被N整除的元素求和,你所需要的只是这个尾部递归实现:
sum_all( Xs , N , Sum ) :-
sum_all( Xs , N , 0 , Sum )
.
sum_all( [] , _ , S , S ) .
sum_all( [X|Xs] , N , T , S ) :-
X mod N =:= 0 ,
! ,
T1 is T+X ,
sum_all(Xs,N,T1,S)
.
sum_all( [_|Xs] , N , T , S ) :-
sum_all(Xs,N,T,S)
.
非尾部递归实现稍微简单一点,但会在长列表上破坏堆栈:
sum_all( [] , _ , 0 ) .
sum_all( [X|Xs] , N , S ) :-
sum(Xs,N,T) ,
( X mod N =:= 0 -> S is T+X ; S is T )
.
你甚至可以做这样的事情来分解从列表求和中提取的"感兴趣的"值:
sum_all(Xs,N,Sum) :-
findall( X , ( member(X,Xs), X mod N =:= 0 ) , L ) ,
sum(L,Sum)
.
sum(L,S) :- sum(L,0,S).
sum( [] , S ,S ) .
sum( [X|Xs] , T ,S ) :- T1 is T+X , sum(Xs,T1,S) .
一旦你有了这个,你就可以简单地说:
sum_modulo_N_values( Xs , N ) :-
sum_all(Xs,N,Sum) ,
writenl( sum = Sum )
.
像这样调用
sum_modulo_N_values( [1,2,5,6,7,10,11,15,31,30] , 5 ) .
您将得到预期的sum = 60
写入控制台
你的代码看起来比需要的更复杂,也许这种复杂性隐藏了一个重要的事实:sumAll(Number, RunningTotal, Result):-
Result为单例。那么得到计算值的机会就很小了。
我会尝试摆脱number(NextResult) -> etc..
(顺便说一句,当使用if/then/else -即(C -> T ; F)
时,通常需要括号来获得预期的嵌套),并将"分配"代之以Result。