我编写了一个测试程序,其中包含原子和数字之间的绑定(事实)。
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
。
您是否有可能的解决方案或更好的方法或我错过的东西的提示?
答案 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
,然后让我的解释器通过它运行表达式。