Prolog程序不会计算变量答案?

时间:2013-05-29 02:58:36

标签: list prolog increment

这应该是一个简单的解决方案,但我似乎无法解决这个问题,而且令人沮丧。我编写了一个程序,用于计算或验证两个列表是否相关,因为第二个列表的元素都是从第一个列表的元素中加1。这在给出两个列表时有效,但在需要计算列表时则不行。

代码如下:

inc([], []).
inc([X|XS],[Y|YS]) :- 
  Y =:= X+1,
  inc(XS,YS).
ERROR: =:=/2: Arguments are not sufficiently instantiated

非常感谢任何帮助!

2 个答案:

答案 0 :(得分:1)

你的问题基本上是=:=/2用于测试而不是建立绑定,尽管is/2仍然没有真正做你想要的。例如,虽然2 is 1 + 1为真,但2 is X+1不会导致X绑定到1,因为is/2期望左边只有一个变量或值右边有一个表达式,它与Prolog的其余部分没有“关系”。如果你想要以这种方式运算,你应该看看clpfd;看看它增加的复杂性是一个很好的解释为什么事情就是这样。

幸运的是,您不需要所有算术来解决您的问题。 succ/2内置函数将完全满足您的需求,并且您可以获得一个单行解决方案:

inc(X, Y) :- maplist(succ, X, Y).

使用中:

?- inc([1,2,3], [2,3,4]).
true.

?- inc([1,2,3], X).
X = [2, 3, 4].

?- inc(X, [1,2,3]).
X = [0, 1, 2].

如果您使用succ/2代替=:=/2

,您的代码也可以正常运行
inc([], []).
inc([X|XS],[Y|YS]) :- 
  succ(X, Y),
  inc(XS,YS).

这一定是您怀疑的“简单修复”。 :)

我不确定@mbratch指的是一个谓词存在“太多变量”。我怀疑这是对Prolog的误解,也许是其他语言的延续,其中一个函数可以返回一个值或一些东西。这里没有技术限制;谓词可以采用尽可能多的地面或非地面参数,并根据需要绑定尽可能多的参数;限制因素是你的创造力。

同样,我不认为“不对称”在这里是一个有意义的概念。定义仅具有单个实例化模式的谓词是很正常的,但是对于实例化而言,使谓词变得灵活也是正常的并且是可取的 - 您无法提前知道将来可能需要使用什么。您可能会认为破坏信息的实例化模式可能会排除逆实例化模式,但在实践中,您经常可以将其转换为生成器。

举一个例子,append/3的名字似乎暗示了这种模式:

 ?- append([1,2], [3,4], X).
 X = [1,2,3,4]

这是一个非常好的用途,但也是如此:

?- append(X, Y, [1,2,3,4]).

这是一种非确定性的实例化模式,将产生五种解决方案:

X = [], Y = [1,2,3,4]
X = [1], Y = [2,3,4]
X = [1,2], Y = [3,4]
X = [1,2,3], Y = [4]
X = [1,2,3,4], Y = []

这似乎与@ mbratch的一些想法相矛盾,但是在append/3的通常定义中没有对ground / nonground的明确测试,因为它没有必要,同样对于第二个调用模式你从一个输入中得到两个“返回值”。 SWI source

append([], L, L).
append([H|T], L, [H|R]) :-
    append(T, L, R).

修改:负数。我忘了succ/2仅在正整数上定义。我们可以应用@ mbratch的技术,并且仍然可以获得具有所需属性的整洁解决方案:

isucc(X, Y) :- var(X), X is Y-1.
isucc(X, Y) :- Y is X+1.

inc(X, Y) :- maplist(isucc, X, Y).

行动中:

?- inc(X, [-1,2]).
X = [-2, 1] ;
false.

编辑:使用clp(fd)(通过@mat):

fdsucc(X,Y) :- Y #= X + 1.
inc(X, Y) :- maplist(fdsucc, X, Y).

即使是最常见的查询,也会生成:

?- inc(X, Y).
X = Y, Y = [] ;
X = [_G467],
Y = [_G476],
_G467+1#=_G476 ;
X = [_G610, _G613],
Y = [_G622, _G625],
_G610+1#=_G622,
_G613+1#=_G625 ;
X = [_G753, _G756, _G759],
Y = [_G768, _G771, _G774],
_G753+1#=_G768,
_G756+1#=_G771,
_G759+1#=_G774 
...

这个实用程序是值得怀疑的,但是大概是因为你正在使用clp(fd),你最终会施加其他限制并获得有用的东西。

答案 1 :(得分:0)

inc([],[]).
inc([X|XS],[Y|YS]) :-
  nonvar(X),
  Z is X + 1,
  Y = Z,
  inc(XS,YS), !.
inc([X|XS],[Y|YS]) :-
  nonvar(Y),
  Z is Y - 1,
  X = Z,
  inc(XS,YS), !.

这里我们需要为加法得到一个真实的计算,然后用=来尝试实例化。必须对谓词进行拆分以处理X未实例化的情况,而不是Y的情况。每个末尾的!是为了防止它在找到两个相似路径中的一个之后尝试更多解决方案。