Coding style 在Prolog中实现常见的决定论模式

Coding style 在Prolog中实现常见的决定论模式,coding-style,prolog,deterministic,Coding Style,Prolog,Deterministic,在Prolog中编程时,我经常编写谓词,当所有参数都实例化时,其行为应该是半确定性的(否则其行为应该是非确定性的) 这方面的一个具体用例是我的谓词walk/3,它实现了图形漫游。由于两个顶点之间可能存在多条路径,因此实例化(+,+)会在true之后提供多个选择点。然而,这些都是毫无用处的。出于性能原因,调用代码必须显式使用一次/1 %! walk(+Graph:ugraph, +StartVertex, +EndVertex) is semidet. %! walk(+Graph:ugraph,

在Prolog中编程时,我经常编写谓词,当所有参数都实例化时,其行为应该是半确定性的(否则其行为应该是非确定性的)

这方面的一个具体用例是我的谓词
walk/3
,它实现了图形漫游。由于两个顶点之间可能存在多条路径,因此实例化
(+,+)
会在
true
之后提供多个选择点。然而,这些都是毫无用处的。出于性能原因,调用代码必须显式使用
一次/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.
半决定论可以通过在调用上下文中使用
one/1
来强制实现,但我想将半决定论实现为谓词
walk/3
的一个属性,而不是每次调用它时都必须特别处理的东西

除了对代码美学的关注之外,调用上下文并不总是需要知道它对
walk/3
的调用是否是半确定性的。例如:

%! cycle(+Graph:ugraph, +Vertex) is semidet.
%! cycle(+Graph:ugraph, -Vertex) is nondet.

cycle(Graph, Vertex):-
  walk(Graph, Vertex, Vertex).
cycle(+Graph:ugraph, +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中通用/有效/风格地进行编程?

不是答案,而是太长,无法发表评论。请记住,我不确定我是否完全理解,所以我想首先重新陈述你的问题

以图形为例。您希望能够使用相同谓词的相同调用来问以下问题

给出一个图表

问题1:顶点B是否可以从顶点A(以某种方式)到达是或否

问题2:哪些顶点可以从通过回溯进行枚举

问题3:B可以从哪个顶点到达通过回溯进行枚举

问题4:哪个A和B存在,哪个B可以从A访问通过回溯进行枚举

我在这里可能错了,但似乎回答问题1和问题2可能会采用与回答问题3不同的搜索策略

更一般地说,你想有一种说法:如果我有一个是或否的问题,成功或失败。否则,请列举答案

我的问题来了:你打算如何处理这两种不同类型的答案?在什么情况下,你事先不知道你需要什么样的答案?(如果您事先知道,您可以使用
一次(目标)
,正如您自己所说的。)

附言:
显然有
setof/3
,如果没有答案,或收集所有答案,则会失败。是否存在您想知道一些答案但不想收集所有答案的情况?这是因为答案的大小和数量而引起的效率问题吗?

不是答案,而是建议。 也许我误解了你的问题。我认为您试图通过强制谓词为非确定性来解决性能问题。这个问题毫无意义:如果
p(X)
是非确定性的(多个解),那么
p(X)是确定性的(仅第一个解决方案)

您不应该通过改变程序逻辑或谓词可逆性来解决性能问题。我建议采取不同的办法:

首先,利用prolog索引。例如:

%! cycle(+Graph:ugraph, +Vertex) is semidet.
%! cycle(+Graph:ugraph, -Vertex) is nondet.

cycle(Graph, Vertex):-
  walk(Graph, Vertex, Vertex).
cycle(+Graph:ugraph, +Vertex)
(在性能方面)不同于:

您应该在Web上找到有关prolog索引(和性能影响)的文档


其次,为同一个问题编写多个实现。每一个都将针对不同的情况优化性能。然后,编写一个谓词,为每种情况选择最佳的实现。

您应该尝试使用双重否定作为失败。是的,地面目标只能是对的或错的,所以它不应该留下任何选择点。假设我们有一个非循环图,让事情变得简单:

如果我使用此代码:

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.
?- smart(path(a,e)).
true.
在我看来,建议的方法是消除这些 选择点,但有一个多模式谓词, 就是在Prolog中使用所谓的元编程

元编程有时也被称为减损编程 非逻辑编程,因为它基于非逻辑 谓词,如ground/1、/0或(+)/1。但是让我们打电话 当声明性不受影响时,它将支持元编程

您可以编写一个包装器smart/1,如下所示 与您的call\u ground\u as\u semidet/1相同,但有一点细微差别:

smart(G) :- ground(G), !, \+ \+ G.
smart(G) :- G.
Prolog系统将不再为关闭的查询留下选择点:

?- path(a, e).
true ;
true ;
true ;
true ;
true ;
false.
?- smart(path(a,e)).
true.
\++的优点在于前者确实如此 不仅没有留下选择点,而且还清除了痕迹。信息技术
有时被称为Prolog的垃圾收集元谓词。

只是为了确保我理解您的问题,举一个简单的例子:您想要一个谓词,当第一个参数为ground时,它的行为将是
memberchk/2
,当第一个参数为ground时,它的行为将是
member/2
?您是否已经选中了pack?@Boris:library(xpath)它是一个很好的应用环境的候选者,在这个应用环境中,效率和控制是不同的
memberchk/2
member/2
都是ISO。但至少,是序言的一部分。@capelical:memberchk/2没有声明意义:
memberchk(a,Xs),Xs=[b,a]。
失败,但
memberchk(a,Xs),Xs=[b,a]。