序言。比较列表中的元素

Prolog. Comparing elements in a list

我有一个包含姓名+年龄的人员列表。

person(bob, 20).
person(charlie, 15).
person(steve, 21).
person(david, 25).

我想创建一个谓词 oldest(List, Name),它将获取一个姓名列表和 return 此列表中最年长的人的姓名。像这样:

?- oldest([bob,charlie,steve,david], Name).
Name = david.

?- oldest([bob,charlie], Name).
Name = bob.

据我所知 - 它应该遍历列表并比较其中每个人的年龄,但对我来说问题是你应该先获得年龄然后 return 一个名字。

怎么办?提前谢谢你。

这就是我现在的全部:

%If person is older
maximum([X|Y], MaxName, MaxAge):-
    person(X, Age),
    Age > MaxAge,
        %Set MaxName to X, do not really understand how to do it
    maximum(Y,MaxName,Age).
    
%Skipping a younger person, passing current MaxName and MaxValue to the next element
maximum([X|Y], MaxName, MaxAge):-
    person(X, Age),
    Age =< MaxAge,
        maximum(Y,MaxName,MaxAge).

递归定义必须至少有一个子句定义案例基础和至少一个子句定义递归步骤:

  • 对于案例库,您可以假设仅包含一个人的列表中最年长的人就是那个人。
  • 对于递归步骤,你可以认为列表至少包含两个人。在这种情况下,您可以 丢弃两者中最年轻的 并继续处理其余的。
% oldest(++ListOfNames, ?Oldest)

  oldest([Name], Name) :- !.
  oldest([Name1, Name2|Names], Oldest) :-
      person(Name1, Age1),
      person(Name2, Age2),
      (   Age1 > Age2
      ->  oldest([Name1|Names], Oldest)
      ;   oldest([Name2|Names], Oldest) ).

person(bob, 20).
person(charlie, 15).
person(steve, 21).
person(david, 25).

测试:

?- oldest([bob,charlie,steve,david], Name).
Name = david.

?- oldest([bob,charlie], Name).
Name = bob.

您可以在库中使用foldl/4(申请):

oldest(People, OldestPerson) :-
    foldl(oldest, People, 0-nobody, _OldestAge-OldestPerson).

oldest(Person, Oldest0-Person0, Oldest2-Person2) :-
    person(Person, Age),
    (   Age > Oldest0
    ->  Oldest2 = Age,
        Person2 = Person
    ;   Oldest2 = Oldest0,
        Person2 = Person0
    ).

“折叠”谓词遍历列表,通过迭代传递一个值。在这种情况下,该值为 OldestAge-Person,其中包含迄今为止最年长的年龄和该年龄的人。

请注意,如果给定一个空列表 ([]),此谓词不会执行您期望的操作。通常,您需要决定如何处理边缘情况 - 例如,您可以这样做:

oldest(People, OldestPerson) :-
    foldl(oldest, People, 0-nobody, OldestAge-OldestPerson),
    OldestAge > 0 .