这是从 Ivan Bratko 的书“Prolog Programming for人工智能 - 第 3 版 - 第 377 页”中获取的代码示例。它非常适合查询?-value(kiwi, active_at, night)。通过给出结果为true。
但它也给出了true查询的结果?- value(kiwi, active_at, daylight).,它应该是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).
Run Code Online (Sandbox Code Playgroud)
首先让我们看一下相关的查询。您的第一个查询...
?- value(kiwi, active_at, night).
true.
Run Code Online (Sandbox Code Playgroud)
...第一条规则 value/3 成功。如果您手动尝试目标,您可以看到这一点。查询...
?- Query =.. [kiwi, active_at, night].
Run Code Online (Sandbox Code Playgroud)
...成功并将变量统一Query为kiwi(active_at, night):
?- Query =.. [kiwi, active_at, night].
Query = kiwi(active_at, night).
Run Code Online (Sandbox Code Playgroud)
下一个目标叫Query...
?- call(kiwi(active_at, night)).
true.
Run Code Online (Sandbox Code Playgroud)
...并且成功,因为你有一个事实kiwi(active_at, night).。下一个目标!防止 Prolog 回溯并搜索进一步的解决方案,因此查询?- value(kiwi, active_at, night).确定性地成功(您不必;在第一个解决方案之后按)。现在让我们看一下您的第二个查询......
?- value(kiwi, active_at, daylight).
true ;
false.
Run Code Online (Sandbox Code Playgroud)
...这也成功了。value/3 的第一条规则Query与它统一kiwi(active_at, daylight)并随后调用它:
?- call(kiwi(active_at, daylight)).
false.
Run Code Online (Sandbox Code Playgroud)
由于您没有合适的事实,因此此调用失败。请注意,此时该规则无法成功,因此 Prolog 不会理会最后一个目标,而是继续执行第二个规则 value/3。这里是第一个目标
?- parent(kiwi,ParentName).
ParentName = bird ;
false.
Run Code Online (Sandbox Code Playgroud)
...由于析取的第一个参数而成功并ParentName与统一...bird
?- Query =.. [kiwi, a_kind_of, ParentName].
Query = kiwi(a_kind_of, ParentName).
Run Code Online (Sandbox Code Playgroud)
...以及parent/2 中的后续调用/1:
?- call(kiwi(a_kind_of, ParentName)).
ParentName = bird.
Run Code Online (Sandbox Code Playgroud)
现在 value/3 的第二条规则的第二个目标调用自身,再次从第一条规则开始:
?- value(bird, active_at, daylight).
true.
Run Code Online (Sandbox Code Playgroud)
这成功了,因此查询...
?- value(kiwi, active_at, daylight).
true
Run Code Online (Sandbox Code Playgroud)
...也成功了。请注意,Prolog 在回答后正在等待输入true,如果您按;它,它会搜索进一步的解决方案:
?- value(kiwi, active_at, daylight).
true ;
Run Code Online (Sandbox Code Playgroud)
value(bird, active_at, daylight)尽管由于 value/3 的第一条规则被切断,目标确定性地成功了parent(kiwi,ParentName),但是调用谓词(value/3 的第二条规则)的第一个目标 ( ) 留下了一个开放的选择点(参见上面的查询),现在 Prolog正在回溯那里寻找其他解决方案。但没有,所以 Prolog 回答:
?- value(kiwi, active_at, daylight).
true ;
false.
Run Code Online (Sandbox Code Playgroud)
然而,如果你问新西兰人什么时候活跃,Prolog 会回答:
?- value(kiwi, active_at, X).
X = night.
Run Code Online (Sandbox Code Playgroud)
这里之所以daylight不推导,又是因为剪切。当第一个规则 value/3 成功后,X = night它会阻止 Prolog 搜索其他解决方案。从逻辑的角度来看,这种行为是完全不正确的:要么猕猴桃在白天和晚上都活跃,那么最后一个查询应该产生两种解决方案,要么它们是夜间活动的,那么查询?- value(kiwi, active_at, daylight).应该失败。我不想推测谓词值/3 的预期行为,因此我将参考有关 kiwi 的维基百科条目,其中指出: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).
Run Code Online (Sandbox Code Playgroud)
通过这些更改,您将始终获得两种解决方案:
?- value(kiwi, active_at,X).
X = night ;
X = daylight ;
false.
?- value(kiwi, active_at,night).
true ;
false.
?- value(kiwi, active_at,daylight).
true ;
false.
Run Code Online (Sandbox Code Playgroud)
要了解为什么在 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
Run Code Online (Sandbox Code Playgroud)
谓词值/3 试图调用不存在的谓词动物/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
Run Code Online (Sandbox Code Playgroud)
另一方面,如果您仅选择夜间解决方案,则可以定义一个事实 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).
Run Code Online (Sandbox Code Playgroud)
这会产生所需的结果:
?- value(kiwi, active_at,X).
X = night ;
false.
?- value(kiwi, active_at,night).
true ;
false.
?- value(kiwi, active_at,daylight).
false.
Run Code Online (Sandbox Code Playgroud)
如果您计划扩展此示例以包含夜行性鸟类的动物属性,则需要添加一个事实nocturnalbird(a_kind_of, animal).并在 value/3 和 Parent/2 中包含 iscallable/1,如上所示。
事实上,正如评论中的 @false 所指出的,这里根本不需要 =../2 。您可以简单地使用 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
Run Code Online (Sandbox Code Playgroud)