有没有一种方法可以只对Prolog中包含字母和整数的对列表中的整数求和?

时间:2019-04-22 05:12:09

标签: list prolog

我很难弄清楚如何找到像这样的成对列表中的整数之和:

[[a, 1], [b, 2], [c, 3], [d, 4]]

我尝试过类似的操作,因为它让人想起常规的求和函数:

sum([], 0).
sum([[_,Head]|[_,Tail]], Sum) :-
    sum([_,Tail], Sum2),
    Sum is Head+Sum2.

正在通话:

sum([[a, 1], [b, 2], [c, 3], [d, 4]], Total),
write('Sum = '), write(Total).

但这不起作用。当它应该打印出总和时,它会打印出false,这是10。

3 个答案:

答案 0 :(得分:1)

我认为将其分为两个任务可能会有所帮助:

  1. 为每个子列表的第二项创建一个新列表;和
  2. 汇总该列表。

这使解决两个问题变得更加容易,此外,您现在还有两个额外的谓词可用于其他目的。

我们可以通过以下方式获得子列表的第二项的列表:

item2list([], []).
item2list([[_, X|_]|T], [X|T2]) :-
    item2list(T, T2).

或者我们可以使用https://www.instagram.com/p/Bwedsp-HEF3/maplist/3 [swi-doc]

item2list(L1, L2) :-
    maplist(nth1(2), L1, L2).

或者我们可以用nth1/3 [swi-doc]findall/3 [swi-doc]来写item2list

item2list(L1, L2) :-
    findall(X, member([_,X|_], L1), L2).

尽管这里的谓词不是双向的。

例如:

?- item2list([[a, 1], [b, 2], [c, 3], [d, 4]], L).
L = [1, 2, 3, 4].

我将汇总列表作为练习。

答案 1 :(得分:1)

尝试定义谓词sum/2时,您没有正确处理列表列表。试试:

sum(Lists, Sum) :-
    sum(Lists, 0, Sum).

sum([], Sum, Sum).
sum([[_,N]| Lists], Sum0, Sum) :-
    Sum1 is Sum0 + N,
    sum(Lists, Sum1, Sum).

此版本使用累加器启用尾递归定义。通话示例:

| ?- sum([[a, 1], [b, 2], [c, 3], [d, 4]], Sum).

Sum = 10
yes

答案 2 :(得分:0)

每当您期望成功的目标失败时,将其视为学习的机会(逻辑赚取的简写=赚取逻辑)。毕竟,这就是Prolog,它的意思是逻辑编程。那么程序中的逻辑在哪里?

目前您的程序失败了,但是您希望它能够成功。罪魁祸首在哪里?让我们一般化您的程序,以使生成的程序仍然失败,但是要小得多。概括程序的两种简单方法:

  • 删除目标(通过添加前缀*

  • 删除条款(将term替换为_/*term*/

我们可以很盲目的做到这一点。无需了解您的程序。只需重新检查目标仍然失败即可。这是我第一次尝试时想到的:

:- op(950, fy, *).
* _G_0. % ignore the argument _G_0

sum([], _/*0*/).
sum([_/*[_,Head]*/|[_,Tail]], Sum) :-
    * sum([_,Tail], Sum2),
    * Sum is Head+Sum2.

?- sum([_/*[a, 1]*/, _/*[b, 2]*/, _/*[c, 3]*/, _/*[d, 4]*/], Total).
false.                            % gnah - still fails

一个问题必须存在于其余可见部分。太难弄清楚了吗?让Prolog通过查询最普遍的查询

向您解释
| ?- sum(Xs, Sum).
   Xs = []
;  Xs = [_A,_B,_C].

因此,只能有两个长度的列表:空列表和包含三个元素的列表。请注意,我们目前有谓词的通用版本。因此,不能保证我们会找到两种长度的解决方案。但是,我们可以100%确保所有其他长度都没有解决方案。

让我们回到原始程序并询问最普遍的查询:

?- sum(Os, Total).
   Os = [], Total = 0
;  false.

Oh no,只有一个解决方案。甚至没有sum([_|_], Total)的单一解决方案。

所以让我们再次概括该程序,但现在针对这个失败的目标:

sum([], _/*0*/).
sum([_/*[_,Head]*/|[_,Tail|_/*[]*/]], Sum) :-
    sum([_,Tail], Sum2),
    * Sum is Head+Sum2.

?- Os = [_|_], sum(Os, Total).
false.

在此部分中,必须进一步存在错误。实际上,目标sum([_,Tail], Sum2)是罪魁祸首:它是关于一列恰好两个元素的列表,但是该规则至少要包含三个

有关实际的修补程序,请参见其他答案。

此方法适用于纯单调的程序,例如您的程序。