Prolog - 在最后一个事实时停止重复

时间:2016-12-01 07:18:37

标签: prolog max prolog-assert

我有一个这样的事实清单:

set(h, 3).
set(h, 6).
set(h, 12).
set(h, 1).
set(h, 7).

我需要找到set h的最大值,查询需要如下所示:

?- maximum(h, Max).
Max = 12.

现在有很多方法可以做到这一点,显然。但是目前我正在寻找一种方法来使用动态谓词和失败谓词。我可能不得不使用重复我猜?不幸的是,重复和动态谓词似乎让我感到困惑。

我在想这样的事情:

set(h, 3).
set(h, 6).
set(h, 12).
set(h, 1).
set(h, 7).

:- dynamic current_max/1.

assert(current_max(0)).

maximum(Set, Element):-
    repeat,
    set(Set,Element),
    current_max(Max),
    (Max > Element,
        fail);
     (Element > Max,
     retract(current_max(Max)),
     assert(current_max(Element)),
     fail).

maximum(_, Max):-
    current_max(Max).

我认为自己的一个问题是重复循环不会停止。如果我以某种方式知道它是该集合的最后一个元素我可能会使用剪切,但我不确定如何。

3 个答案:

答案 0 :(得分:2)

故障驱动的循环如下所示:

(   Generator,
    Side_effect,
    fail
;   true
)

在您的情况下,set/2是生成器,更新当前最大值是副作用。在这种情况下,您不应该repeat:调用set/2将生成选择点。当你必须在没有发电机的情况下创建选择点时,你需要repeat

同样,我不得不说这是一个非常奇怪的问题(你有asked it before)。如果你想做这样的事情,至少不要使用动态谓词来保持当前的最大值:这只是乞求麻烦。

在SWI-Prolog的library(aggregate)中有这样做的例子。以aggregate_all/3为起点,只留下寻找最大数字的代码,你有:

set(h, 3).
set(h, 6).
set(h, 12).
set(h, 1).
set(h, 7).

maximum(Set, Max) :-
        State = state(Max),
        (       set(Set, Max),
                arg(1, State, M0),
                M is max(M0, Max),
                nb_setarg(1, State, M),
                fail
        ;       arg(1, State, Max),
                nonvar(Max)
        ).

这使用一个术语来保持当前最大值,因此您保留的状态是maximum/2谓词的本地。它也没有假定一个初始值(如果你从0开始,就像你一样,并且所有值都是负数,你将得到一个不正确的结果!)。有了这个定义:

?- maximum(h, M).
M = 12.

?- maximum(x, M).
false.

很少有评论:

在析取开始时对nonvar(Max)的调用失败的情况下,最后需要set/2。但是请注意,就目前而言,您仍然会得到错误的结果:

?- maximum(x, 42).
true.

你应该能够弄清楚如何处理这个问题。

目前,这使用算术函数max来获取两个数字中较大的一个。如果你还有别的东西,比如原子(即使原子有一个定义的总排序),这将不起作用。您可以定义一个谓词,该谓词可以找到任意两个术语的最大值:

term_max(X, Y, Max) :-
    (   X @> Y
    ->  Max = X
    ;   Max = Y
    ).

然后,你只需要替换

M is Max(M0, Max)

使用:

term_max(M0, Max, M)

答案 1 :(得分:0)

好。我已经看了好几天了。我想我明白为什么我们不需要重复。我尝试将我的(坏)方法与你的一些想法结合起来。

这就是我提出的:

{{1}}

不幸的是,现在它给了我"参数没有充分实例化"。

这就是我的想法:set / 2生成你的值(Set可以给它,Element从事实中获取第一个值)。然后它取一个来自current_max / 1的值(首先是0)。如果max大于失败,则从set中取另一个值。如果Element大于Max,我们删除先前的current_max事实并创建new。然后我们也失败了,回去了。一旦所有值都通过,它返回true。然后我希望它也会通过第二条规则来给出Max答案。

答案 2 :(得分:0)

效率低下但可能是教学上有用的解决方案是使用forall/2为您找到合适的值:

maximum(Set, Max) :-
  set(Set, Max),
  forall(set(Set, Other), 
           Max >= Other).

Prolog将(低效率)选择每个元素作为候选Max,直到满足forall/2步骤,所以这绝对是一个二次时间解决方案,但它说明了" Prolog思考& #34;在我看来,比失败驱动的循环更好。

repeat/0实际上是Prolog中的一种低级机器。大多数时候,通过逻辑思考问题并围绕逻辑构建解决方案,您可以获得更好的结果,而不是担心Prolog的解决方案系统如何为结果做好准备。在前一种方法中,您仍然需要了解回溯以及Prolog如何重试事物,但您使用它,而不是与之抗争或试图直接利用它。