如果我有以下格式的事实:
person(name,age).
如何编写查询以找到最年轻的人?
我曾尝试使用递归,但我一直陷入无限循环。到目前为止,从我所做的所有阅读中,我发现我需要使用 ! 切割运算符。任何帮助将不胜感激。
您绝对不需要使用 cut 运算符。我很难想象这个解决方案会是什么样子。
最简单的做法是进行此类查询:
youngest(person(Name,Age)) :-
person(Name, Age),
\+ (person(Name2,Age2), Name2 \= Name, Age2 < Age).
遗憾的是,这不是很有效,因为它可能必须为每个人搜索一次数据库,导致 O(N^2) 性能。但应该清楚它为什么起作用。
更快的解决方案是使用setof/3
.
youngest(person(Name, Age)) :-
setof(Age-Name, person(Name,Age), [Age-Name|_]).
我们依赖于对setof/3
列表进行排序的事实,这将导致最年轻的人被移动到结果列表的开头以使其正常工作。它表现得更好,但它并没有读得那么清楚。
有一个标准库可用于解决 SWI 的此类问题,但我不确定您是否在使用 SWI,而且我自己也没有使用过,但您可以研究一下。它被称为聚合。
另一种方法是直接用具体化数据库findall/3
,然后直接用一个专门为此编写的谓词找到最小值。这样的解决方案可能看起来像这样:
youngest(Person) :-
findall(person(Name,Age), person(Name,Age), [P1|Rest]),
youngest(P1, Rest, Person).
youngest(Person, [], Person).
youngest(person(Name, Age), [person(N2,A2)|Rest], Person) :-
Age < A2 -> youngest(person(Name, Age), Rest, Person)
; youngest(person(N2, A2), Rest, Person).
然而,这似乎需要做很多工作,尽管它可能会给你最好的性能(应该是线性时间)。
只是为了添加丹尼尔(+1)的(非常完整的)答案:库(聚合)可以进行此类搜索 - 以及更多:
youngest(Person) :-
aggregate(min(Age,Pers), person(Pers,Age), min(_, Person)).
我认为它值得研究,因为 Prolog 与数据库的类比,以及这种语言中缺少的聚合运算符。