我是Prolog的初学者,我想问一个关于Prolog的问题。
我的程序基于非确定性有限状态自动机。
开始状态为 S0 ,最终状态为 S3 。
图表是
所以,如果有一个字符串[a,a,b,b,c,c]
,它应该像
start(s0).
edge(a, s0, s0).
edge(a, s0, s1).
edge(b, s1, s1).
edge(b, s1, s2).
edge(c, s2, s2).
edge(c, s2, s3).
final(s3).
有一个谓词accepts(Ls)
if(Ls
是一个字符串列表)
accepts(Ls) :- start(A), goesTo(Ls, A, B), final(B).
并假设NFA从状态 Si 变为状态 Sj ,并且在它们之间存在状态 Sk ,{{1}谓词定义为
goesTo
但是如果查询goesTo(Ls, Si, Sj) :- edge (L, Si, Sk), goesTo(Ls, Sk, Sj).
(任意字符串列表范围从accepts(Ls)
到a
)
教程问题说它几乎肯定会进入无限搜索并且会发生堆栈溢出。
但是,我不明白为什么查询将进入无限搜索并导致堆栈溢出。如果你能给我理由,那真的很棒!
(编辑:) 确切的引用是:
“一个典型的Prolog用户可能希望他/她的goTo规则会如此严格,查询接受(X)会产生上面NFA接受的连续字符串。几乎可以肯定,鉴于给定NFA的上述表示, Prolog系统将进入无限搜索,并且会发生堆栈溢出。说出为什么会这样。(如果你要去避免这个问题,请说明你是如何设法避免它的。)
答案 0 :(得分:2)
这是您的NFA的定义:
start(s0).
edge(a, s0, s0).
edge(a, s0, s1).
edge(b, s1, s1).
edge(b, s1, s2).
edge(c, s2, s2).
edge(c, s2, s3).
final(s3).
它与您可能测试的任何输入字符串没有任何关联。注意每行末尾的点 - 这些是定义NFA的Prolog谓词。
使用任何 具体输入字符串运行accepts/1
将导致有限递归。这里的搜索空间是有限的,如果你用完全实例化的有限长度字符串调用accepts/1
,它肯定会用尽。
现在,如果您尝试生成所有可能的路径可接受的字符串,那么您将拥有无限递归,因为有无限数量的可接受字符串:
a,b,c
a,a,b,c
a,a,a,b,c
a,a,a,a,b,c
.....
是此自动机可接受的所有字符串。
你的谓词定义都错过了最后一个点BTW。 goesTo
并不完全正确。必须更改为:
goesTo([], S, S).
goesTo([L1|Ls], S1, Sn) :- edge(L1, S1, S2), goesTo(Ls, S2, Sn).
accepts(Ls) :- start(A), goesTo(Ls, A, B), final(B).
另请注意,谓词名称和左括号之间必须没有空格。
所以现在OP已经澄清了他们的问题。是的,
为什么尝试生成所有可能接受的字符串几乎肯定会进入非生产性循环?
调用accepts(X)
确实进入无限递归,因为尝试生成新节点从头开始重新启动,因此在内部尝试了一个无限增长的a
字符串:
a
a,a
a,a,a
a,a,a,a
....
只是因为edge(a,s0,s0)
是数据库中的第一个edge
事实,对edge/3
的调用位于goesTo/3
谓词定义中的第一个位置。而Prolog的搜索策略是从左到右。
通过重新安排目标,我们可以从完全没有生产力的行为(Prolog只是无限循环)中获得生产行为:
start(s0).
edge(a, s0, s1).
edge(b, s1, s2).
edge(c, s2, s3).
edge(a, s0, s0).
edge(b, s1, s1).
edge(c, s2, s2).
final(s3).
现在,
12 ?- accepts(X).
X = [a, b, c] ;
X = [a, b, c, c] ;
X = [a, b, c, c, c] ;
X = [a, b, c, c, c, c] ;
X = [a, b, c, c, c, c, c] ;
X = [a, b, c, c, c, c, c, c] ;
X = [a, b, c, c, c, c, c, c, c]
不幸的是,可以看出这一代人偏向c
。如何让它“公平”......让我们把它留给另一个问题,好吗?
答案 1 :(得分:1)
由于Prolog采用的搜索策略,该程序将循环。它尝试使用 depth first 策略解决探索解决方案空间的问题,该策略节省空间但不完整。
现在应该清楚解决方案空间在您的情况下无限,因为图中的这些循环。从初始状态到最终状态都有无限的路径。
Iterative deepening应该是枚举路径的简单方法,在Prolog中很容易实现。另一种可能性是实施 breadth first 搜索。
答案 2 :(得分:1)
因为它是其他人所说的循环。
p.s这项任务已延长至周四,因此无需恐慌[尚] 是的,我知道你正在做什么工作