Как мне кодировать 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" означает ввод, но для третьего аргумента вместо "выход"! Ни в том, ни в другом случае мета-предикат не выполняет никаких мета-вызовов! Конечным результатом этого нарушения значения давно установленных индикаторов мета-аргументов в директивах мета-предиката является то, что пользователь больше не будет знать, как интерпретировать шаблон мета-предиката, не глядя на действительный код мета-предиката.

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