一个atom / 1谓词在prolog中是如何工作的?

时间:2017-10-10 07:15:38

标签: prolog path-finding cyclic-graph

我一直在尝试解决Prolog中的寻路问题。谓词是

edge(a,b).
edge(a,c).
edge(b,d).
edge(c,d).
edge(d,e).
edge(d,f).
edge(f,g).

规则是
edge(X,Y) :- edge(X,Z), edge(Z,Y).

然后当我编译并运行查询时 | ?- edge(a,X)。 它正在显示 Fatal Error: local stack overflow (size: 8192 Kb, environment variable used: LOCALSZ) 然后我搜索了解决方案,发现在我们的规则中包含atom(x).atom(y).可以解决堆栈溢出问题。即新规则是

edge(X,Y) :- atom(X), atom(Y), edge(X,,Z), edge(Z,Y).是的,它确实解决了堆栈溢出问题。但是,我想知道这是怎么回事(atom / 1) 谓词在这里解决了我的问题?它对我们的变量X,Y有什么作用来解决StackOverflow问题? 我是Prolog的新手任何帮助将不胜感激 谢谢。 :)

1 个答案:

答案 0 :(得分:2)

首先在命名时,edge/2名称不能很好地描述您的谓词。你可能真的想要path/2,它由一个或多个边组成。

atom/1真的可以解决您的问题吗?换句话说,edge(X, Y)现在真的为查询提供了所有正确的解决方案吗? atom/1所做的就是确保它的参数是一个原子,所以它不能是一个未绑定的变量。所以edge(X, Y)没有提供所有正确的解决方案。它只产生那些你有直接事实的解决方案,因为当前定义的谓词edge(X, Y)总是失败,XY未绑定。

| ?- edge(a, Y).

Y = b ? ;

Y = c ? ;

no

解决方案Y = d在哪里? edge(X, Y)仅提取edge/2个事实中提供的解决方案,但没有包含多个连接边的解决方案。

您的原始问题是由于无限递归造成的,这是edge/2不必要地调用自身的结果。命名在这里实际上很重要,因为它使逻辑更精确和正确。我们可以说edge(X, Y)表示XY形成边缘XY直接相连)。我们可以说path(X, Y)表示从XY通过一个或多个边缘存在路径。换句话说,从x到y的路径可以是从x到y的边缘,也可以是从x到y的边缘 z和从z到y的路径

path(X, Y) :- edge(X, Y).
path(X, Y) :- edge(X, Z), path(Z, Y).

现在我们得到:

| ?- path(a, X).

X = b ? a

X = c

X = d

X = e

X = f

X = g

X = d

X = e

X = f

X = g

(1 ms) no
| ?-

有重复项,因为可能有多种方式从a转到e。如果你包含一个显示遍历路径的参数,那么这将变得很明显。

然而,这个解决方案不是故事的结尾。您当前的事实是没有“迂回”的路径(如果遵循,最终会重新访问同一节点的路径)。要处理这个问题,你需要一个谓词参数来跟踪你已经遍历过的节点/边缘,并避免再次遍历它们。