在Prolog中编程时,我经常编写谓词,当所有参数被实例化时调用其行为应该是半确定性的(否则其行为应该是非确定性的)。
这个的具体用例是我的谓词walk/3
,它实现了图形遍历。由于两个顶点之间可以存在多个路径,因此实例化(+,+)
会在true
之后提供多个选择点。然而,这些都是无用的。出于性能原因,调用代码必须明确使用once/1
。
%! walk(+Graph:ugraph, +StartVertex, +EndVertex) is semidet.
%! walk(+Graph:ugraph, -StartVertex, +EndVertex) is nondet.
%! walk(+Graph:ugraph, +StartVertex, -EndVertex) is nondet.
%! walk(+Graph:ugraph, -StartVertex, -EndVertex) is nondet.
半决定论可以通过在调用上下文中使用once/1
来强制,但我想将半决定论作为谓词walk/3
的属性来实现,而不是作为必须的东西每次被召唤时都要特别对待。
除了对代码美学的关注之外,调用上下文不必总是知道它对walk/3
的调用是否是半确定性的。例如:
%! cycle(+Graph:ugraph, +Vertex) is semidet.
%! cycle(+Graph:ugraph, -Vertex) is nondet.
cycle(Graph, Vertex):-
walk(Graph, Vertex, Vertex).
我已经提出了以下解决方案,它可以产生正确的行为。
walk_wrapper(Graph, Start, End):-
call_ground_as_semidet(walk(Graph, Start, End)).
:- meta_predicate(call_ground_as_semidet(0)).
call_ground_as_semidet(Goal):-
ground(Goal), !,
Goal, !.
call_ground_as_semidet(Goal):-
Goal.
然而,这种解决方案存在不足之处:
ground
应为nonvar
。我的问题是:是否还有其他方式可以在Prolog中对通常出现的(非)确定性模式(如此处描述的模式)进行一般/高效/风格编程?
答案 0 :(得分:2)
您应该尝试将双重否定视为失败。是的,基本目标只能是对或错,因此不应留下任何选择点。让我们假设有一个非循环图,以使事情变得简单:
如果我使用此代码:
edge(a, b). edge(a, c).
edge(a, d). edge(b, c).
edge(c, d). edge(c, e).
edge(d, e).
path(X,X).
path(X,Y) :- edge(X,Z), path(Z,Y).
Prolog系统现在将为封闭查询保留选择点:
?- path(a, e).
true ;
true ;
true ;
true ;
true ;
false.
我认为推荐的方法是消除这些 选择点,尽管如此,但谓词却多种多样, 是在Prolog中使用所谓的元编程。
元编程有时也被贬义为 非逻辑程序设计,因为它基于非逻辑程序 谓词,例如ground / 1,!/ 0或(+)/ 1。但是让我们打电话 在不影响声明性的情况下进行元编程。
您可以按以下方式编写包装器smart / 1, 与您的call_ground_as_semidet / 1相同,但有一点细微差别:
smart(G) :- ground(G), !, \+ \+ G.
smart(G) :- G.
Prolog系统将不再为封闭查询留下选择点:
?- smart(path(a,e)).
true.
\ + \ +超过一次的优点是,前者确实 不仅不留下任何选择点,而且消除了足迹。它 有时称为Prolog的垃圾收集元谓词。
答案 1 :(得分:0)
不是答案,但评论太长了。请记住,我不确定我完全理解,所以我想先重新陈述你的问题。
以图表为例。您希望能够使用相同谓词的相同调用来询问以下问题。
给出图表,
问题1 :顶点B是否可以从顶点A到达(不知何故)? - 是或否
问题2 :哪些顶点可以从A到达? - 通过回溯枚举
问题3 :从哪个顶点可以到达? - 通过回溯枚举
问题4 :哪个A和B存在哪个B可从A到达? - 通过回溯枚举
我可能在这里错了,但似乎回答问题1和问题2可能采用不同的搜索策略而不是回答问题3?
更一般地说,你想要说一句:如果我有一个是或否的问题,成功或失败。否则,列举答案。
我遇到了麻烦:你将如何处理两种不同的类型的答案?有哪些情况你事先不知道你需要哪种答案? (如果你事先知道,你可以使用once(goal)
,就像你自己说的那样。)
PS:
显然有setof/3
,如果没有答案则会失败,或收集所有答案。在某些情况下,您想知道某些的答案,但您不想收集所有答案吗?由于答案的大小和数量,这是一个效率问题吗?
答案 2 :(得分:0)
不是答案,而是建议。
也许我错过了你的问题。我认为您试图通过强制谓词不确定来解决性能问题。这个问题毫无意义:如果p(X)
是非确定性的(多个解决方案),则p(X),!
是确定性的(仅限第一个解决方案)。
您不应该通过更改程序逻辑或谓词可逆性来解决性能问题。我建议采用不同的方法:
首先,利用prolog索引。例如:
cycle(+Graph:ugraph, +Vertex)
与(性能方面)不同:
cycle(+Vertex, +Graph:ugraph)
您应该在网上找到关于prolog索引(和性能影响)的文档。
其次,针对同一问题编写多个实现。每一个都将针对不同的案例优化性能。然后,编写一个谓词,为每个案例选择最佳实现。