Как писать / редактировать собственные сопрограммы в Прологе?
Я хотел бы построить свои собственные сопрограммы в Прологе. Я хотел бы добавить некоторые дополнительные функции.
2 ответа
Одним из возможных решений будет использование механизма расширения терминов, предоставляемого некоторыми системами Prolog и Logtalk, для перезаписи вызовов, например, в freeze/2
Предикат, чтобы сделать дополнительные шаги, которые вы хотите. Однако нужно быть осторожным, чтобы не расширить вызов предиката в другую цель, которая вызывает тот же предикат, что и расширение цели рекурсивно применяется до тех пор, пока не будет достигнута фиксированная точка. Реализация механизма расширения терминов в Logtalk позволяет легко избежать этой ловушки (с дополнительным преимуществом переносимости, поскольку вы можете использовать Logtalk с большинством систем Prolog), используя конструкцию управления обходом компилятора, {}/1
, Глупый пример будет:
:- object(my_expansions,
implements(expanding)).
goal_expansion(
freeze(Var,Goal),
( write('If you instantiate me, I will run away!\n'),
{freeze(Var,Goal)}, % goal will not be further expanded
write('Bye!\n')
)
).
:- end_object.
Этот объект может затем использоваться в качестве объекта ловушки для компиляции исходных файлов, содержащих обращения к freeze/2
что вы хотите расширить. Что-то вроде (при условии, что объект выше сохранен в файле с именем my_expansions.lgt
и что исходный файл, который вы хотите расширить, называется source.lgt
):
?- logtalk_load(my_expansions), logtalk_load(source, [hook(my_expansions)]).
Для получения полной информации см. Документацию и примеры Logtalk.
Возможно, существует простой способ, чтобы я не знал, что делать то же самое можно с помощью собственной реализации механизма расширения терминов системы Prolog. Кто-нибудь?
Написание ванильного переводчика для сопрограмм должно быть в списке преподавания каждого курса Пролог. Это довольно просто, здесь вы видите обычный ванильный интерпретатор, упрощенный:
% solve(+Term)
solve(true).
solve((A,B)) :- solve(A), solve(B).
solve(H) :- clause(H, B), solve(B).
Теперь для определения соответствия, в смысле приостановки целей с помощью freeze/2, просто добавьте дополнительную пару входных выходных параметров с отложенными целями, для спецификации select/3 см. (*):
% solve(+Term, +List, -List)
solve(G, L, R) :- select(freeze(V, F), L, H),
nonvar(V), !,
solve((F,G), H, R).
solve(freeze(V, G), L, [freeze(V,G)|L]) :- var(V), !.
solve(freeze(_, G), L, R) :- solve(G, L, R).
solve(true, L, L).
solve((A,B), L, R) :- solve(A, L, H), solve(B, H, R).
solve(H, L, R) :- clause(H, B), solve(B, L, R).
Вы можете использовать вышеупомянутый ванильный переводчик для изучения различных стратегий пробуждения. Я не уверен, захватывает ли он существующие системы Prolog. Но вы можете запустить примеры, такие как:
?- freeze(X, member(X, [the(1), the(2)])), X = the(Y).
успешно, поставив следующий вопрос:
?- solve((freeze(X, member(X, [the(1), the(2)])), X = the(Y), true), [], L).
"Истина" необходима, чтобы в последний раз проверять проснувшиеся цели. Если L возвращается пустым, то все замороженные цели проснулись. В противном случае есть несколько ожидающих замороженных целей. Что иногда называют барахтанием.
Вышеприведенный прототип также приводит к естественной реализации сопрограмм через тонкие атрибуты, отмену /1 и некоторую небольшую поддержку интерпретатора в очереди внедрения цели. Я скоро напишу об этом где-нибудь еще.
до свидания
(*) https://www.complang.tuwien.ac.at/ulrich/iso-prolog/prologue