3

我有以下代码在没有 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 */

现在,如果我在 rec/3 的子句之前添加如下 meta_predicate 声明:

 :- 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-Prolog):

  ?- F = \A^B^(B is A+1), rec(F, 8, G), call(G, 3, R).
  false

如何修复 rec/3 和查询的子句,以便它们在 meta_predicate 存在的情况下工作?

再见

4

3 回答 3

1

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).
...

名称可能更好,但操作员不会这样做。

于 2014-06-25T22:58:00.063 回答
1

您的代码的 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/stackoverflow/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请注意指令的“修复” 。除了将 lambda 表达式语法转换为 Logtalk 语法之外,谓词的代码rec/3是相同的。但是,在 Logtalk 的情况下,此示例不需要meta_predicate/1该指令(因为谓词所做的只是将术语转换为新术语)并且仅用于文档目的。您可以将其注释掉并仍然使用谓词,从(即从顶级解释器)或从客户端对象调用它。rec/3rec::rec/3user

call/3调用是在logtalk内置对象的上下文中进行的,只是为了解释 Logtalk lambda 表达式(Logtalk 不会故意在 Prolog 顶级解释器中提供其原生 lambda 表达式支持)。

于 2014-06-25T13:21:51.363 回答
1

以下直截了当的解决方案(仅在 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 .

不需要低级别的 hack(meta_predicate/1指令的动机之一是避免使用显式限定)或需要误导性的meta_predicate/1指令。重新看了帖子和评论,我还是想知道你为什么要强行写:

:- meta_predicate(rec(2,?,2)).

第一个参数rec/2不会用作闭包,元谓词将附加两个参数以构造目标以调用它。第三个参数是输出参数。在第一个参数中,“2”表示输入,但对于第三个参数,它表示输出!在这两种情况下,元谓词都没有进行任何元调用!这种破坏元谓词指令中长期建立的元参数指示符含义的最终结果是,用户将不再知道如何在不查看元谓词的实际代码的情况下解释元谓词模板。

于 2014-06-26T19:36:32.157 回答