循环一次后如何防止递归

时间:2017-03-21 22:24:56

标签: loops recursion prolog prolog-cut

我刚才意识到这是一个愚蠢的问题。好奇,如果有人仍然可以找到一个漏洞。

源代码:

married(trump,obama).
married(trump,goat).
married(pepee,pepper).
married(X,Y) :- married(Y,X),!. % not awesome because of infinite recursion

Goal: ex. married(trump, putin).
trace(
first base case fails.
second base case fails.
third base case fails.
married(trump,putin) = married(putin,trump),!.

我想要它做的是再次尝试结婚(putin,trump),但所有早期的基础案例将再次失败。我们之前尝试过切换args并失败了。所以不要递归。只是返回false。

我得到了一个堆栈错误,因为直到结婚(putin,trump)或其他方式之前!将永远不会返回true或false,因此无法触发切割。

更简单,更理智的方法是重写代码以防止递归。我很好奇是否有办法尝试切换args一次,如果失败则返回失败。如果你有很长的事实列表,如果你可以尝试arg1,arg2,你可以将那个长列表减少一半,反之亦然。如果我们得到疯狂的排列场景,那么可能会呈指数级增长。

任何见解都会非常棒。

1 个答案:

答案 0 :(得分:4)

你正走在正确的轨道上“切换args一次,如果失败则返回失败”,即使这个措辞非常命令并且不包括我们期望从这种关系中获得的所有模式。 / p>

为此,您需要将其分为两个谓词。很容易证明具有给定接口的单个​​谓词是不够的。

首先,辅助谓词

married_(a, b).
married_(c, d).
etc.

然后,主谓词,基本上就像你建议的那样:

married(X, Y) :- married_(X, Y).
married(X, Y) :- married_(Y, X).

在你的解决方案中添加杂质会让事情变得更糟:几乎无一例外,你会破坏你们关系的普遍性,提出你为什么要使用声明性语言的问题。

示例查询:

?- married(X, Y).
X = a,
Y = b ;
X = c,
Y = d ;
X = b,
Y = a ;
X = d,
Y = c.

严格地说,你当然也可以只使用单个谓词来做到这一点,但如果你这样做,你需要携带额外的信息。

例如:

married(_, a, b).
married(_, c, d).
married(first, X, Y) :- married(second, Y, X).

示例查询:

?- married(_, X, Y).
X = a,
Y = b ;
X = c,
Y = d ;
X = b,
Y = a ;
X = d,
Y = c.

这紧跟你所描述的方法:“我们之前尝试过切换args。所以不要再做了。”