我使用 "cut" 太多了吗?
Am I using "cut" too much?
我是 Prolog 和一般逻辑编程的新手,为了好玩,我正在编写一个小的定理证明器,为此我编写了一个规范化程序。我希望这个过程是确定的和坚定的,所以我写了这样的东西:
normal(S, R):- var(S), !, S = R.
normal(true(S), R):- !, normal(S, R).
normal(!(S), R):- !, normal(false(S), R).
normal(P => Q, R):- !, normal(false(P and false(Q)), R).
normal(A or B, R):- !, normal(false(false(A) and false(B)), R).
normal(false(S), R):- !, normal(S, NS), normal_false(NS, R).
normal(A and B, R):- !, normal(A, NA), normal(B, NB), normal_and(NA, NB, R).
normal(S, S):- !.
normal_false(S, R):- var(S), !, S = false(R).
normal_false(false(S), S):- !.
normal_false(true, false):- !.
normal_false(false, true):- !.
normal_false(S, false(S)):- !.
normal_and(A, B, R):- var(A), var(B), !, R = A and B.
normal_and(A, true, A):- !.
normal_and(true, B, B):- !.
normal_and(_, false, false):- !.
normal_and(false, _, false):- !.
normal_and(A, B, A and B):- !.
我现在想知道这样做是否正确。它目前似乎可以工作,但我想知道这是否不符合我在某些边缘情况下所期望的属性,我编写它的方式是否可能存在一些性能问题,或者这是否只是糟糕的编码style/practice一般。
使用剪切时会出现几个问题。
你在写什么样的 Prolog 程序?
这里有几种可能性。从 纯程序 开始,可以通过仅考虑解决方案集以声明方式理解。在这样的程序中,有很多理想的属性,但你的显然不是其中之一。毕竟,目标 normal_false(false, true)
成功了,但它的泛化 normal_false(V, true)
失败了(如预期的那样)。所以 normal_false/2
不是纯关系。
您的程序将 Prolog 变量视为对象。它更像是一个 元逻辑程序 这并不奇怪,因为您想通过重用 Prolog 的基础结构来实现证明者。在这里,仍然有许多不错的属性,例如,您仍然可以使用 failure-slice 来确定未终止的原因。但是,使用 var/1
特别容易出错,因为 Prolog 无法枚举所有可能的解决方案,因此很容易忘记一两个案例。在您的示例中(添加缺少的运算符声明后):
?- normal(X and true, N).
X = N. % expected
?- normal(true and X, N).
X = true, N = true. % unexpected
cut 切掉什么?
在 normal/2
的第一个子句 normal(S, R):- var(S), !, ...
中,剪切确保所有后续子句仅考虑大小写 nonvar(S)
.
可疑的是最后一个子句 normal(S, S):- !.
如果参数统一则该子句将被删除。乍一看,这并没有什么区别,因为它是最后一个子句。但是在 freeze(S, ( T = 1 ; T = 2 ) )
的情况下你会得到不同的结果。但除此之外,normal/2
似乎还不错。
normal_and/2
中的删减更令人信服。除了以上异常,这个定义不稳。毕竟,结果受其最后一个参数的影响:
?- normal(true and true, N).
N = true. % expected
?- normal(true and true, true and true).
true. % unexpected
此处剪辑来得太晚了。
我是 Prolog 和一般逻辑编程的新手,为了好玩,我正在编写一个小的定理证明器,为此我编写了一个规范化程序。我希望这个过程是确定的和坚定的,所以我写了这样的东西:
normal(S, R):- var(S), !, S = R.
normal(true(S), R):- !, normal(S, R).
normal(!(S), R):- !, normal(false(S), R).
normal(P => Q, R):- !, normal(false(P and false(Q)), R).
normal(A or B, R):- !, normal(false(false(A) and false(B)), R).
normal(false(S), R):- !, normal(S, NS), normal_false(NS, R).
normal(A and B, R):- !, normal(A, NA), normal(B, NB), normal_and(NA, NB, R).
normal(S, S):- !.
normal_false(S, R):- var(S), !, S = false(R).
normal_false(false(S), S):- !.
normal_false(true, false):- !.
normal_false(false, true):- !.
normal_false(S, false(S)):- !.
normal_and(A, B, R):- var(A), var(B), !, R = A and B.
normal_and(A, true, A):- !.
normal_and(true, B, B):- !.
normal_and(_, false, false):- !.
normal_and(false, _, false):- !.
normal_and(A, B, A and B):- !.
我现在想知道这样做是否正确。它目前似乎可以工作,但我想知道这是否不符合我在某些边缘情况下所期望的属性,我编写它的方式是否可能存在一些性能问题,或者这是否只是糟糕的编码style/practice一般。
使用剪切时会出现几个问题。
你在写什么样的 Prolog 程序?
这里有几种可能性。从 纯程序 开始,可以通过仅考虑解决方案集以声明方式理解。在这样的程序中,有很多理想的属性,但你的显然不是其中之一。毕竟,目标 normal_false(false, true)
成功了,但它的泛化 normal_false(V, true)
失败了(如预期的那样)。所以 normal_false/2
不是纯关系。
您的程序将 Prolog 变量视为对象。它更像是一个 元逻辑程序 这并不奇怪,因为您想通过重用 Prolog 的基础结构来实现证明者。在这里,仍然有许多不错的属性,例如,您仍然可以使用 failure-slice 来确定未终止的原因。但是,使用 var/1
特别容易出错,因为 Prolog 无法枚举所有可能的解决方案,因此很容易忘记一两个案例。在您的示例中(添加缺少的运算符声明后):
?- normal(X and true, N).
X = N. % expected
?- normal(true and X, N).
X = true, N = true. % unexpected
cut 切掉什么?
在 normal/2
的第一个子句 normal(S, R):- var(S), !, ...
中,剪切确保所有后续子句仅考虑大小写 nonvar(S)
.
可疑的是最后一个子句 normal(S, S):- !.
如果参数统一则该子句将被删除。乍一看,这并没有什么区别,因为它是最后一个子句。但是在 freeze(S, ( T = 1 ; T = 2 ) )
的情况下你会得到不同的结果。但除此之外,normal/2
似乎还不错。
normal_and/2
中的删减更令人信服。除了以上异常,这个定义不稳。毕竟,结果受其最后一个参数的影响:
?- normal(true and true, N).
N = true. % expected
?- normal(true and true, true and true).
true. % unexpected
此处剪辑来得太晚了。