【问题标题】:Prolog predicate that will check if a list A is a list of the prefix sums of a list DProlog 谓词将检查列表 A 是否是列表 D 的前缀和的列表
【发布时间】:2017-12-12 21:34:48
【问题描述】:

所以我对 prolog 很陌生,当整数列表 D 是列表 A 的前缀和列表时,我必须编写一个可满足的谓词。

sums(A, D)

例如,

sums([4,11,1,-3,8], [4,15,16,13,21]) is satisfiable

我已经用十几种不同的方式编写了这个谓词,但都无济于事。这是我目前写的。

sums([], []).
sums([A], [A]).
sums([A|[B|C]], [A|[E|F]]) :- TOTAL is A + B, E = TOTAL, sums([C], [F]).

这有点工作,因为它将检查每个列表的第一个值是否相等,并检查列表中的第二个元素是否正确,因为它应该是 15。我理解它为什么以这种方式不正确地工作,但我无法想出如何以不同的方式以正确的方式编写它。

我已将代码更改为,

sumrunner(L, S) :- sumrunner(L, S, 0).
sumrunner([], [], _).
sumrunner([A], [A], _).
sumrunner([A|B], [C|D], TOTAL) :- TOTAL is TOTAL + A, TOTAL = C,sumrunner(B, D, TOTAL).

但是,现在它只对所有情况都说 false,除非两个列表为空,并且两个列表都包含一个元素并且它们彼此相等。

【问题讨论】:

  • 你的意思是 predicate 不是 functor (我编辑了你的问题)。如果您编写了术语foo(a, b),那么foo 是一个称为函子 的术语,具有两个参数 abfunctor 只是指foo 部分,它必须是一个atom。但它作为 functor 没有其他指定用途。 谓词使用一个函子作为它的head来实现一个rulefunctor 也可用于声明事实或其他结构数据。网上有几个不同的Prolog glossaries
  • @lurker。您链接到的词汇表质量有些低。考虑函子的条目:“函子是目标或查询的一部分,位于 '(' 之前。函子必须遵守与原子完全相同的命名规则。” OTOH 查看this part of the SICStus Prolog documentation ... 完全不同,你不同意吗?
  • @repeat 是的,事后看来我同意并且对此并不完全满意,但必须从某个地方开始。老实说,我找不到两个匹配的 Prolog 词汇表。 “官方 Prolog 词汇表”没有确定的位置。信不信由你,我发现了一些保真度甚至低于那个的。
  • @lurker。无论如何thx 4让球滚动! “权威”词汇表 (ISO/IEC 13211-1:1995) 不是免费提供的;(但好消息是:SICStus Prolog 4.3.5 manual (PDF, 1458 pages) 无需注册即可免费下载。即使您不注册也可以查看使用 SICStus Prolog——但是;)太好了!
  • 非常感谢@repeat!我将下载免费手册。ISO 文档有点贵。

标签: list prolog clpfd


【解决方案1】:

您应该了解有关列表表示法的更多信息:例如,[A|[B|C]] 可以写为 [A,B|C]。现在更清楚C 是列表的尾部,因此,它本身就是一个列表!因此,当您编写sums([C], [F]) 时,您将CF 包装到一个列表中,即使它们已经是列表,这是您的问题。

如果我们解决这个问题并运行您的谓词,我们会得到:

?- sums([4,11,1,-3,8],Z).
Z = [4, 15, 1, -2, 8] 

如您所见,它仍然是错误的。主要问题是,第三条规则中的递归调用sums表示列表尾部的前缀和是列表前缀和的尾部,这是错误的,因为这些前缀和依赖于前一个元素!

要解决这个问题,您需要引入一个额外的参数来在整个递归调用中保持总和值:

:- use_module(library(clpfd)).

prefix_sums(L, D) :-
    prefix_sums(L, 0, D).

prefix_sums([], _, []).
prefix_sums([H|T], S, [S1|T2]) :-
    S1 #= H + S,
    prefix_sums(T, S1, T2).

使用library(clpfd),我们得到了我们期望的行为:

?- prefix_sums([4,11,1,-3,8],Z).
Z = [4, 15, 16, 13, 21].

还有相反的行为:

?- prefix_sums(Z, [4,15,16,13,21]).
Z = [4, 11, 1, -3, 8].

还可以用更少的信息纠正行为:

?- prefix_sums([A,B,C],Z).
Z = [A, _7964, _7970],
B+A#=_7964,
C+_7964#=_7970.

?- prefix_sums(X,Z).
X = Z, Z = [] ;
X = Z, Z = [_7122],
_7122 in inf..sup ;
X = [_7452, _7458],
Z = [_7452, _7482],
_7458+_7452#=_7482 ;
X = [_7770, _7776, _7782],
Z = [_7770, _7806, _7812],
_7776+_7770#=_7806,
_7782+_7806#=_7812 ;
…

【讨论】:

  • 好的,谢谢你为我澄清了。此后,我已将代码更改为上面在我的问题中编辑的内容,但我仍然没有收到正确的输出。
  • @Frank 你的第三条规则应该声明C is TOTAL + A, sumrunner(B, D, C)。你不能像你一样用另一个值重新分配TOTAL,变量是不可变的。另外,删除事实sumrunner([A], [A], _).,它会导致错误的结果,因为它过早停止递归。
【解决方案2】:

你的代码必须简化很多:

sums(L, S) :- sumrunner(L, S, 0).
sumrunner([], [], _).
sumrunner([A|B], [C|D], TOTAL) :- C is TOTAL + A, sumrunner(B, D, C).

?- sums([4,11,1,-3,8], [4,15,16,13,21]).
true.

?- sums([4,11,1,-3,8], [4,15,16,14,21]).
false.

表达式C is TOTAL + A 既检查需求又更新递归步骤的累加器。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-23
    • 1970-01-01
    • 1970-01-01
    • 2017-08-14
    相关资源
    最近更新 更多