在Stack Overflow的Prolog部分中,谓词 if_/3
似乎是fairly popular中的少数主要贡献者。
这个谓词是这样实现的,由@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(error(instantiation_error,_))
).
但是,我无法找到关于此谓词的作用的清晰,简单和简洁的解释,以及与谓词相比的用途。 Prolog if -> then ; else
的经典if-then-else结构。
我发现的大多数链接直接使用这个谓词,并且很少解释为什么它被使用,Prolog中的非专家可以轻松理解。
答案 0 :(得分:20)
在老式的Prolog代码中,以下模式经常出现:
predicate([], ...). predicate([L|Ls], ...) :- condition(L), then(Ls, ...). predicate([L|Ls], ...) :- \+ condition(L), else(Ls, ...).
我在这里使用列表作为发生这种情况的例子(参见例如include/3
,exclude/3
等),尽管模式当然也发生在其他地方。
悲剧如下:
总之,现有的结构和语言特征在某种程度上都不足以表达经常在实践中出现的模式。因此,几十年来,似乎有必要妥协。你可以很好地猜测Prolog社区中“妥协”的方向:几乎无一例外,如果有疑问,正确性会因效率而牺牲。毕竟,只要你的程序很快,谁会关心正确的结果,对吧?因此,在'.'(_, _)
的发明之前,这经常是错误地写成:
predicate([], ...). predicate([L|Ls], ...) :- ( condition(L) -> then(Ls, ...). ; else(Ls, ...). )
错误当然是当 元素被充分实例化时,这可能错误地提交到一个分支,即使两种替代方案在逻辑上都是可行的。出于这个原因,使用if-then-else几乎总是声明性地错误,并且由于它违反了我们对纯Prolog程序期望的最基本属性而大量采用声明式调试方法。
使用if_/3
,您可以将其写为:
predicate([], ...). predicate([L|Ls], ...) :- if_(condition(L), then(Ls, ...), else(Ls, ...)).
和保留所有理想的方面。这是:
价格这是相当实惠的:正如Boris在评论中提到的,您需要实施具体化。我现在对此有一些经验,并且通过一些练习发现它很容易。
每个人都好消息:在很多情况下,if_/3
的格式为condition
或(=)/2
,第一个甚至附带library(reif)
} 免费。
有关更多信息,请参阅Ulrich Neumerkel和Stefan Kral的Indexing dif/2!
答案 1 :(得分:10)
让我们尝试使用if_/3
来解决一个简单的问题;例如,我将尝试在两个列表中对列表进行分区(在谓词p/2
上排序):一个前缀,对于每个元素X
,我们有p(X, true)
,其余(其中,如果列表在p/2
上排序,我们将p(X, false)
。
我将使用库reif
作为here。所以,这是我的程序的完整代码:
:- 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_/2
的第一个参数中,使用当前列表元素评估谓词;第二个论点是true
时发生的事情;第三个参数是false
时发生的事情。
有了这个,我可以在前导a
和休息中分割一个列表:
?- 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).
我刚刚删除了所有不必要的参数。
如果您必须在没有 if_/3
的情况下执行,则必须写下:
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:请仔细阅读代码和顶级互动。