Prolog 无法统一隐藏在谓词主体中的变量。 alive_after/1 中的A 与alive_before/1 中的A 之间没有关系。 Prolog 实际上告诉你,当它报告这些警告时它不知道你在做什么:`
|: alive_after(X) :- scientist(A, B, C), B < X.
Warning: user://2:19:
Singleton variables: [A,C]
|: alive_before(X) :- scientist(A, B, C), C > X.
Warning: user://2:23:
Singleton variables: [A,B]
|: alive_during(X, Year) :- alive_after(X), alive_before(X).
Warning: user://2:27:
Singleton variables: [Year]
极其重要将这些消息视为错误,尤其是在您刚接触 Prolog 时!
解决方案是让 Prolog 能够跨这些谓词统一科学家:
alive_after(Scientist, Year) :- scientist(Scientist, Birth, _), Year > Birth.
alive_before(Scientist, Year) :- scientist(Scientist, _, Death), Year < Death.
alive_during(Scientist, Year) :-
alive_before(Scientist, Year), alive_after(Scientist, Year).
您可能还会发现,当您为变量指定有意义的名称时,遵循逻辑会更容易一些。在编写非常通用的谓词时,我对使用极其简洁的变量名感到内疚,但这些实际上是非常具体的谓词,一个好的名称可以帮助您理解您正在做的事情的结构。会是,我认为这比你写的更正确:
alive_after(A, X) :- scientist(A, B, _), X > B.
alive_before(A, X) :- scientist(A, _, C), X < C.
alive_during(A, X) :- alive_before(A, X), alive_after(A, X).
有了更好的名字,更容易看出为什么你的原始代码不正确,因为科学家实际上并没有在alive_before/2 和alive_after/2 调用之间共享。
另一个让您感到困惑的提示是,这个对查询的响应毫无意义:
?- alive_during(1628).
X = boyle
X 是从哪里来的?变量与查询中的值统一,它们不是从谓词体内到达的。
更直接的解决方案是使用 Prolog 的内置 between/3 谓词:
alive_during(Scientist, Year) :-
scientist(Scientist, Birth, Death),
between(Birth, Death, Year).
这还有一个额外的好处,就是它实际上会为您生成解决方案:
?- alive_during(boyle, X).
X = 1627 ;
X = 1628 ;
X = 1629 ;
X = 1630 ;
X = 1631 ;
另一个解决方案没有这个属性。如果您确实具有此生成属性,则可以编写一些有趣的谓词,例如 contemporaries/2:
contemporaries(S1, S2) :-
alive_during(S1, Y),
alive_during(S2, Y),
dif(S1, S2).
这会生成很多不感兴趣的解决方案副本,但您可以通过使用setof/3 来摆脱它们:
?- setof(X-Y, contemporaries(X, Y), Contemporaries).
Contemporaries = [boyle-newton, newton-boyle].