Prolog - 列表中的数字总和

时间:2014-12-04 22:00:34

标签: list prolog

我是Prolog的新手,仍然在努力学习语言的语法。我正在尝试编写一个通过列表查找的仿函数,并创建一个新列表,其中head是列表中所有数字的总和,而尾部是其中的任何其他内容。

例如,[1,2,a,3,b,c,4] = [10,a,b,c]。

现在,我担心我的代码非常粗糙,但如果有人能指出我正确的方向,我会非常感激。

sumOfNumbers([X], Z) :-
    number(X),
    Z is Z+X.
sumOfNumbers([X], _) :-
    not(number(X)).
sumOfNumbers([X|Rest], Z) :-
    number(X),
    Z is Z+X,
    sumOfNumbers(Rest, Z).
sumOfNumbers([X|Rest], Z) :-
    not(number(X)),
    sumOfNumbers(Rest, Z).

希望这不完全偏离基础。再次感谢

3 个答案:

答案 0 :(得分:0)

我会采用这种方法: 将给定列表分成两个数字和字母列表,总结数字并附加到字母列表:

% Sum of list of numbers
% sum(+List, -Sum)
sum([], 0).
sum([H|T], S) :-
    sum(T, S1),
    S is H + S1.

% Separate a list into two lists one of numbers and second of non-numbers
% separate(+InputList, -Numbers, -Letters)
separate([], [], []).
separate([H|T], [H|N], L) :- 
    number(H),
    separate(T, N, L).
separate([H|T], N, [H|L]) :- 
    separate(T, N, L).

% This is your function
sumOfNumbers(L, [Sum | Letters]) :-
    separate(L, Numbers, Letters),
    sum(Numbers, Sum).

这不是最佳方法,但它在逻辑上是直截了当的。

答案 1 :(得分:0)

您使用的变量很少。 Prolog是一种声明性语言,一旦设置了变量就无法更改。正如CapeliiC指出的那样,Z is Z + X只有X = 0才有效。因此,大多数谓词文档都以true if xxxx/y unifies with…开头。

List递归的基本情况在大多数情况下是空列表[],而不是单个元素列表[X]。在大多数情况下,它只会使您的程序复杂化,甚至更糟糕的是,在回溯后添加解决方案。

这些是您在浏览列表时需要处理的案例。

  1. 列表为空→返回列表[0]

  2. 头部是一个数字→返回上一个结果加上找到的数字

  3. 头部不是数字→在上一个结果的编号后将其添加到列表中。

  4. 这样,您始终确保将数字作为输出列表的第一个元素。由于第一个元素只是另一个参数,因此sumOfNumbers(Input, Sum, Rest)不会分心,所以谓词更好。

    所以这是程序:

    sumOfNumbers([],[0]).
    sumOfNumbers([X|R],[Z|A]):-
        number(X),               % cut here
        sumOfNumbers(R,[Y|A]),
        Z is Y + X.
    sumOfNumbers([X|R],[Y,X|A]):-
        \+ number(X),            % cut here
        sumOfNumbers(R,[Y|A]).
    

    您可以在数字检查后添加(绿色)剪切运算符,以防止重做失败。

    如果检查两种情况并且结构相似,我个人更喜欢使用->运算符:

    sumOfNumbers2([],[0]). 
    sumOfNumbers2([X|R],Out):-
        sumOfNumbers2(R,[Y|A]),
        (number(X)->             % read as if X is a number
           Z is Y + X,
           Out = [Z|A];
         Out =[Y, X|A]).
    

答案 2 :(得分:0)

在prolog中,一旦你将一个变量绑定到一个值,它就不再是变量了。这意味着您需要在递归列表时保持调用堆栈上的状态。所以我会像这样处理这个问题,使用辅助谓词和额外的参数来保持状态。

约定通常是帮助程序与“公共”谓词具有相同的函子,并具有维护状态所需的额外值。我会接近这样的事情:

sum_of_numbers(Xs,Ys) :-     % to sum the numbers in a list, 
  sum_of_numbers(Xs,0,[],Ys) % we invoke the helper, seeding its two accumulators appropriately.
  .

sum_of_numbers( []     , T , L , [T|L] ) .  % when the source list isexhausted, unify the accumulators with the result
sum_of_numbers( [X|Xs] , T , L , R     ) :- % otherwise,
  number(X) ,                               % - if X is numeric
  T1 is T+X ,                               % - increment the tally
  sum_of_numbers(Xs,T1,L,R)                 % - and recurse down
  .                                         %
sum_of_numbers( [X|Xs] , T , L , R     ) :- % otherwise,
  \+ number(X) ,                            % - if X is non-numeric
  sum_of_numbers(Xs,T,[X|L],R)              % - add X to the list accumulator
  .                                         % - and recurse down.

您还可以使用软剪辑(暗示)来合并第2和第3条:

sum_of_numbers( []     , T , L , [T|L] ) .
sum_of_numbers( [X|Xs] , T , L , R     ) :-
  ( number(X) ->
    T1 is T+X , L1 = L
  ;
    T1 = T    , L1 = [X|L]
  ) ,
  sum_of_numbers(Xs,T1,L1,R)
  .

这是否有所改善取决于你。