Пролог найти минимальное значение запроса

Если у меня есть факты в следующем формате:

person(name,age).

Как я могу написать запрос, чтобы найти самого молодого человека?

Я пытался использовать рекурсию, но я продолжал застрять в бесконечных циклах. Так далеко от всего чтения, которое я сделал, я узнал, что мне нужно использовать! режущий оператор. Любая помощь будет очень высоко ценится.

2 ответа

Решение

Вам определенно не нужно использовать оператор вырезания. Мне трудно представить, как будет выглядеть это решение.

Самое простое, что нужно сделать, это сделать запрос такого рода:

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 с базами данных и отсутствующих операторов агрегации в этом языке.

Другие вопросы по тегам