【问题标题】:Prolog - stop repeat when last factProlog - 在最后一个事实时停止重复
【发布时间】:2016-12-01 07:18:37
【问题描述】:

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

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

我需要找到集合 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).

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

【问题讨论】:

    标签: prolog max prolog-assert


    【解决方案1】:

    失败驱动的循环如下所示:

    (   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.
    

    几个cmets:

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

    ?- 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)
    

    【讨论】:

    • 伙计,我会说实话。这似乎在另一个层次上,我还没有学习很多东西(state、arg、nb_setarg、nonvar 等)。有没有办法让它与保持最大值的动态谓词一起工作?或重复。我正在尝试专门处理这些问题。另外,如果我确实想让它与原子一起工作怎么办?所以它会先是数字,然后是字母(a-z)?这可能吗?
    • @PadaKatel 你在这里真的不需要repeat,因为set/2 将创建选择点。在答案的开头,我展示了故障驱动循环的外观,以及如何使用它来解决您的问题;您在问题中已有的撤回+断言是“副作用”。但请记住,随心所欲地做这件事本身就是坏事,只能用于学习目的!
    • 嘿。我为不同的评论写了另一个代码示例。
    【解决方案2】:

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

    这是我想出的:

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

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

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

    【讨论】:

      【解决方案3】:

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

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

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

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

      【讨论】:

        猜你喜欢
        • 2012-05-11
        • 1970-01-01
        • 2018-02-12
        • 2023-04-08
        • 2015-09-21
        • 1970-01-01
        • 2013-10-10
        • 1970-01-01
        • 2013-09-20
        相关资源
        最近更新 更多