在SWI-Prolog中编写宏

时间:2016-06-16 18:29:33

标签: macros prolog swi-prolog

我正在尝试在SWI-Prolog中为switch语句实现一个简单的宏。

这是一系列条件陈述:

(X = a ->
    Output = case1;
X = b ->
    Output = case2;
X = c ->
    Output = case3).

这是一个具有相同效果的等效(但速度要慢得多)的表达式:

switch(X, [
    a : (Output = case1),
    b : (Output = case2),
    c : (Output = case3)
])

我在应用程序中使用了许多这样的谓词,但这会大大减慢它的速度。是否可以将此switch predicate实现为宏,以便在编译时将其更改为正常的条件表达式,以提高应用程序的性能?

2 个答案:

答案 0 :(得分:3)

最小化尝试:创建名为switch.pl

的文件
:- module(switch, []).

compile_caselist(X, [K:Clause], (X = K -> Clause)) :- !.
compile_caselist(X, [K:Clause|CaseList], ((X = K -> Clause);Translated)) :-
    compile_caselist(X, CaseList, Translated).

:- multifile user:goal_expansion/2.
user:goal_expansion(F, G) :-
    F = switch(X, CaseList),
    compile_caselist(X, CaseList, G).

然后像往常一样使用它:例如,在文件switch_test.pl

:- use_module(switch).

test1(X) :-
    X = a -> writeln(case1) ;
    X = b -> writeln(case2) ;
    X = c -> writeln(case3).

test2(X) :-
    switch(X, [
           a : writeln(case1),
           b : writeln(case2),
           c : writeln(case3)
       ]).
编译switch_test.pl之后

?- listing(test2).
test2(A) :-
    (   A=a
    ->  writeln(case1)
    ;   A=b
    ->  writeln(case2)
    ;   A=c
    ->  writeln(case3)
    ).

true.
由于多个请求,

编辑,这里是一个用于分隔子句的编译模式:

:- module(switch, []).

:- multifile user:term_expansion/2.
user:term_expansion((H:-B), [(H:-T)|SWs]) :-
    collect_switches(H,B,T,SWs),
    SWs \= [],
    debug(switch, 'compiled <~w>~nto <~w>~nwith <~w>', [H,T,SWs]).

collect_switches(H,(A0;A),(B0;B),SWs) :-
    collect_switches(H,A0,B0,S0),
    collect_switches(H,A,B,S),
    append(S0,S,SWs).

collect_switches(H,(A0,A),(B0,B),[S|SWs]) :-
    call_switch(H,A0,B0,S), !,
    collect_switches(H,A,B,SWs).
collect_switches(H,(A0,A),(A0,B),SWs) :-
    collect_switches(H,A,B,SWs).
collect_switches(H,A,B,[S]) :-
    call_switch(H,A,B,S), !.
collect_switches(_,C,C,[]).

call_switch(H,switch(X,CL),call(G,X),CTs) :-
    functor(H,F,A),
    R is random(1000000),
    format(atom(G), '~s_~d_~d', [F,A,R]),
    maplist({G}/[K:C,(H:-C)]>>(H=..[G,K]),CL,CTs).

现在测试脚本已经包装在一个模块中,以便于进一步列出:

:- module(switch_test, [test1/1,test2/1]).
:- use_module(switch).

test1(X) :-
    X = a -> writeln(case1) ;
    X = b -> writeln(case2) ;
    X = c -> writeln(case3).

test2(X) :-
    switch(X, [
           a : writeln(case1),
           b : writeln(case2),
           c : writeln(case3)
       ]).

和结果,在编译switch_test.pl之后:

?- switch_test:listing.

test1(A) :-
    (   A=a
    ->  writeln(case1)
    ;   A=b
    ->  writeln(case2)
    ;   A=c
    ->  writeln(case3)
    ).

test2(A) :-
    call(test2_1_362716, A).

test2_1_362716(a) :-
    writeln(case1).
test2_1_362716(b) :-
    writeln(case2).
test2_1_362716(c) :-
    writeln(case3).

简化调试:

?- debug(switch).

在编译时输出如下消息:

% [Thread pq] compiled <test2(_G121946)>
to <call(test2_1_362716,_G121946)>
with <[[(test2_1_362716(a):-writeln(case1)),(test2_1_362716(b):-writeln(case2)),(test2_1_362716(c):-writeln(case3))]]>

注意:这个草图显然非常可能需要更多测试。

如果您决定对改进进行基准测试(如果有的话),请不要使用IO语句(如writeln),因为无论如何这些都将主导执行时间。

答案 1 :(得分:1)

我希望您使用上面的writeln仅用于演示目的。以下是编写与您的问题相同的程序的惯用方法:

foo(a, case1).
foo(b, case2).
foo(c, case3).

这就是这个程序的作用:

?- foo(a, X).
X = case1.

?- foo(X, case1).
X = a.

?- foo(X, Y).
X = a,
Y = case1 ;
X = b,
Y = case2 ;
X = c,
Y = case3.

重点:

  • 不需要writeln,顶级就是这样(如果你真的需要写输出,你当然可以这样做,但是将它与其他部分区分开来并不会有什么坏处逻辑)。
  • 这绝对比任何其他建议更节省空间和时间
  • 当switch表达式是变量
  • 时,您可以枚举您的案例

您是否有可能完全理解此this answerquestion of yours

请注意,如果您可以在谓词的开头执行所有操作,那么您甚至不需要谓词正文:再次,请参阅that same answer和我的示例。

由于参数的数量,您似乎放弃了这个建议,但我没有看到任何其他解决方案将如何解决该问题。在涉及更多参数时,您能否在您的问题中证明您希望如何编写switch语句?

还有一件事:如果你有很多个案,那么确实可以更容易地将它们写在一个列表中;然后,您可以使用术语扩展在编译时将表添加到数据库。在this question结尾处查看this answerterm_expansion示例;该示例是来自SWI-Prolog documentation的逐字副本(请查看该页面的底部)。当然,您可以使用goal_expansion代替term_expansion