我有一个看起来像的数据库
hasChild(person1, person2).
hasChild(person1, person3).
hasChild(person4, person5).
Run Code Online (Sandbox Code Playgroud)
这意味着(例如)person1的孩子名为person2.
然后我创建一个谓词来识别该人是否是父母
parent(A):- hasChild(A,_).
Run Code Online (Sandbox Code Playgroud)
这表明该人是否是父母,即有任何子女
然后我尝试创建一个谓词childless(A)
,如果用户没有任何基本上与之相反的子项,则该谓词应该返回true parent(A)
.
所以我在这里有两个问题:
a)是否有可能以某种方式采用谓词的"逆",childless(A):-not(parent(A)).
或者以任何其他方式使用hasChild
或任何其他方法绕过它?
b)parent(A)
如果该人有多个孩子,将多次返回true.是否有可能只返回一次真实?
对于问题1,是的.Prolog对某些人来说并不完全神奇,因为它混淆了否定和失败,但你绝对可以写:
childless(X) :- \+ hasChild(X, _).
Run Code Online (Sandbox Code Playgroud)
对于没有孩子的人,你会看到"真实".您还会看到蔬菜,矿物质,意识形态,程序,鞋盒,啤酒食谱和未风化的两足动物的"真实".如果这对您来说是一个问题,一个简单的解决方案是改善您的数据模型,但抱怨Prolog是一个非常受欢迎的选择.:)
对于问题2,最简单的解决方案是使用once
:
parent(A) :- once(hasChild(A, _)).
Run Code Online (Sandbox Code Playgroud)
这是使用cut运算符的更安全的替代方法,如下所示:
parent(A) :- hasChild(A, _), !.
Run Code Online (Sandbox Code Playgroud)
这具有相当大的成本:parent/1
虽然它将验证其他正确的解决方案,但只生成一个有效的解决方案.以机智:
?- parent(X).
X = person1.
Run Code Online (Sandbox Code Playgroud)
注意它没有暗示person4
.然而,
?- childless(person4).
true.
Run Code Online (Sandbox Code Playgroud)
对于像我这样的大多数中级Prolog程序员来说,这种不对称当然是一种"代码味道".这就好像Prolog根据查询有某种健忘症或选择性听力.这无法被邀请参加上流社会活动!
我建议这里最好的解决方案(处理上面的矿物/蔬菜问题)是增加一些关于人的事实.毕竟,一个人在他们有孩子之前就存在(或者他们呢?)因此他们没有被这种关系"定义".但是继续玩游戏,您可以通过setof/3
构建所有人的列表来规避问题:
parent(Person) :-
setof(X, C^hasChild(X, C), People),
member(Person, People).
Run Code Online (Sandbox Code Playgroud)
奇怪的表达式C^hasChild(X, C)
告诉Prolog C是一个自由变量; 这可以确保我们在hasChild/2
绑定到列表的第一个参数中获取所有内容的集合People
.这不再是一阶逻辑了!这里的优点是,member/2
它将为我们生成以及检查:
?- parent(person4).
true.
?- parent(X).
X = person1 ;
X = person4.
Run Code Online (Sandbox Code Playgroud)
这有效吗?不,这很聪明吗?可能不是.它是否也能解决您的问题?是的,似乎是.好吧,三分之一也不错.:)
作为最后的评论,一些Prolog实现将其not/1
视为别名\+/1
; 如果您碰巧使用其中之一,我建议您不要误解与ISO前约定的兼容性,以获得多样化的快速容差:更正not(X)
to 的拼写\+ X
.:)