将原子绑定到Prolog中递归目标的结果



假设我要找出小于某个最大值的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。

最新更新