Как мне кодировать rec/3 при наличии объявления meta_predicate?
У меня есть следующий код, который отлично работает без объявления meta_predicate. Я определил предикат rec/3 следующим образом:
:- use_module(library(lambda)).
rec(F,1,F).
rec(F,N,\A^B^(call(F,A,H),call(G,H,B))) :-
N>1, M is N-1, rec(F,M,G).
Предикат rec/3 в основном реализует следующее уравнение рекурсии высшего порядка:
F^1 = F
F^N = F*F^(N-1) for N>1
Где * это композиция двух отношений. Например, его можно использовать для определения дополнения в терминах преемника. Преемником будет следующее отношение:
?- F = \A^B^(B is A+1), call(F, 2, R).
R = 3 /* 3 = 2+1 */
Добавление может быть сделано следующим образом (SWI-Prolog):
?- F = \A^B^(B is A+1), rec(F, 8, G), call(G, 3, R).
R = 11 /* 11 = 3+8 */
Теперь, если я добавлю объявление meta_predicate следующим образом, перед предложениями rec/3:
:- meta_predicate rec(2,?,2).
rec(F,1,F).
rec(F,N,\A^B^(call(F,A,H),call(G,H,B))) :-
N>1, M is N-1, rec(F,M,G).
Вещи больше не работают (SWI-Пролог):
?- F = \A^B^(B is A+1), rec(F, 8, G), call(G, 3, R).
false
Как я могу исправить предложения для rec/3 и запроса, чтобы они работали в присутствии meta_predicate?
до свидания
3 ответа
Объявления и модули мета-предикатов SWI аналогичны описанным в Quintus, SICStus и YAP. Основное предположение в этих системах состоит в том, что вся информация передается через объявленный мета-аргумент с использованием (:)/2
, Там нет скрытого состояния или контекста. В обычных случаях (простые экземпляры аргументов) объявлений мета-предикатов достаточно, чтобы освободить программиста от бремени явной квалификации.
Тем не менее, в более сложных ситуациях, как нынешняя, вы должны обеспечить добавление явной квалификации. Кроме того, вы должны убедиться, что (:)/2
префиксы соответственно. В SWI есть strip_module/3
:
?- strip_module(a:b:c:X,M,G).
X = G,
M = c.
Примите определение:
rec(_, -1, local).
rec(_, 0, =).
rec(F, 1, F).
local(S0,S) :-
S is S0+1.
Который теперь должен быть написан так:
:- meta_predicate goal_qualified(:,-).
goal_qualified(G,G).
:- meta_predicate rec(2,+,2).
rec(_, -1, G) :-
strip_module(G,_,VG),
goal_qualified(local,VG).
rec(_, 0, G) :-
strip_module(G,_,VG),
goal_qualified(=,VG).
rec(F, 1, G) :-
strip_module(G,_,F).
Многие предпочитают добавлять префиксы модулей вручную:
:- meta_predicate rec(2,+,2).
rec(_, -1, G) :-
strip_module(G,_,mymodule:local).
...
И если мы ограничимся только SWI, жертвуя тем самым совместимостью с SICStus или YAP:
:- meta_predicate rec(2,+,2).
rec(_, -1, _:mymodule:local).
rec(_, 0, _:(=)).
rec(F, 1, _:F).
Правило в вашем вопросе
rec(F,N,\A^B^(call(F,A,H),call(G,H,B))) :-
N>1, M is N-1, rec(F,M,G).
таким образом переводится как:
rec(F, N, MG) :-
N > 1, M is N - 1,
strip_module(MG,_,VG),
goal_qualified(\A^B^(call(F,A,H),call(G,H,B)),VG),
rec(F, M, G).
При условии, что library(lambda)
импортируется везде, это можно снова упростить в SWI для:
rec(F, N, _:(\A^B^(call(F,A,H),call(G,H,B)) )) :-
N > 1, M is N -1,
rec(F, M, G).
Мой вывод
1mo: системы должны выдавать предупреждение для всегда неудачных предложений, как в:
| ?- [user].
% compiling user...
| :- meta_predicate p(0).
| p(1).
% compiled user in module user, 0 msec 2080 bytes
yes
| ?- p(X).
no
2do: Может быть, было бы лучше использовать следующий вспомогательный предикат:
:- meta_predicate cont_to(:,:).
cont_to(MGoal, MVar) :-
strip_module(MVar, _, Var),
( nonvar(Var)
-> throw(error(uninstantiation_error(Var),_))
; true
),
( strip_module(MGoal,_,Goal),
var(Goal)
-> throw(error(instantiation_error,_))
; true
),
Var = MGoal.
Использование.
rec(_, -1, MV) :-
cont_to(local, MV).
Или скорее: одна версия для каждого количества вспомогательных аргументов, таким образом
:- meta_predicate cont0_to(0,0).
:- meta_predicate cont1_to(1,1).
:- meta_predicate cont2_to(2,2).
...
Название могло бы быть лучше, оператор не будет делать, хотя.
Нет проблем с версией вашего кода Logtalk:
:- object(rec).
:- public(rec/3).
:- meta_predicate(rec(2,*,*)).
rec(F, 1, F).
rec(F, N, [A,B]>>(call(F,A,H),call(G,H,B))) :-
N > 1, M is N - 1,
rec(F, M, G).
:- public(local/2).
local(A, B) :-
B is A + 1.
:- end_object.
Я получил:
$ swilgt
...
?- {rec}.
% [ /Users/pmoura/Desktop/lgtemp/stackru/rec.lgt loaded ]
% (0 warnings)
true.
?- F = [A,B]>>(B is A+1), rec::rec(F, 8, G), logtalk<<call(G, 3, R).
F = [A, B]>> (B is A+1),
G = [_G88, _G91]>> (call([A, B]>> (B is A+1), _G88, _G99), call([_G108, _G111]>> (call([A, B]>> (B is A+1), _G108, _G119), call([_G128, _G131]>> (call(... >> ..., _G128, _G139), call(... >> ..., _G139, _G131)), _G119, _G111)), _G99, _G91)),
R = 11 ;
false.
?- F = [A,B]>>(rec::local(A,B)), rec::rec(F, 8, G), logtalk<<call(G, 3, R).
F = [A, B]>> (rec<<local(A, B)),
G = [_G2655, _G2658]>> (call([A, B]>> (rec<<local(A, B)), _G2655, _G2666), call([_G2675, _G2678]>> (call([A, B]>> (rec<<local(A, B)), _G2675, _G2686), call([_G2695, _G2698]>> (call(... >> ..., _G2695, _G2706), call(... >> ..., _G2706, _G2698)), _G2686, _G2678)), _G2666, _G2658)),
R = 11 ;
false.
Обратите внимание на "исправление" для meta_predicate/1
директивы. Код для rec/3
Предикат такой же, за исключением преобразования синтаксиса лямбда-выражения в синтаксис Logtalk. Тем не менее, в случае Logtalk, meta_predicate/1
Директива не требуется для этого примера (так как все, что rec/3
Предикат конвертирует термин в новый термин) и служит только для целей документирования. Вы можете закомментировать это и по-прежнему использовать rec::rec/3
предикат, называя это от любого user
(т.е. из интерпретатора верхнего уровня) или из объекта клиента.
call/3
вызов сделан в контексте logtalk
встроенный объект только для интерпретации лямбда-выражения Logtalk (Logtalk специально не делает поддержку своей родной лямбда-выражения доступной в интерпретаторе верхнего уровня Prolog).
Следующее прямолинейное решение (протестировано только на SWI-Prolog, но в любом случае далеко от широкой переносимости решения на основе Logtalk):
:- module(m, [rec/3]).
:- use_module(library(lambda)).
:- meta_predicate(rec(:,?,-)).
rec(F, 1, F).
rec(F, N, \A^B^(call(F,A,H),call(G,H,B))) :-
N > 1, M is N -1,
rec(F, M, G).
дает:
?- [mrec].
true.
?- use_module(library(lambda)).
true.
?- F = \A^B^(B is A+1), rec(F,10,G), call(G,0,R).
F = \A^B^ (B is A+1),
G = \_G56^_G59^ (call(user: \A^B^ (...is...), _G56, _G67), call(\_G75^_G78^ (call(..., ..., ...), call(..., ..., ...)), _G67, _G59)),
R = 10 .
без необходимости взлома низкого уровня (одна из причин meta_predicate/1
Директива состоит в том, чтобы избежать необходимости использовать явную квалификацию) или требовать вводить в заблуждение meta_predicate/1
директивы. Перечитав пост и комментарии, я все еще задаюсь вопросом, почему вы хотите принудительно написать:
:- meta_predicate(rec(2,?,2)).
Первый аргумент rec/2
не будет использоваться в качестве замыкания, к которому мета-предикат добавит два аргумента для построения цели для ее вызова. Третий аргумент является выходным аргументом. В первом аргументе "2" означает ввод, но для третьего аргумента вместо "выход"! Ни в том, ни в другом случае мета-предикат не выполняет никаких мета-вызовов! Конечным результатом этого нарушения значения давно установленных индикаторов мета-аргументов в директивах мета-предиката является то, что пользователь больше не будет знать, как интерпретировать шаблон мета-предиката, не глядя на действительный код мета-предиката.