【问题标题】:Code Issue on implementing Inference in Frames with Prolog使用 Prolog 在框架中实现推理的代码问题
【发布时间】:2017-11-24 07:58:38
【问题描述】:

这是取自 Ivan Bratko 的书“Prolog Programming for Artificial Intelligence - 3rd edition - pg 377”的代码示例。它非常适合查询?-value(kiwi, active_at, night)。将结果设为true

但对于查询?- value(kiwi, active_at, daylight).,它也给出了true的结果,它应该是false

我应该如何修改它,或者我错过了什么?

%--------Define frames
bird(a_kind_of, animal).
bird(active_at, daylight).

albatross(a_kind_of, bird).
albatross(colour, black).
albatross(size, 10).

kiwi(a_kind_of, bird).
kiwi(active_at, night).
kiwi(colour, brown).
kiwi(size, 24).

albert(instance_of, albatross).
albert(size, 10).

%--------Inference in Frames
value(Frame, Slot, Value):-
    Query =.. [Frame, Slot, Value],
    call(Query), !.     %Value is directly retrieved
value(Frame, Slot, Value):-
    parent(Frame, ParentName),
    value(ParentName, Slot, Value).     % More general rule

parent(Frame, ParentName):-
    (Query =.. [Frame, a_kind_of, ParentName];
    Query =.. [Frame, instance_of, ParentName]),
    call(Query).

【问题讨论】:

  • 它给出了正确的结果,为什么它会给出错误的结果?
  • 替换:Query =.. [Frame, Slot, Value], call(Query)call(Frame, Slot, Value)!保证有效!

标签: prolog


【解决方案1】:

我很晚才遇到这个问题,但是另一种处理方法是将 value 的第一个子句更改为:

value(Frame, Slot, Value):-
    call(Frame, Slot, V), !,    % Value is directly retrieved
    V = Value.                  % Slot might already inherit value so check

【讨论】:

    【解决方案2】:

    首先让我们看看有问题的查询。您的第一个查询...

    ?- value(kiwi, active_at, night).
    true.
    

    ... 以 value/3 的第一条规则成功。如果您手动尝试目标,您会看到这一点。查询...

    ?- Query =.. [kiwi, active_at, night].
    

    ...成功将变量Querykiwi(active_at, night)统一起来:

    ?- Query =.. [kiwi, active_at, night].
    Query = kiwi(active_at, night).
    

    下一个目标调用Query...

    ?- call(kiwi(active_at, night)).
    true.
    

    ...并且成功了,因为您有一个事实kiwi(active_at, night).。下一个目标! 阻止 Prolog 回溯和搜索进一步的解决方案,因此查询 ?- value(kiwi, active_at, night). 确定性地成功(您不必在第一个解决方案之后按 ;)。现在让我们看看您的第二个查询...

    ?- value(kiwi, active_at, daylight).
    true ;
    false.
    

    ...也成功了。 value/3 的第一条规则将Querykiwi(active_at, daylight) 统一起来,随后调用它:

    ?- call(kiwi(active_at, daylight)).
    false.
    

    此调用失败,因为您没有合适的事实。请注意,此时该规则无法成功,因此 Prolog 不会理会最后一个目标并继续执行 value/3 的第二个规则。这是第一个目标

    ?- parent(kiwi,ParentName).
    ParentName = bird ;
    false.
    

    ...由于析取的第一个参数...成功并正在将ParentNamebird 统一...

    ?- Query =.. [kiwi, a_kind_of, ParentName].
    Query = kiwi(a_kind_of, ParentName).
    

    ... 以及 parent/2 中的后续调用/1:

    ?- call(kiwi(a_kind_of, ParentName)).
    ParentName = bird.
    

    现在 value/3 的第二个规则的第二个目标调用自己,再次从第一个规则开始:

    ?- value(bird, active_at, daylight).
    true.
    

    这成功了,因此查询...

    ?- value(kiwi, active_at, daylight).
    true
    

    ... 也成功了。请注意,Prolog 在回答 true 后正在等待输入,如果您按 ;,它正在寻找进一步的解决方案:

    ?- value(kiwi, active_at, daylight).
    true ;
    

    虽然目标value(bird, active_at, daylight)由于第一个规则值/3的剪切而确定性地成功,但调用谓词(值/3的第二个规则)的第一个目标(parent(kiwi,ParentName))留下了一个选择点(请参阅上面的查询),现在 Prolog 正在回溯那里以寻找其他解决方案。但是没有这样 Prolog 的答案:

    ?- value(kiwi, active_at, daylight).
    true ;
    false.
    

    但是,如果你问猕猴桃什么时候活跃,Prolog 会回答:

    ?- value(kiwi, active_at, X).
    X = night.
    

    daylight 之所以没有派生到这里,也是因为切入。在X = night 的第一条 value/3 规则成功后,它会阻止 Prolog 搜索其他解决方案。从逻辑的角度来看,这种行为是完全不正确的:要么猕猴桃在白天和晚上都活跃,那么最后一个查询应该产生两种解决方案,或者它们是夜间的,那么查询 ?- value(kiwi, active_at, daylight). 应该会失败。我不想推测谓词 value/3 的预期行为,所以我将参考 Wikipedia entry on kiwi 而不是它指出:猕猴桃很害羞,通常是夜间活动。它们主要是夜间活动的习惯可能是包括人类在内的捕食者入侵栖息地的结果。在新西兰一些引进的捕食者已经被移除的地区,例如保护区,猕猴桃经常在白天出现。

    如果您想同时拥有这两种解决方案,我建议您添加一些涵盖您的事实的事实 iscallable/1(不要称它为 callable/1,因为已经有一个具有该名称的内置函数),将其添加到value/3 和 parent/2 并像这样删除剪切:

    iscallable(bird).                                 % <- new fact
    iscallable(albatross).                            % <- new fact
    iscallable(kiwi).                                 % <- new fact
    iscallable(albert).                               % <- new fact
    
    value(Frame, Slot, Value):-
        iscallable(Frame),                            % <- new goal iscallable/1
        Query =.. [Frame, Slot, Value],
        call(Query).                                  % <- removed cut here
    value(Frame, Slot, Value):-
        parent(Frame, ParentName),
        value(ParentName, Slot, Value).
    
    parent(Frame, ParentName):-
        iscallable(Frame),                            % <- new goal iscallable/1
        (Query =.. [Frame, a_kind_of, ParentName];
         Query =.. [Frame, instance_of, ParentName]),
        call(Query).
    

    通过这些更改,您将始终获得两种解决方案:

    ?- value(kiwi, active_at,X).
    X = night ;
    X = daylight ;
    false.
    
    ?- value(kiwi, active_at,night).
    true ;
    false.
    
    ?- value(kiwi, active_at,daylight).
    true ;
    false.
    

    要了解为什么在 value/3 的规则 1 中需要 iscallable/1 之类的东西,请将其删除并考虑以下查询:

    ?- value(kiwi, active_at, X).
    X = night ;
    X = daylight ;
    ERROR: value/3: Undefined procedure: animal/2
       Exception: (9) animal(active_at, _G4462) ? creep
       Exception: (8) value(animal, active_at, _G4462) ? creep
    

    谓词值/3 试图调用一个不存在的谓词animal/2。要了解为什么在 parent/2 中需要 iscallable/1 之类的内容,请将其删除并考虑以下查询:

    ?- value(kiwi, active_at,X).
    X = night ;
    X = daylight ;
    ERROR: parent/2: Undefined procedure: animal/2
       Exception: (10) animal(a_kind_of, _G4558) ? creep
    

    另一方面,如果您只选择夜间解决方案,您可以定义描述夜间活动的事实 nocturnalbird/2 并相应地更改事实 kiwi/2:

    nocturnalbird(active_at, night).  % <- new fact
    
    %kiwi(a_kind_of, bird).           % <- remove fact
    kiwi(a_kind_of, nocturnalbird).   % <- new fact
    %kiwi(active_at, night).          % <- remove fact
    kiwi(colour, brown).
    kiwi(size, 24).
    

    这会产生预期的结果:

    ?- value(kiwi, active_at,X).
    X = night ;
    false.
    
    ?- value(kiwi, active_at,night).
    true ;
    false.
    
    ?- value(kiwi, active_at,daylight).
    false.
    

    如果您打算扩展此示例以包括夜行性鸟类的动物属性,则需要添加一个事实 nocturnalbird(a_kind_of, animal). 以及在 value/3 和 parent/2 中包含 iscallable/1,如上所示。

    编辑:

    事实上,这里根本不需要 =../2,正如 cmets 中的 @false 所指出的那样。您可以简单地使用 call/N 代替:

    value(Frame, Slot, Value):-
        iscallable(Frame),               
        call(Frame, Slot, Value).                    % <- here
    value(Frame, Slot, Value):-
        parent(Frame, ParentName),
        value(ParentName, Slot, Value).
    
    parent(Frame, ParentName):-
        iscallable(Frame),
        (call(Frame, a_kind_of, ParentName);         % <- here
         call(Frame, instance_of, ParentName)).      % <- here
    

    【讨论】:

    • call/N 怎么样?这些(=..)/2s 太糟糕了。
    • @false:感谢您的提示。我正忙于试图接近原始代码,以至于我完全忽略了显而易见的:-D。我相应地更新了我的帖子。
    猜你喜欢
    • 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
    相关资源
    最近更新 更多