Prolog - 替换和评估
Prolog - subsitution and evaluation
你好编程的好人。
与命令式编程相比,逻辑编程总是令人着迷。
由于追求未知的逻辑程序设计,在遇到算术表达式时遇到了一些问题。
这是我到目前为止完成的代码。
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).
以加操作为例:
arithmeticAdd_expression(Expression, Variable, Value, Result)
?- arithmeticAdd_expression(a+10, a, 1, Result).
?- Result = 11;
?- Result = a + 10.
?- arithmeticAdd_expression(a+10, b, 1, Result).
?- Result = a + 10.
我想实现的是
如果 Expression 中的原子只能由给定的 Variable 和 value 替换,则 Result 只是数字,如上例所示 (Result = 11)。否则,结果只是表达式本身。我的代码问题就在那里,我就是能弄明白。那么,请有人可以帮助我吗?谢谢。
逻辑编程相对于函数式编程的一个重要吸引力在于,您经常可以在多个方向 中使用相同 代码。
这意味着如果输入给定,您不仅可以询问特定结果,还可以询问解决方案一般[=88] =].
但是,要使其发挥作用,您必须考虑一下表示数据的方式。例如,在您的情况下,表达式中仍然是逻辑变量的任何术语可能表示 给定数字 或 应该是的原子与普通数字 或 不同的解释是另外两个术语的加法。这被称为 defaulty 表示,因为您必须决定一个变量在默认情况下 应该表示什么,并且没有办法将其含义限制为仅可能的情况之一。
因此,我建议首先更改表示形式,以便您可以象征性地区分这两种情况。例如,为了在您的案例中表示表达式,让我们采用以下约定:
- atoms 由包装器
a/1
表示
- 数字 由包装器
n/1
. 表示
- 和已经存在的情况一样,
(+)/2
应表示两个表达式的 加法。
所以,像 b+10
这样的默认术语现在应该写成:a(b)+n(10)
。请注意包装器 a/1
和 n/1
的使用,以明确我们正在处理哪种情况。这种表示称为 clean。包装器是任意选择的(尽管是助记符),我们可以使用完全不同的包装器,例如 atom/1
和 number/1
,或 atm/1
和 nmb/1
。关键 属性 只是我们现在可以象征性地 区分 不同的情况,凭借它们的最外层函子和元数。
现在关键优势:使用这样的约定,我们可以写成例如: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了解更多),我们还可以问"What do results look like in general?"
?- 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
。我把它留作一个简单的练习。一旦你有了一个干净的表示,你就可以使用上面的代码而无需更改。
你好编程的好人。
与命令式编程相比,逻辑编程总是令人着迷。 由于追求未知的逻辑程序设计,在遇到算术表达式时遇到了一些问题。
这是我到目前为止完成的代码。
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).
以加操作为例:
arithmeticAdd_expression(Expression, Variable, Value, Result)
?- arithmeticAdd_expression(a+10, a, 1, Result).
?- Result = 11;
?- Result = a + 10.
?- arithmeticAdd_expression(a+10, b, 1, Result).
?- Result = a + 10.
我想实现的是 如果 Expression 中的原子只能由给定的 Variable 和 value 替换,则 Result 只是数字,如上例所示 (Result = 11)。否则,结果只是表达式本身。我的代码问题就在那里,我就是能弄明白。那么,请有人可以帮助我吗?谢谢。
逻辑编程相对于函数式编程的一个重要吸引力在于,您经常可以在多个方向 中使用相同 代码。
这意味着如果输入给定,您不仅可以询问特定结果,还可以询问解决方案一般[=88] =].
但是,要使其发挥作用,您必须考虑一下表示数据的方式。例如,在您的情况下,表达式中仍然是逻辑变量的任何术语可能表示 给定数字 或 应该是的原子与普通数字 或 不同的解释是另外两个术语的加法。这被称为 defaulty 表示,因为您必须决定一个变量在默认情况下 应该表示什么,并且没有办法将其含义限制为仅可能的情况之一。
因此,我建议首先更改表示形式,以便您可以象征性地区分这两种情况。例如,为了在您的案例中表示表达式,让我们采用以下约定:
- atoms 由包装器
a/1
表示
- 数字 由包装器
n/1
. 表示
- 和已经存在的情况一样,
(+)/2
应表示两个表达式的 加法。
所以,像 b+10
这样的默认术语现在应该写成:a(b)+n(10)
。请注意包装器 a/1
和 n/1
的使用,以明确我们正在处理哪种情况。这种表示称为 clean。包装器是任意选择的(尽管是助记符),我们可以使用完全不同的包装器,例如 atom/1
和 number/1
,或 atm/1
和 nmb/1
。关键 属性 只是我们现在可以象征性地 区分 不同的情况,凭借它们的最外层函子和元数。
现在关键优势:使用这样的约定,我们可以写成例如: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了解更多),我们还可以问"What do results look like in general?"
?- 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
。我把它留作一个简单的练习。一旦你有了一个干净的表示,你就可以使用上面的代码而无需更改。