Prolog:对表示整数的两个列表的元素求和(内部限制不是常规总和!!)

时间:2015-06-15 15:30:25

标签: list prolog

我正在解决一个问题: 列表表示一个整数,表示12345,L = [12,34,5]每个元素应该是0到99.练习是写一个函数(sum),它将两个列表相加并给出它们的和的等价列表表示两个整数的总和。

?-sum([11,11],[11,11],L,0).
L=[22,22].

?-sum([12,81],[11,44],L,0).
L=[24,25].

//digit fxn for making sure that we insert an elemnt of two or single integer 
//and saving the overflow digit in S1.

我的代码给了我错误:

digit(X,D,S1):- S is X/100,S1 is integer(S),0 is S1,D is X.

digit(X,D,S1):-D is mod(X/100).

sum([],[],[],0).

sum(H1|T1,H|T,H3|T3,S):-Z is H1+H+S ,digit(Z,H3,S1),sum(T1,T,T3,S1).

3 个答案:

答案 0 :(得分:1)

使用简单明了!

:- use_module(library(clpfd)).

digitFD(X,Zs) :-
   digitFD_aux(X,Zs,[],Zs).

digitFD_aux(X,[_],Zs,[X|Zs]) :-
   X in 0..99.
digitFD_aux(X,[_|Ys],Zs0,Zs) :-
   X #> 99,
   Z #> 0,
   Y in 0..99,
   X #= Y + 100 * Z,
   digitFD_aux(Z,Ys,[Y|Zs0],Zs).

让我们稍微测试digitFD/2

?- As = [12,34,56,78], digitFD(N,As).
As = [12,34,56,78], N = 12345678 ;
false.

?- N = 123456789, digitFD(N,As).
N = 123456789, As = [1,23,45,67,89] ;
false.

OK!我们来定义sumFD/4

sumFD(As,Bs,Cs,Zs) :-
   digitFD(A,As), 
   digitFD(B,Bs),
   C #= A+B,
   digitFD(C,Cs),
   append([As,Bs,Cs],Zs).

让我们使用它!

?- sumFD([11,11],[11,11],Xs,Zs), labeling([],Zs).
Xs = [22,22], Zs = [11,11,11,11,22,22] ;
false.

?- sumFD([12,81],[11,44],Xs,Zs), labeling([],Zs).
Xs = [24,25], Zs = [12,81,11,44,24,25] ;
false.

答案 1 :(得分:0)

digit是一个递归谓词。我的定义中没有看到递归。

基本情况当然是数字为0时,列表为空:

digit(0, []).

然而递归情况被分成两部分,取决于第一个参数是变量,还是第二个:

digit(X, [Y|Ys]) :- nonvar(Y), digit(Z, Ys), X is Y + 100 * Z.
digit(D, [X|Xs]) :- nonvar(D), D>0, X is mod(D,100), R is div(D,100), digit(R, Xs).

现在您可以使用digit方式:

?- digit(12345,X).
X = [45, 23, 1] ;
false.

?- digit(X,[45,23,1]).
X = 12345.

请注意列表是相反的!你可以让它的输出已经反转了(我将把它留作练习,而我会在这里使用reverse/2;提示:使用accumulators)。

sum(A,B,R) :- reverse(A,AR), reverse(B,BR),
              digit(A1,AR), digit(B1,BR),
              R1 is A1+B1,
              digit(R1,RR),
              reverse(R,RR).

?- sum([11,11],[11,11],X).
X = [22, 22] .

?- sum([12,81],[11,44],X).
X = [24, 25] .

答案 2 :(得分:0)

您的列表是出于所有意图和目的的基数为100的数字。解决这个问题的简单方法是以相同的方式进行算术运算:用最低有效数字开始,使用最高有效数字,对每一对数字求和,用左边的进位如果溢出。

我们反转列表以便让我们从右到左轻松工作。您会注意到,工作线谓词sum_list_mod_100/4构建的结果是正确的。

sum_list_mod_100( Xs , Ys , Zs ) :-       % to compute the sum of a list representing a base-100 integer.
  reverse( Xs , X1 ) ,                    % - reverse the digits of the left hand side (so we're working from least- to most-significant digit)
  reverse( Ys , Y1 ) ,                    % - reverse the digits of the right hand side (so we're working from least- to most-significant digit)
  sum_list_mod_100( X1 , Y1 , 0 , Zs ) .  % - invoke the worker with the carry initialized as zero.
  .

sum_list_mod_100( []     , [] , C , []  ) .         % both lists are empty w/o carry: terminate. 
  C = 0                                             %
  .                                                 %
sum_list_mod_100( []     , [] , C , [C] ) :-        % both lists are empty with a carry: prepend the carry to the result.
  C > 0                                             %
  .                                                 %
sum_list_mod_100( [X|Xs] , [] , C , [Z|Zs]  ) :-    % right-hand side exhausted?
  sum_digits(X,0,C,Z,C1) ,                          % - sum the digits, returning the new digit and the new carry
  sum_list_mod_100( Xs , [] , C1 , Zs )             % - recurse down, passing the new carry
  .                                                 % 
sum_list_mod_100( [] , [Y|Ys] , C , [Z|Zs]  ) :-    % left-hand side exhausted?
  sum_digits(0,Y,C,Z,C1) ,                          % - sum the digits, returning the new digit and the new carry
  sum_list_mod_100( [] , Ys , C1 , Zs )             % - recurse down, passing the new carry
  .                                                 %
sum_list_mod_100( [X|Xs] , [Y|Ys] , C , [Z|Zs] ) :- % not yet exhausted?
  sum_digits(X,Y,C,Z,C1) ,                          % - sum the digits, returning the new digit and the new carry
  sum_list_mod_100( Xs , Ys , C1 , Zs )             % - recurse down passing the new carry
  .                                                 % Easy!

sum_digit(X,Y,C,Z,C1) :-  % to sum two digits (and the carry)
  S is X+Y+C ,            % - sum the LHS, RHS and the carry
  Z is X mod 100 ,        % - compute the digit modulo 100
  C1 is X div 100         % - compute the carry
  .