如何在meta_predicate声明的情况下编码rec / 3?

时间:2014-06-25 07:25:07

标签: module prolog swi-prolog 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-Prolog):

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

如何修复rec / 3和查询的子句 他们在meta_predicate的存在下工作?

再见

3 个答案:

答案 0 :(得分:2)

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

名称可能会更好,但运营商不会这样做。

答案 1 :(得分: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.

注意&#34;修复&#34;对于meta_predicate/1指令。除了将lambda表达式语法转换为Logtalk语法之外,rec/3谓词的代码是相同的。但是,对于Logtalk,此示例的meta_predicate/1指令不需要(因为rec/3谓词所做的只是将术语转换为新术语)和仅用于文档目的。您可以对其进行注释,并仍然使用rec::rec/3谓词,从user(即从顶级解释器)或从客户端对象调用它。

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

答案 2 :(得分: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 .

不需要低级别的攻击(meta_predicate/1指令的动机之一是避免使用显式限定)或要求误导meta_predicate/1指令。重新阅读帖子和评论之后,我仍然想知道你为什么要强行写作:

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

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