Ответ 1
В SWI-Prolog:
aggregate_all(count, is_man(X), Count).
Я хочу подсчитать количество раз, когда пользовательский предикат является истинным. Например, у меня есть следующий код:
is_man(john).
is_man(alex).
?:-is_man(X).
X вернет john, тогда, если я нажимаю точку с запятой, он также вернет alex, а затем false.
Я хочу создать что-то вроде:
count(is_man(X), Count).
И чтобы вернуться
Count = 2
Как я могу это сделать?
В SWI-Prolog:
aggregate_all(count, is_man(X), Count).
Для стандартного решения Prolog для ISO вы можете использовать findall/3, чтобы составить список всех решений, а затем установить Count на длину результирующего списка. Это может быть несколько сложнее обернуть это в пользовательский предикат count/2, как вы предлагаете, потому что нам нужно сформировать первый аргумент findall/3 в который учитывает любые свободные (несвязанные) переменные в цели, которую вы хотите передать в качестве первого аргумента count/2.
Многие прологи предоставляют "счетчики" или другие формы изменчивых глобальных значений, нестандартное расширение, которое может использоваться в связи с "циклом", вызванным ошибкой, для того, чтобы сделать тот же подсчет. Немного более громоздко, но придерживаться буквы стандарта Prolog было бы использовать assert и retract, чтобы создать свой собственный счетчик, настроив динамический факт.
Далее следует иллюстрация последнего подхода. Чтобы сделать его "многопоточным безопасным", потребуется дополнительная логика.
count(Goal,_) :-
setGoalCount(0),
call(Goal),
incGoalCount(1),
fail. /* or false in some Prologs */
count(_,Count) :-
getGoalCount(Count).
setGoalCount(_) :-
retract(getGoalCount(_)),
fail.
setGoalCount(X) :-
assert(getGoalCount(X)).
incGoalCount(Y) :-
retract(getGoalCount(X)),
!,
Z is X + Y,
assert(getGoalCount(Z)).
count(P,Count) :-
findall(1,P,L),
length(L,Count).