【问题标题】:Disjunction G1 ; G2 vs. If-then-else Cond -> G1 ; G2析取 G1 ; G2 vs. If-then-else Cond -> G1 ; G2
【发布时间】:2019-10-22 09:51:17
【问题描述】:

我遇到了一个包含嵌套 if-then-else 形式的 Prolog 程序

p(X,Y) :-
     (cond1(X,Y) -> q(X)); true,
     (cond2(X,Y) -> q(Y)); true.

有意想不到的答案。此行为的原因与以下查询中的相同:

?- (true, (true -> X=a)); X=b.
X = a ;
X = b.

?- ((true -> X=a), true); X=b.
X = a ;
X = b.

?- (true -> X=a); X=b.
X = a.

第一个查询有两个答案,但第二个只有一个。不同行为的原因是什么?

P.S.:我知道其中的区别,但我还没有找到一个 SO question 来处理这个非常令人困惑的现象。那么为什么不以这种方式记录呢?

【问题讨论】:

  • 有趣的行为:?- (true -> X=a); X=b. 我得到X = a. 相反:trace, (true -> X=a); X=b. 我得到X = a ; X = b。我在网上嗖嗖嗖嗖。
  • 这很有趣。我一直认为p1 -> p2 等同于p1, !, p2。但是,(true, !, X=a), true); X = b 仅产生一种解决方案 X=a,而 ((true -> X=a), true); X=b. 产生两种解决方案。我真的很惊讶它会产生两个。
  • @damianodamiano 很有趣。就在 gprolog 中,trace, (true -> X=a); X=b. 产生两种解决方案,但 trace, (true, !, X=a) ; X=b 只产生一种 (X=a),这也是我对 -> 的预期行为。
  • Guy Coder:我希望其他人获得积分并最终添加我的解决方案。如果你愿意,我现在可以把它放进去。
  • 在这里查看相同的问题:stackoverflow.com/q/56111688/3768871

标签: if-statement syntax prolog iso-prolog


【解决方案1】:

关于析取的可追溯性。 这两个查询在结构上是不同的:

trace, (true -> X=a); X=b.

trace, ((true -> X=a); X=b).

您可以使用 write_canonical/1 来查看区别:

?- write_canonical((trace, (true -> X=a); X=b)), nl.
;(','(trace,->(true,=(A,a))),=(A,b))
true.

?- write_canonical((trace, ((true -> X=a); X=b))), nl.
','(trace,;(->(true,=(A,a)),=(A,b)))
true.

而且它们的行为也不同:

?- trace, (true -> X=a); X=b.
   Call: (9) true ? creep
   Exit: (9) true ? creep
   Call: (9) _428=a ? creep
   Exit: (9) a=a ? creep
X = a 
   Call: (9) _428=b ? creep
   Exit: (9) b=b ? creep
X = b.

[trace]  ?- trace, ((true -> X=a); X=b).
   Call: (9) true ? creep
   Exit: (9) true ? creep
   Call: (9) _706=a ? creep
   Exit: (9) a=a ? creep
X = a.

只有第二个查询会测试 (true -> X=a); X=b。

【讨论】:

  • 我也在 SWI 论坛上问过,这似乎也是我这边的用户错误 - 我按回车键继续前进,但没有输入 ; 以获得其他答案.
  • 我也用trace.进入追踪模式,分别进入查询。
【解决方案2】:

首先让我们解决初始谓词:格式建议嵌套 if-then-else,但第二个主体周围的括号将第一个 true 分组到 else 分支:

?- listing(p).
p(A, B) :-
    (   cond1(A, B)
    ->  q(A)
    ;   true,
        (   cond2(A, B)
        ->  q(B)
        )
    ;   true
    ).

这就是我们最终得到以true, ... 开头的查询的方式。这里的第二个问题是; 的使用被重载:仅当G1 不是(Cond -> Goal) 形式时,G1; G2 形式的术语才被解释为析取项。否则,它被解释为 if-then-else。让我们看看不同的情况:

  • (true, true -> X=a); X=b; 解释为析取,因为左侧的最外层函子是合取 ,。 Prolog 报告每个分支的答案替换。
  • (true -> X=a, true); X=b: 同理是析取
  • (true -> X=a); X=b: 是一个 if-then-else,因为左侧最外面的函子是 if-then 运算符 ->。 Prolog 只报告true 分支的答案替换。

有趣的是,当我们将条件放入变量时,这似乎不再起作用(在 SWI 8 上):

?- G1 = (true -> (X = a)), (G1 ; X=b).
G1 =  (true->a=a),
X = a ;
G1 =  (true->b=a),
X = b.

当我将G1 包装成call/1 时,也会发生同样的情况:

?- G1 = (true -> (X = a)), (call(G1) ; X=b).
G1 =  (true->a=a),
X = a ;
G1 =  (true->b=a),
X = b.

如果我正确阅读了前面问题的answer,那么第一个问题应该不同。

我会假设不同的跟踪行为是断点干扰了 if-then-else 的检测。我在跟踪过程中的错误是点击enter 爬行但没有意识到我上报实际答案时需要输入;

【讨论】:

  • 我不确定我是否同意;/2 过载的解释。在g1 -> g2 ; g3 的情况下,g1 -> g2 根据定义消除了g1 处的选择点。对于要调用的g3,需要有一个选择点,一旦第一个参数失败,它就会采用;/2 的第二个参数。这也是我对 (true -> X=a, true); X=b 产生 2 个结果感到惊讶的原因,正如我在问题的 cmets 中提到的那样。
  • 我从SWI Documentation for -> 中的一个神秘评论中得到了这个想法。看来该术语确实必须采用; ( -> (g1, g2), g3) 的形式。也许我错了,但不是( (true -> X=a), true )这个词的括号吗?这将使, 成为; 的子函子,这不是我们应该遵循的 if-then-else 的定义。
  • 是的,你是对的。 ((true -> X=a), true) ; X=b 不在 if-then-else 表单中。但是->/2 的行为应该有所不同,所以它应该摆脱第一个true 的选择点(或者我应该认为)。这就是为什么我很惊讶X=b 被显示为一个解决方案。
  • 始终使用listing/1 设置圆括号。
  • I would assume that the different tracing behaviour is that the breakpoints interfere with the detection of if-then-else. 如果您在SWI-Prolog discourse 提问,Jan 很有可能会回复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-08
  • 1970-01-01
  • 1970-01-01
  • 2019-01-28
  • 1970-01-01
  • 2012-03-09
相关资源
最近更新 更多