包装值的序言添加

时间:2018-09-23 21:53:23

标签: prolog swi-prolog

我编写了一个测试程序,其中包含原子和数字之间的绑定(事实)。

bind(a, 3).
bind(b, 4).
bind(c, 5).

作为玩具解释器的一部分,我希望能够使用Prolog的本机算术运算符对这些原子执行加法运算。例如,我希望能够运行此查询:

% val(X) is the value bound to X
?- X is val(a) + val(b).
X = 7.

但是,我正在努力寻找一种方法来允许这种添加。我的第一种方法是:

% val(X, Y): Y is the value bound to X
val(X, Y) :- bind(X, Y).

% Make val an arithmetic function
:- arithmetic_function(val/1).

但是,arithmetic_function/1不再是Prolog的一部分(或者至少SWI-Prolog表示已弃用),所以我不能使用它。然后,我认为最好的解决方案是使+运算符重载以考虑到这一点:

% val(X, Y): Y is the value bound to X
val(val(X), Y) :- bind(X, Y).

% Overload the + operator
+(val(_X, XVal), val(_Y, YVal)) :- XVal + YVal.

但是在这里,我的语法一团糟,因为我真的不知道如何重载本机算术运算。当我从以前键入示例查询时,SWI-Prolog会说ERROR: Arithmetic: ``val(a)' is not a function

您是否有可能的解决方案或更好的方法或我错过的东西的提示?

3 个答案:

答案 0 :(得分:4)

从文档中,我坚信您应该使用documentation / 3。 但是我无法使其正常工作,function_expansion可以做到,但不是很吸引人……例如,如果您将以下定义保存在文件bind.pl中(只是说)

:- module(bind, [test/0]).

:- dynamic bind/2.

bind(a, 3).
bind(b, 4).
bind(c, 5).

% :- multifile user:goal_expansion/2.
user:goal_expansion(val(X), Y) :- bind(X, Y).
user:goal_expansion(X is Y, X is Z) :- expand_goal(Y, Z).
user:goal_expansion(X + Y, U + V) :- expand_goal(X, U), expand_goal(Y, V).

test :-
     X is val(a) + val(b), writeln(X).

并进行咨询,您可以运行测试:

?- test.
7

修改

在Paulo建议之后,这是一个增强的解决方案,适用于每个 binary 表达式。

user:goal_expansion(X is Y, X is Z) :- expr_bind(Y, Z).

expr_bind(val(A), V) :- !, bind(A, V).
expr_bind(X, Y) :-
     X =.. [F, L, R],  % get operator F and Left,Right expressions
     expr_bind(L, S),  % bind Left expression
     expr_bind(R, T),  % bind Right expression
     Y =.. [F, S, T].  % pack bound expressions back with same operator
expr_bind(X, X).       % oops, I forgot... this clause allows numbers and variables

已将 user 定义为Goal_expansion的目标模块,它可在CLI上运行:

?- R is val(a)*val(b)-val(c).
R = 7.

修改

现在,让我们推广到其他算术运算符,使用与expr_bind用于二进制表达式相同的骨架:

user:goal_expansion(X, Y) :-
     X =.. [F,L,R], memberchk(F, [is, =<, <, =:=, >, >=]),
     expr_bind(L, S),
     expr_bind(R, T),
     Y =.. [F, S, T].

和一元运算符(我无法除掉负号,所以我展示了一种比(= ..)/ 2更简单的方法):

...
expr_bind(-X, -Y) :- expr_bind(X, Y).
expr_bind(X, X).

现在我们得到

?- -val(a)*2 < val(b)-val(c).
true.

答案 1 :(得分:3)

一种方法是使用Logtalk参数对象(Logtalk在SWI-Prolog和其他11个Prolog系统上运行;这使该解决方案具有很高的可移植性)。想法是将每个算术运算定义为可理解eval/1消息的参数对象。首先,我们定义一个协议,该协议将由代表算术运算的对象实现:

:- protocol(eval).

    :- public(eval/1).

:- end_protocol.

基本参数对象可以理解val/1并包含bind/2表:

:- object(val(_X_), implements(eval)).

    eval(X) :-
         bind(_X_, X).

    bind(a, 3).
    bind(b, 4).
    bind(c, 5).

:- end_object.

我在这里仅举例说明算术加法的实现:

:- object(_X_ + _Y_, implements(eval)).

    eval(Result) :-
        _X_::eval(X), _Y_::eval(Y),
        Result is X + Y.

:- end_object.

示例调用(假设上面的实体保存在eval.lgt文件中):

% swilgt
...
?- {eval}.
% [ /Users/pmoura/Desktop/eval.lgt loaded ]
% (0 warnings)
true.

?- (val(a) + val(b))::eval(R).
R = 7.

如果您计划实现除表达式评估之外的更多功能,那么这可能是一个有趣的解决方案。例如。可以在以下位置找到类似的解决方案,但可以对算术表达式进行符号区分:

https://github.com/LogtalkDotOrg/logtalk3/tree/master/examples/symdiff

该解决方案也可以在运行时生成的表达式中使用(基于术语扩展的解决方案通常仅在源文件编译时和顶层使用)。

如果您仅对表达式评估感兴趣,则Capelli的解决方案更紧凑,并保留is/2进行评估。如有必要,还可以使用Logtalk的可移植术语扩展机制使其更具可移植性(但请注意上一段中的警告)。

答案 2 :(得分:1)

这可能不完全是我想要的,但是我有一个主意:

compute(val(X) + val(Y), Out) :-
  bind(X, XVal),
  bind(Y, YVal),
  Out is XVal + YVal.

现在我可以运行以下查询:

?- compute(val(a) + val(c), Out).
Out = 8.

现在,我需要为我感兴趣的每个算术运算定义compute,然后让我的解释器通过它运行表达式。

相关问题