Parameters 如何在Prolog中将一个谓词作为另一个谓词的参数传递?

Parameters 如何在Prolog中将一个谓词作为另一个谓词的参数传递?,parameters,prolog,expression,predicate,Parameters,Prolog,Expression,Predicate,我有以下3个谓词: times(X, Y):- Result is X * Y. minus(X, Y):- Result is X - Y. plus(X, Y):- Result is X + Y. 例如,我想通过加(X,Y)中的次(2,2)这样的加(次(2,2),减(X,Y))首先,你需要理解Prolog谓词实际上描述的是什么:它们不是函数,而是值之间的关系。因此,如果您想要有一个用于加法的谓词,那么它需要是一个具有三个参数的谓词:加上(a,B,Sum)。因此,在

我有以下3个谓词:

times(X, Y):-
    Result is X * Y.
minus(X, Y):-
    Result is X - Y.
plus(X, Y):-
    Result is X + Y.

例如,我想通过
加(X,Y)
中的
次(2,2)
这样的
加(次(2,2),减(X,Y))

首先,你需要理解Prolog谓词实际上描述的是什么:它们不是函数,而是值之间的关系。因此,如果您想要有一个用于加法的谓词,那么它需要是一个具有三个参数的谓词:
加上(a,B,Sum)
。因此,在Prolog中,结果不像在许多其他语言中那样是免费的

而不是

plus(X, Y):-
   Result is X + Y.
你需要写作

plus(X, Y, Result) :-
   Result is X + Y.
接下来要做的是进一步传递中间值。在支持函数的语言中,这是一项非常简单的任务。但在Prolog中,函数符号并不令人惊讶。因此,要么对关系中的所有内容进行编码,要么实现自己版本的
(is)/2
。 对于初学者,宁可坚持第一种选择。因此

..., plus(times(2,2), minus(X, Y)) ...
现在写

..., times(2, 2, R), plus(R, minus(X, Y), S), ...
最终结果为
S

请注意,如果直接在关系中对表达式进行编码,则必须引入中介变量,如上面的
R

很明显,(直接)关系表示法对于这样的目的来说并不那么优雅。对于非常精确的区域,Prolog还提供表达式,特别是
(is)/2
,然后在
库(clpfd)
的更一般的设置中


但是,作为初学者,最好先习惯关系表示法。首先学习可能是更好的主意。在使用之前和使用
(is)/2

之前,我不清楚您的问题标题和问题文本之间的关系,我认为@false可能是对的,这里对Prolog有一个更基本的误解。我不知道这是否真的满足了你的需求,但这里的替代方案是编写你自己的评估器

eval(times(X,Y), Result) :-
    eval(X, XResult),
    eval(Y, YResult),
    Result is XResult * YResult.
eval(minus(X,Y), Result) :-
    eval(X, XResult),
    eval(Y, YResult),
    Result is XResult - YResult.
eval(plus(X,Y), Result) :-
    eval(X, XResult),
    eval(Y, YResult),
    Result is XResult + YResult.
需要在每个规则的主体内递归调用
eval/2
,以处理像
加(乘以(2,2),减(X,Y))
这样的情况。然后你需要一个数字规则:

eval(Num, Num) :- number(Num).
这对于以下情况非常有效:

?- eval(plus(times(2,2), minus(7,1)), Result).
Result = 10.
?- eval(plus(times(2,2), minus(X,Y)), Result).
ERROR: Out of local stack
这样的情况对你没有任何好处:

?- eval(plus(times(2,2), minus(7,1)), Result).
Result = 10.
?- eval(plus(times(2,2), minus(X,Y)), Result).
ERROR: Out of local stack
当然,如果我们在到达之前为X和Y建立了绑定,这将是可行的,但是如果您希望它为X和Y生成可能的解决方案,那么您就不走运了,您需要使用
clpfd
。这个奇怪错误的原因,如果你追踪的话,是因为
number(X)
X
未绑定时是false,所以它实际上生成了新的子句,包括times,减号和加号结构,并尝试它们,这不是你在求值器中想要的

eval(times(X,Y), Result) :-
    eval(X, XResult),
    eval(Y, YResult),
    Result is XResult * YResult.
eval(minus(X,Y), Result) :-
    eval(X, XResult),
    eval(Y, YResult),
    Result is XResult - YResult.
eval(plus(X,Y), Result) :-
    eval(X, XResult),
    eval(Y, YResult),
    Result is XResult + YResult.
编辑:实现
printterm/1。

eval/2
谓词显示了如何执行递归树遍历。制作漂亮的打印机的原理是一样的。我很懒,所以我只画草图,你得自己填细节

printterm(T) :- format_term(T, Formatted), write(Formatted), nl.

format_term(plus(X,Y), Formatted) :- 
  format_term(X, XFormatted),
  format_term(Y, YFormatted),
  format(atom(Formatted), '(~a + ~a)', [XFormatted, YFormatted]).

% other format_term clauses here for other arithmetic expressions

format_term(X, X) :- number(X).

希望这有帮助

Daniel的解决方案的另一种选择是将
加(乘以(2,2),减(5,3))
等术语视为支持一组操作的表达式对象,如“evaluate”或“differention”。这允许您编写目标,例如:

| ?- plus(times(2,2), minus(5,3))::evaluate(Result).
Result = 6
yes

其优点是,适用于特定表达式的所有操作的规则(例如,
times/2
)都整齐地封装在相应的对象中。也就是说,不是通过操作对谓词进行聚类,而是通过(基本)表达式对它们进行聚类。沿着这些思路,但在派生和简化表达式的上下文中可以找到一个示例。

请阅读基于@capelical上面所写内容的内容,在SWI prolog中,您可以使用
call
调用现有谓词,例如包含此谓词的变量,并将其传递给参数列表。对于创建Prolog泛型非常有用!您可以使用
call(操作符,参数列表)
call(操作符,Arg1,Arg2)
来创建函数,这些函数执行操作,返回结果,但您不知道或不想知道,只要结果是某种类型的,例如
+(1,输出),调用(算术(Input1,Input2,Output)
。执行一些返回数字的函数/3,在结果中加1。神圣的垃圾
symdiff
很酷!
?-(x**2+2*x+1)::diff(D),D::simplify(simplify).
->
D=2*x**1*1+2*1,Sim=2*x+2.
在Logtalk中将
.*.
声明为对象的功能真是太棒了!谢谢。参数化对象用途非常广泛。例如,Thx,您得到了一个printterm/1谓词的idé,该谓词可以用于这样的命令?-printterm(加(减(8,2),乘以(4,-3))。打印输出:((8-2)+(4 * -3))