好的编程人员。
与命令式编程相比,逻辑编程总是令人着迷。 由于追求未知的逻辑编程,遇到算术表达式时会遇到一些问题。
这是我到目前为止所做的代码。
hy
以添加操作为例:
number_atom(N) :-
(number(N) -> functor(N, _, _); functor(N, _, _), atom(N)).
arithmeticAdd_expression(V,V,Val,Val).
arithmeticAdd_expression(N, _Var, _Val, N) :-
number_atom(N).
arithmeticAdd_expression(X+Y, Var, Val, R) :-
arithmeticAdd_expression(X, Var, Val, RX),
arithmeticAdd_expression(Y, Var, Val, RY),
(number(RX), number(RY) -> R is RX + RY; R = RX + RY).
我想要达到的目标是 如果表达式中的原子只能被给定的变量和值替换,那么结果就是数字,就像上面显示的例子一样(结果= 11)。否则,结果只是表达式本身。我的代码问题出在那里,我可以搞清楚。所以,请有人可以帮助我吗?谢谢。
答案 0 :(得分:3)
逻辑编程相对于函数式编程的一个重要吸引力在于,您通常可以在多个方向中使用相同的代码。
这意味着,如果输入给出,您不仅可以询问特定结果,还可以询问解决方案一般的方式。
但是,要实现这一点,您必须考虑一下您表示数据的方式。例如,在您的情况下,表达式中仍然是逻辑变量的任何术语都可以表示 给定数字或原子应该被解释为与普通数字不同或另外两个术语。这称为默认表示,因为您必须确定变量应该默认 ,,并且没有办法将其含义仅限于其中一种可能的情况。
因此,我建议首先更改表示,以便符号区分这两种情况。例如,要表示您的案例中的表达式,让我们采用以下约定:
a/1
n/1
表示。(+)/2
将表示两个表达式的添加。因此,像b+10
这样的默认术语现在应写为: a(b)+n(10)
。请注意使用包装器a/1
和n/1
来说明我们正在处理哪种情况。这种表示称为 clean 。包装器是任意选择(虽然是助记),我们可以使用完全不同的包装器,例如atom/1
和number/1
,或atm/1
和nmb/1
。关键属性只是我们现在可以凭借其最外面的算符和arity象征性地区分不同的案例。
现在关键优势:使用这样的约定,我们可以编写例如:a(X)+n(Y)
。这是早期术语的泛化。但是,它只包含了比X+Y
更多的信息,因为在后一种情况下,我们已经忘记了这些变量的含义,而在前一种情况下,这种区别仍然存在。
现在,假设在表达式中使用了这个约定,它描述了不同的情况变得直截了当:
expression_result(n(N), _, _, n(N)). expression_result(a(A), A, N, n(N)). expression_result(a(A), Var, _, a(A)) :- dif(A, Var). expression_result(X+Y, Var, Val, R) :- expression_result(X, Var, Val, RX), expression_result(Y, Var, Val, RY), addition(RX, RY, R). addition(n(X), n(Y), n(Z)) :- Z #= X + Y. addition(a(X), Y, a(X)+Y). addition(X, a(Y), X+a(Y)).
请注意,我们现在可以使用模式匹配来区分这些案例。不再是if-then-elses,不再需要atom/1
或number/1
测试。
您的测试用例按预期工作:
?- expression_result(a(a)+n(10), a, 1, Result). Result = n(11) ; false. ?- expression_result(a(a)+n(10), b, 1, Result). Result = a(a)+n(10) ; false.
现在关键优势:有了这样一个纯粹的程序(请参阅logical-purity了解更多信息),我们也可以问“结果看起来像一般? “
?- expression_result(Expr, Var, N, R). Expr = R, R = n(_1174) ; Expr = a(Var), R = n(N) ; Expr = R, R = a(_1698), dif(_1698, Var) ; Expr = n(_1852)+n(_1856), R = n(_1896), _1852+_1856#=_1896 ; Expr = n(_2090)+a(Var), R = n(_2134), _2090+N#=_2134 .
在这里,我使用了所有参数的逻辑变量,我从这个程序得到了相当一般的答案。这就是我使用clpfd约束进行声明性整数运算的原因。
因此,通过使用干净的表示法并使用上面的代码,可以很容易地解决您的直接问题。
只剩下一个非常小的挑战:也许你实际上希望使用默认表示,例如c+10
(而不是a(c)+n(10)
)。您当前面临的任务是将默认表示转换为干净的表示,例如通过谓词defaulty_clean/2
。我把这作为一个简单的练习。一旦你有一个干净的表示,你可以使用上面的代码而无需更改。