我一直在尝试解决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的新手任何帮助将不胜感激
谢谢。 :)
答案 0 :(得分:2)
首先在命名时,edge/2
名称不能很好地描述您的谓词。你可能真的想要path/2
,它由一个或多个边组成。
atom/1
真的可以解决您的问题吗?换句话说,edge(X, Y)
现在真的为查询提供了所有正确的解决方案吗? atom/1
所做的就是确保它的参数是一个原子,所以它不能是一个未绑定的变量。所以edge(X, Y)
没有提供所有正确的解决方案。它只产生那些你有直接事实的解决方案,因为当前定义的谓词edge(X, Y)
总是失败,X
或Y
未绑定。
| ?- edge(a, Y).
Y = b ? ;
Y = c ? ;
no
解决方案Y = d
在哪里? edge(X, Y)
仅提取edge/2
个事实中提供的解决方案,但没有包含多个连接边的解决方案。
您的原始问题是由于无限递归造成的,这是edge/2
不必要地调用自身的结果。命名在这里实际上很重要,因为它使逻辑更精确和正确。我们可以说edge(X, Y)
表示X
和Y
形成边缘(X
和Y
直接相连)。我们可以说path(X, Y)
表示从X
到Y
通过一个或多个边缘存在路径。换句话说,从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
。如果你包含一个显示遍历路径的参数,那么这将变得很明显。
然而,这个解决方案不是故事的结尾。您当前的事实是没有“迂回”的路径(如果遵循,最终会重新访问同一节点的路径)。要处理这个问题,你需要一个谓词参数来跟踪你已经遍历过的节点/边缘,并避免再次遍历它们。