如何在Prolog中模拟软切?



如何模拟软切I *-> T;ISO Prolog中的E ?I有副作用,所以我可以多次调用它。

除了最后一个要求,我认为下面的定义是有效的:

if_(I, T, E) :-
    not(not(I)) ->
    call((I, T));
    call((not(I), E)).

(我实际上使用XSB prolog;XSB的解决方案对我也很有用)

是的,我们可以在ISO Prolog甚至XSB中实现这一点,但不是很有效。为了提高效率,你需要进行一些"选择性切割"。此外,XSB不实现符合ISO标准的整数,因此必须单独处理溢出。

:- dynamic(if_counter/1).
if_counter(0).
:- dynamic(no_if_answer/1).
if(If_0, Then_0, Else_0) :-
   once(if_counter(Id)),
   Idx is Id+1,
   (  Idx > Id -> true
   ;  throw(error(representation_error(max_integer),
               'XSB misses ISO conforming integers'))
   ),
   retractall(if_counter(_)),
   asserta(if_counter(Idx)),
   asserta(no_if_answer(Id)),
   (  If_0,
      retractall(no_if_answer(Id)),
      Then_0
   ;  retract(no_if_answer(Id)) ->
      Else_0
   ).

低效率的主要来源是对于一个确定的条件If_0,仍然有一个选择点。这是可以想象的几乎不可想象的实现可以得出retract(no_if_answer(Id))总是失败的结论,一旦retractall(no_if_answer(Id))被执行,但我怀疑实现者会在这样的优化上投资。编辑:这看起来极不可能的原因是,实现必须保证断言的数字总是上升。

注意,软切割产生不完整性的方式与切割相同。考虑:

| ?- if(X = a, T = equal, T = not_equal).
X = a
T = equal;
no

这显然漏掉了一个答案!要了解原因,以X = b:

为例
| ?- X = b, if(X = a, T = equal, T = not_equal).
X = b
T = not_equal;
no
| ?- if(X = a, T = equal, T = not_equal), X = b.
no % bad!!

合取应该是可交换的(模不终止、误差、副作用)。

如果您对声明性合理的条件语句感兴趣,这些条件语句也非常有效,并且通常比不纯粹的条件语句更快,请考虑if_/3。参考library(reif)给出所有正确答案的SICStus:

| ?- if_(X = a, T = equal, T = not_equal).
X = a,
T = equal ? ;
T = not_equal,
prolog:dif(X,a) ? ;
no

好吧,让我们来点创意吧…本质上,您需要一种方法来记住(通过回溯)if条件至少有一个解。动态谓词对我来说是禁忌,但还有其他选择吗?那么,ISO-Prolog定义了一种匿名对象,流项,它可以(ab)用于以这种相当优雅的方式实现不可回溯标志:

if(If, Then, Else) :-
    open(., read, S),
    (
        If,
        close(S, [force(true)]),
        Then
    ;
        catch(close(S), error(existence_error(stream,_),_), fail),   % fail if already closed
        Else
    ).

我们关闭流来表示If有一个解决方案,然后这被else分支中的close尝试检测到。这在像ECLiPSe这样的系统中完美地工作并且没有泄漏。然而,许多系统(包括XSB)重用关闭流的标识符(这没有被ISO禁止),使得该解决方案不可移植。

但是等等,流有一个position属性,它可以设置,并且在回溯中保持其值!使用这个技巧,下面的代码可以在XSB上运行:

if(If, Then, Else) :-
    % open('ReadableAndNonemptyFile', read, S),      % general ISO
    open(atom(a), read, S),                          % XSB (needs no file)
    stream_property(S, position(Zero)),
    get_char(S, _),
    (
        catch(If, Ball, (close(S),throw(Ball))),
        set_stream_position(S, Zero),
        Then
    ; stream_property(S, position(Zero)) ->
        close(S),
        fail
    ;
        close(S),
        Else
    ).

遗憾的是,open(atom(...),...)特性是特定于xsb的,对于严格的ISO-Prolog,您需要一个虚拟文件…

问题是,软切应该是相当聪明的,它不应该当它的参数不留下一个选择点时,留下一个选择点。

在SWI-Prolog中没有选择点:

   Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.4)
   ?- X=1 *-> Y=1; true.
   X = Y, Y = 1.
   ?- 

Jekejeke Prolog中没有选择点:

   Jekejeke Prolog 3, Runtime Library 1.3.6
   ?- X=1 *-> Y=1; true.
   X = 1,
   Y = 1
   ?- 

到目前为止,这里还没有一个公认的创造性解决方案将其归档,因此它们都不能有效地取代本机实现。

Jekejeke Prolog进行决定论检查,然后移除分离选择点。否则,标记为分离选择点。从模块"logic":

:- set_predicate_property(;/2, sys_nobarrier).
A *-> B; C :- sys_local_cut, sys_soft_cond(A, B, C).
:- set_predicate_property(sys_soft_cond/3, sys_nobarrier).
sys_soft_cond(A, B, _) :- sys_safe(A), sys_soft_local_cut, B.                        
sys_soft_cond(_, _, C) :- C. 

您的定义没有实现软切语义:当测试成功时,您可以回溯到它。这是一个有用的控制结构(例如,我用它来实现Logtalk中的协同感应),但不幸的是,不能在Prolog级别以可移植的方式实现,当然在ISO Prolog标准的限制范围内。好消息是越来越多的Prolog系统实现了这种控制结构。它们包括(不分先后)SWI-Prolog、YAP、SICStus Prolog、GNU Prolog、CxProlog、ECLiPSe、Jekejeke Prolog和Ciao。但是,请注意,虽然有些系统使用*->/2操作符,但少数系统(SICStus Prolog和Ciao)使用if/3谓词(YAP两者都有)。此外,语义在极端情况下也会变化(Logtalk发行版包括一个Prolog一致性套件,它也会检查*->/2变体)。

相关内容

  • 没有找到相关文章

最新更新