【问题标题】:swi prolog: conjunction and cutswi prolog:连接和切割
【发布时间】:2018-08-01 23:59:47
【问题描述】:

我正在尝试在 java 中实现 prolog 解释器。我试图弄清楚“,”运算符应该如何工作。我试图实现这样的等效规则:

and(A, B) :- A, B.

我正在使用测试用例 c1、c2 和 c3 基于以下逻辑库测试我的实现。他们都应该输出'1'和'false'。然而,我注意到最后一条规则 (c3) 打印出“12”和“假”。我在 SWI prolog 中运行了相同的测试,最后一条规则输出 '12' 和 'false'。

所以我的假设是不正确的,逗号运算符可以编码为','(X, Y) :- X, Y.

n(1).
n(2).
and(X, Y) :- X, Y. % this is used to compare with the built in operator ','

c1 :-     n(X),     write(X),     =(1, X),     !, fail.
c2 :- ','(n(X), ','(write(X), ','(=(1, X), ','(!, fail)))).
c3 :- and(n(X), and(write(X), and(=(1, X), and(!, fail)))).

【问题讨论】:

  • 剪切在调用它的谓词子句的上下文中应用修剪回溯。所以and(!, fail) 将在调用它的地方失败并回溯。而','(!, fail)!, fail 相同则不会回溯。所以and(!, fail) 的行为与!, fail 不一样。你必须写and(!, fail), !
  • @lurker IOW 这更多是关于 cut 的语义而不是 and 的?
  • @DanielLyons 是的,绝对的。
  • @lurker,感谢您的回答。我需要咀嚼一段时间。我觉得难以理解的是,c1 显然等同于 c2,但 c2 不等同于 c3。 ',' 是一个运算符而 'and/2' 是一个规则这一事实似乎有所不同?
  • 在 Prolog 中,有一个名为 write_canonical(X) 的谓词,它可以让您按照 Prolog 的规范方式编写术语。试试write_canonical((A, B))

标签: prolog prolog-cut


【解决方案1】:

您对, 作为二进制函数的性质是正确的,但对and 的翻译尚未解释如何解释连词。让我们看一下一般的 Prolog 规则:

head :-  % we disregard variables at the moment
  goal1,
  goal2,
  goal3.

fact. % a fact is a rule without a body

这可以读作“目标暗示着头脑”或“为了推导出每个目标所需的头脑”。但是goal1 本身可能是一个规则,所以我们需要某种TODO 列表(通常实现为堆栈,但现在确切的行为并不重要)。我们从 TODO 列表上的查询开始。如果列表中的某个元素是事实,我们可以将其删除。要删除规则,我们需要导出所有目标,因此我们将列表中的头部替换为目标。我们来看一个例子:

make(coffee).
make(tea).
make(orange_juice).
make(croissant).
make(scrambled_eggs).

prepare(beverage) :-
    make(coffee).
prepare(beverage) :-
    make(tea).
prepare(beverage) :-
    make(orange_juice).
prepare(food) :-
    make(croissant).
prepare(food) :-
    make(scrambled_eggs).

breakfast :-
    prepare(beverage),
    prepare(food).

当我们查询早餐时,我们得到:

?- breakfast.
true ;
true ;
true ;
true ;
true ;
true.

不知道我们早餐吃什么有点无聊,但是有六种方法可以吃到一种。那么我们是如何到达那里的呢?

我们从待办事项清单上的早餐开始:

  • 早餐

唯一适合的规则头是最后一个,因此我们将 TODO 更改为:

  • 准备(饮料)
  • 准备(食物)

我们现在有多个规则告诉我们要制作什么饮料,让我们选择第一个:

  • 制作(咖啡)
  • 准备(食物)

幸运的是,make(coffee) 是事实,因此我们可以将其从列表中划掉。

  • 准备(食物)

同样我们可以准备食物:

  • 制作(羊角面包)

因为有一个相应的事实,我们已经完成了早餐(输出true)。但我们做出了一些选择:我们可以准备茶或橙汁来代替咖啡,我们可以用炒鸡蛋代替羊角面包。这意味着我们可以回溯:

  • 制作(羊角面包)

好的,这里没有选择,让我们更进一步:

  • 准备(食物)

并将其扩展为

  • 制作(scrambled_eggs)。

这又是一个事实(true 再次:))。当我们进一步回溯时,我们得到了制作饮料和食物的所有组合,并为所有 6 个打印 true

剪切可防止回溯到放置之前的某个点。让我们修改规则如下:

breakfast :-
    prepare(beverage),
    !,
    prepare(food).

现在我们无法撤销饮料选择,所以我们最终会得到咖啡和炒鸡蛋或咖啡和羊角面包:

?- breakfast.
true ;
true.

所以连词告诉我们接下来要派生哪些东西,而切分告诉我们停止回溯。在这两种情况下,我们都需要一个数据结构来记录我们已经做出的最后一系列选择。它必须是一个序列,因为我们需要记住我们还可以选择什么作为饮料和食物。在切割的情况下,我们可以忘记最后一个切割之后的序列中的所有内容,即切割切断了选择点序列。因此,我们承诺做出特定的选择。

我希望这对实施有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多