If statement 如果/3有什么用?
谓词似乎是堆栈溢出的Prolog部分的少数几个主要贡献者之一 此谓词是通过@false实现的:If statement 如果/3有什么用?,if-statement,prolog,logical-purity,If Statement,Prolog,Logical Purity,谓词似乎是堆栈溢出的Prolog部分的少数几个主要贡献者之一 此谓词是通过@false实现的: if_(If_1, Then_0, Else_0) :- call(If_1, T), ( T == true -> call(Then_0) ; T == false -> call(Else_0) ; nonvar(T) -> throw(error(type_error(boolean,T),_)) ; /* var(T) */ throw
if_(If_1, Then_0, Else_0) :-
call(If_1, T),
( T == true -> call(Then_0)
; T == false -> call(Else_0)
; nonvar(T) -> throw(error(type_error(boolean,T),_))
; /* var(T) */ throw(error(instantiation_error,_))
).
然而,我一直无法找到一个清晰、简单、简洁的解释这个谓词的作用,以及它与Prologif->then的经典if-then-else结构相比的用途;else
我发现的大多数链接都直接使用了这个谓词,并且很少解释为什么会使用这个谓词,这是Prolog的非专家很容易理解的。在老式的Prolog代码中,以下模式经常出现: predicate([], ...). predicate([L|Ls], ...) :- condition(L), then(Ls, ...). predicate([L|Ls], ...) :- \+ condition(L), else(Ls, ...). 当然,这方面的错误是,当元素没有充分实例化时,这可能会错误地提交到一个分支,即使两个备选方案在逻辑上都是可能的。由于这个原因,使用if-then-else几乎总是声明性错误,并且由于它违反了我们期望从纯Prolog程序获得的最基本属性,因此在声明性调试方法中占据了很大的位置
使用
if_u3;/3
,您可以这样写:
predicate([], ...).
predicate([L|Ls], ...) :-
if_(condition(L),
then(Ls, ...),
else(Ls, ...)).
谓词([],…)。
谓词([L | Ls],…):-
如果(条件(L),
然后(Ls,…),
其他(Ls,…)。
并保留所有需要的方面。这是:
- 确定性如果一切都可以安全决定
- 高效,因为它甚至不会创建选择点
- 完成,因为您从未错误地提交到某个特定分支
条件
的形式是(=)/2
,或者(#=)/2
,第一种甚至是免费提供的
欲了解更多信息,请参阅乌尔里希·纽默克尔和斯特凡克尔 让我们试着用
if_u/3
解决一个简单的问题;例如,我将尝试将一个列表(按谓词p/2
排序)划分为两个列表:一个前缀,其中对于每个元素X
,我们有p(X,true)
,另一个前缀(如果列表按p/2
排序,我们将有p(X,false)
)
我将使用库reif
as。因此,以下是我程序的完整代码:
:- use_module(reif).
pred_prefix(Pred_1, List, L_true, L_false) :-
pred_prefix_aux(List, Pred_1, L_true, L_false).
pred_prefix_aux([], _, [], []).
pred_prefix_aux([X|Xs], Pred_1, True, False) :-
if_( call(Pred_1, X),
( True = [X|True0],
pred_prefix_aux(Xs, Pred_1, True0, False)
),
( True = [],
False = [X|Xs]
)
).
传递到此元谓词的谓词将包含两个参数:第一个是当前列表元素,第二个是true
或false
。理想情况下,此谓词将始终成功,并且不会留下选择点
在if\u2
的第一个参数中,谓词使用当前列表元素进行计算;第二个参数是true
时发生的情况;第三个参数是false
时发生的情况
有了它,我可以在前导a
s中拆分一个列表,然后再拆分一个rest:
?- pred_prefix([X, B]>>(=(a, X, B)), [a,a,b], T, F).
T = [a, a],
F = [b].
?- pred_prefix([X, B]>>(=(a, X, B)), [b,c,d], T, F).
T = [],
F = [b, c, d].
?- pred_prefix([X, B]>>(=(a, X, B)), [b,a], T, F).
T = [],
F = [b, a].
?- pred_prefix([X, B]>>(=(a, X, B)), List, T, F).
List = T, T = F, F = [] ;
List = T, T = [a],
F = [] ;
List = T, T = [a, a],
F = [] ;
List = T, T = [a, a, a],
F = [] .
如何摆脱前导0,例如:
?- pred_prefix([X, B]>>(=(0, X, B)), [0,0,1,2,0,3], _, F).
F = [1, 2, 0, 3].
当然,这本可以写得简单得多:
drop_leading_zeros([], []).
drop_leading_zeros([X|Xs], Rest) :-
if_(=(0, X), drop_leading_zeros(Xs, Rest), [X|Xs] = Rest).
在这里,我刚刚删除了所有不必要的论点
如果您必须在没有的情况下执行此操作,则必须编写:
drop_leading_zeros_a([], []).
drop_leading_zeros_a([X|Xs], Rest) :-
=(0, X, T),
( T == true -> drop_leading_zeros_a(Xs, Rest)
; T == false -> [X|Xs] = Rest
).
在这里,我们假设=/3
确实总是在没有选择点的情况下成功,T
总是true
或false
如果我们也没有=/3
,你会写:
drop_leading_zeros_full([], []).
drop_leading_zeros_full([X|Xs], Rest) :-
( X == 0 -> T = true
; X \= 0 -> T = false
; T = true, X = 0
; T = false, dif(0, X)
),
( T == true -> drop_leading_zeros_full(Xs, Rest)
; T == false -> [X|Xs] = Rest
).
这并不理想,但现在至少你可以在一个地方亲眼看到到底发生了什么
PS:请仔细阅读代码和顶层交互。要理解这一点,您需要成为Prolog以外的专家;-)神秘的名称也没有帮助。请记住,第一个参数不可能是任何东西:它需要是一个谓词,在确定性上成功,并将其第二个参数与true
或false
(“具体化”)统一起来,否则会抛出错误。PS:我并不是说它不是一个有用的谓词,但它的内容远远超出了人们的想象。隐藏在间接层之下的丑陋是Prolog编程风格的一个共同特征,这是由您所说的“少数主要贡献者”所提倡的。我希望不要给人太负面的印象,我确实认为拥有这样的结构并尝试将其应用于现有和新的问题是一件好事。PPS:特别是,研究=/3
,就在if\u3
下面,在这里,你可以看到如何编写一个谓词,与if./3
.D'abord配合得很好,你读了吗?@Fatalize公平地说,那篇文章中有大量的代码,文本只提供了必要的上下文。对于SWI,有。然而,请注意,在SWI中,元谓词的语义是高度不稳定的(与SICStus相反)。从这个答案可以得出这样的结论:人们应该总是使用if\uuuu
而不是if->then;else
(不包括特定情况),类似于总是使用CLP(FD)
而不是低级算术(不包括特定情况)?我会这样说:if->then;否则
几乎在所有情况下都会导致错误的程序。使用这种额外的逻辑结构的唯一原因是为了使程序更高效,通常是以正确性为代价的<另一方面,代码>如果将正确性与可接受的效率结合起来,价格非常合理:需要具体化(通常是(=)/3
,已经包含在库中)和学习新构造。最好的方法总是尽可能地使用模式匹配。如果不可能,<
drop_leading_zeros_full([], []).
drop_leading_zeros_full([X|Xs], Rest) :-
( X == 0 -> T = true
; X \= 0 -> T = false
; T = true, X = 0
; T = false, dif(0, X)
),
( T == true -> drop_leading_zeros_full(Xs, Rest)
; T == false -> [X|Xs] = Rest
).