我想知道通过这样的回溯获取某些事实的最大值(最老的人)是否是一个好主意:
data(MaxID, MaxName, MaxAge),
\+ (data(ID, Name, Age), ID \= MaxID, MaxAge < Age).
反之亦然最小值(最年轻的人):
data(MinID, MinName, MinAge),
\+ (data(ID, Name, Age), ID \= MinID, MinAge > Age).
这在空间或时间复杂性方面是否有效?
实现的风格是否容易/直接转发?是否存在“更好”的实现?
答案 0 :(得分:2)
这在空间或时间复杂性方面是否有效?
你的实现它的时间复杂度为O(N ^ 2),因为对于每个候选谓词,所有其他谓词都被“调用”进行比较。空间复杂度为O(1)。
是否存在“更好”的实施?
是的,存在更好,更有效的实现。很长一段时间,SWI-Prolog提供了库(aggregate),这是对setof / bagof / findall经典内置的一个直接增强:
?- aggregate(max(Age,data(ID,Name,Age)), ID^Name^data(ID,Name,Age), max(_,X)).
注意由ID^Name^...
表示的量化控制风格。这与setof / 3和bagof / 3非常相似。基于非回溯分配,实施更加困难。
可以将最新添加的内容视为库(solution_sequences)。 在挖掘最后一个之前,请考虑使用库(聚合)。
修改的
基于@false对问题的评论和答案@boris,我会尝试提供一个'更好'(当然,主观评价)的实现:
min(P,A) :-
copy_term(P,Q),
arg(A,P,V), arg(A,Q,U),
call(P), \+ ( call(Q), U@<V ).
现在,您可以将谓词作为第一个参数传递,并指定P
的参数以用于与第二个参数进行比较。
答案 1 :(得分:0)
简短的回答是,不,没有真正的&#34;更好的&#34;存在,总是正确的,易于实现的,同时高效的。
一个小小的修正:写
就足够了data(MaxID, MaxName, MaxAge),
\+ (data(ID, Name, Age), MaxAge < Age)
(正如您对问题的评论中@false所建议的那样)
您还可以看到this question and answers。 This answer详细介绍了何时以及为何使用setof/3
可能会出现问题。这可能对您的用例很重要。
另一种方法(在@CapelliC的非常有用的答案中没有提到)是从谓词中收集所有解决方案并使用密钥对它们进行排序。如果您正在使用SWI-Prolog或其他具有4-argument version of sort的Prolog,您可以选择比较和术语中的键,您可以这样做,例如:
bagof(data(ID, Name, Age), data(ID, Name, Age), All),
sort(3, @>=, All, [data(Max_ID, Max_Name, Max_Age)|_])
只要您的data/3
只是一个事实表,就可以安全使用。
如果您在列表中有等效但不相等的元素,那么这当然会被分解为data(10, john, 34)
和data(101, jane, 34)
。在我的链接问题和答案中,有一些例子可以解决这个问题,但同样,我真的不认为它会得到更好的&#34;更好的&#34;。它可能更有效率。我强烈建议您仔细考虑您的使用案例,如果您认为这可能是一个瓶颈,请测量性能。
查看@CapelliC建议的implementation of library(aggregate)很明显,它恰好适用于该用例:它可以在常量内存中找到最小值,最大值,总和等,并且只触及每个事实一次,并且如果有必要,可以回到建立整个清单。