【问题标题】:Is there a way to sum only the integers in a list of pairs that contain a letter and an integer in Prolog?有没有办法只对 Prolog 中包含字母和整数的对列表中的整数求和?
【发布时间】:2019-04-22 21:53:16
【问题描述】:

我无法弄清楚如何找到一对列表中的整数之和,如下所示:

[[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。

【问题讨论】:

  • sum([[_,Head]|[_,Tail]], Sum) -> 这表示它只匹配一个包含两个项目的列表,没有别的。整个尾部必须是一个名称。这个问题是昨天提出的,这里有一个答案:stackoverflow.com/questions/55776612/…如果答案是你要找的。​​span>
  • 回答者是对的:这是一个很好的学习机会。但简单的答案是:当你指的是 Tail 时,你说 [_,Tail]。列表的其余部分不是 2 项称为 _ 和 Tail,而是 3 项,最好统称为 Tail。

标签: list prolog


【解决方案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】:

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

    1. 为每个子列表的第二项创建一个新列表;和
    2. 总结一下这份清单。

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

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

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

    或者我们可以使用maplist/3 [swi-doc]nth1/3 [swi-doc]

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

    或者我们可以将item2list写成findall/3 [swi-doc]member/2 [swi-doc]

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

    虽然这里的谓词不是双向的。

    例如:

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

    我将总结该列表作为练习。

    【讨论】:

    • findall/3 + member/2 将为您的 item2list/2 谓词提供明显更快的实现
    • 但是findall/3 会混淆约束。 maplist/3 解决方案没有这个问题。
    【解决方案3】:

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

    目前您的程序失败了,但您预计它会成功。罪魁祸首在哪里?让我们概括您的程序,以使生成的程序仍然失败,但要小得多。泛化程序有两种简单的方法:

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

    • 删除条款(将term替换为<b>_<s>/*term*/</s></b>

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

    :- op(950, fy, *)。 * _G_0。 % 忽略参数_G_0 总和([],_/*0*/)。 sum([_/*[_,Head]*/|[_,Tail]], Sum) :- * sum([_,Tail], Sum2), * 总和是 Head+Sum2。 ?- sum([_/*[a, 1]*/, _/*[b, 2]*/ , _/*[c, 3]*/, _/*[d, 4]*/ ],总计)。 错误的。 % gnah - 仍然失败

    一个问题必须在剩余的可见部分。太难弄清楚了?让 Prolog 通过查询最通用的查询向您解释:

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

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

    让我们回到原来的程序,问最一般的查询:

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

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

    所以让我们再次概括一下这个程序,但现在关于这个失败的目标:

    总和([],_/*0*/)。 sum([_/*[_,头]*/|[_,尾|_/*[]*/ ]],总和):- 总和([_,尾巴],Sum2), * 总和是 Head+Sum2。 ?- Os = [_|_], sum(Os, Total)。 错误的。

    在这部分中一定还有一个错误。事实上,目标sum([_,Tail], Sum2) 是罪魁祸首:它是关于正好包含两个元素的列表,但规则至少需要三个

    有关实际修复,请参阅其他答案。

    此方法适用于像您这样的纯单调程序。

    【讨论】:

      猜你喜欢
      • 2020-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多