min_member / 2的反直觉行为

时间:2014-12-09 08:54:25

标签: prolog min clpfd

min_member(-Min, +List)

如果Min是标准术语中最小的成员,则为真。如果List为空,则失败。

?- min_member(3, [1,2,X]).
X = 3.

解释当然是变量在标准的术语顺序中位于所有其他术语之前,并且使用了统一。但是,报告的解决方案在某种程度上是错误的。

怎么可以说是合理的?我该如何解释这个解决方案?

编辑:

阻止min_member/2成功使用此解决方案的一种方法是更改​​标准库(SWI-Prolog)implementation,如下所示:

xmin_member(Min, [H|T]) :-
    xmin_member_(T, H, Min).

xmin_member_([], Min0, Min) :-
    (   var(Min0), nonvar(Min)
    ->  fail
    ;   Min = Min0
    ).
xmin_member_([H|T], Min0, Min) :-
    (   H @>= Min0 
    ->  xmin_member_(T, Min0, Min)
    ;   xmin_member_(T, H, Min)
    ).

失败而不是抛出实例化错误背后的基本原理(@mat在他的回答中建议,如果我理解正确的话)是这是一个明确的问题:

"当X是自由变量时,3是[1,2,X]的最小成员吗?"

对此的回答(至少对我来说)是一个明确的" No"而不是"我无法说出来。"

这与sort/2

属于同一类行为
?- sort([A,B,C], [3,1,2]).
A = 3,
B = 1,
C = 2.

适用相同的技巧:

?- min_member(3, [1,2,A,B]).
A = 3.

?- var(B), min_member(3, [1,2,A,B]).
B = 3.

6 个答案:

答案 0 :(得分:7)

混淆的实际来源是a common problem和普通的Prolog代码。对于Prolog谓词的纯度或杂质,没有清晰的,普遍接受的分类。在手册中,同样在标准中,纯粹和不纯的内置插件很乐意混合在一起。出于这个原因,事情经常被混淆,并且谈论应该是什么情况,什么不是,往往导致无益的讨论。

  

怎么可以说是合理的?我该如何解释这个解决方案?

首先,查看“模式声明”或“模式指示器”:

min_member(-Min,+ List)

在SWI文档中,这描述了程序员如何使用该谓词的方式。因此,第一个参数应该是未实例化的(并且可能在目标中也没有区分),第二个参数应该被实例化为某种类型的列表。对于所有其他用途,您可以自己动手。系统假定您能够自己检查。你真的能这样做吗?就我而言,我对此有很多困难。 ISO还有different systemoriginates in DEC10

此外,对于未指明的案例,实施尝试“合理”。特别是,它试图在第一个论点中坚定不移。因此,首先计算最小值,而不考虑Min的值。然后,结果值与Min统一。这种针对滥用的强大功能通常需要付出代价。在这种情况下,min_member/2始终必须访问整个列表。无论这是否有用。考虑

?- length(L, 1000000), maplist(=(1),L), min_member(2, L).

显然,2不是L的最小值。这可以通过仅考虑列表的第一个元素来检测。由于定义的一般性,必须访问整个列表。

这种处理输出统一的方式同样在标准中处理。当(否则)声明性描述(which is the first of a built-in)明确引用统一时,您可以发现这些情况,例如

  

8.5.4 copy_term / 2

     

8.5.4.1说明

     如果copy_term(Term_1, Term_2)统一了,则

Term_2为真   术语T是一个重命名的副本(7.1.6.2)   Term_1

  

8.4.3 sort / 2

     

8.4.3.1说明

     如果sort(List, Sorted)
结合,则

Sorted为真   排序的List(7.1.6.5)列表。

以下是内置函数的那些参数(括号中),只能被理解为输出参数。请注意,还有更多有效的输出参数,但在某些操作之后不需要统一的过程。考虑8.5.2 arg/3(3)或8.2.1 (=)/2(2)或(1)。

8.5.4 1 copy_term/2(2), 8.4.2 compare/3 (1)8.4.3 sort/2 (2)8.4.4 keysort/2 (2), 8.10.1 findall/3(3), 8.10.2 bagof/3(3), 8.10.3 setof/3(3)。

对于你的直接问题,还有一些更基本的问题:

学期订单

历史上,“标准”术语顺序 1 已被定义为允许setof/3sort/2约定于1982年。(在此之前,如1978年,它在DEC10 手册用户指南中未提及。)

从1982年开始,用于实现其他订单的定期订单经常(erm,ab-),特别是因为DEC10没有直接提供更高阶的谓词。两年后(1984年)发明了call/N;但需要几十年才能被普遍接受。正是出于这个原因,Prolog程序员对排序有一种漠不关心的态度。通常他们打算对某种类型的术语进行排序,但更喜欢使用sort/2来实现此目的 - 无需任何其他错误检查。另一个原因是几十年后sort/2在其他编程语言中击败各种“高效”库的出色表现(我相信STL也有这样的错误)。代码中的完整魔法 - 我记得一个变量名为Omniumgatherum - 没有邀请复制和修改代码。

术语顺序有两个问题:变量(可以进一步实例化以使当前排序无效)和无限期。两者都在当前实现中处理而不会产生错误,但结果仍未定义。然而,程序员认为一切都会成功。理想情况下,会产生比较谓词 不明原因like this suggestion的实例化错误。无可比拟的无限期的另一个错误。

SICStus和SWI都有min_member/2,但只有SICStus有min_member/3并附加参数来指定所使用的顺序。所以目标

?- min_member(=<, M, Ms).

表现得更符合您的期望,但仅限于数字(加上算术表达式)。


脚注:

1我引用标准,按标准术语顺序,这个概念自1982年左右开始存在,而标准是1995年出版的。

答案 1 :(得分:5)

显然min_member/2不是真正的关系:

?- min_member(X, [X,0]), X = 1.
X = 1.

然而,在简单地通过(非常理想的)联合的交换性交换两个目标之后,我们得到:

?- X = 1, min_member(X, [X,0]).
false.

正如你正确观察到的那样,这显然非常糟糕。

约束是针对此类问题的声明性解决方案。在整数的情况下,有限域约束是这类问题的完全声明性解决方案。

如果没有约束,最好在我们知道太少而无法给出正确答案时抛出实例化错误

答案 2 :(得分:4)

这是许多(所有?)谓词的共同属性,它依赖于术语的标准顺序,而统一后两个术语之间的顺序可能会发生变化。基线是下面的连接,无法恢复:

?- X @< 2, X = 3.
X = 3.

对于参数使用-Value注释的大多数谓词都说pred(Value)是相同的 为pred(Var), Value = Var。这是另一个例子:

?- sort([2,X], [3,2]).
X = 3.

如果输入 ground ,这些谓词仅表示干净的关系。要求输入接地是太多了,因为它们可以有意义地与变量一起使用,只要用户知道他/她不应该进一步实例化任何有序的术语。从这个意义上说,我不同意@mat。我同意约束肯定会使这些关系中的某些关系健全。

答案 3 :(得分:2)

我希望我的第三个答案不会偏离主题。我没有编辑前两个中的一个,因为我认为这是一个完全不同的想法。我想知道这种不良行为是否存在:

?- min_member(X, [A, B]), A = 3, B = 2.
X = A, A = 3,
B = 2.
如果在AB实例化的情况下可以推迟某些条件,则可以避免

promise_relation(Rel_2, X, Y):-
    call(Rel_2, X, Y),
    when(ground(X), call(Rel_2, X, Y)),
    when(ground(Y), call(Rel_2, X, Y)).

min_member_1(Min, Lst):-
    member(Min, Lst),
    maplist(promise_relation(@=<, Min), Lst).

我希望min_member_1(?Min, ?Lst)能够表达一种关系,即Min始终低于Lst中的任何元素(标准顺序)。

?- min_member_1(X, L), L = [_,2,3,4], X = 1.
X = 1,
L = [1, 2, 3, 4] .

如果变量稍后被实例化,它们被绑定的顺序变得很重要,因为可以在自由变量和实例变量之间进行比较。

?- min_member_1(X, [A,B,C]), B is 3, C is 4, A is 1.
X = A, A = 1,
B = 3,
C = 4 ;
false.
?- min_member_1(X, [A,B,C]), A is 1, B is 3, C is 4.
false.

但是,可以通过将所有这些目标统一在同一目标中来避免这种情况:

?- min_member_1(X, [A,B,C]), [A, B, C] = [1, 3, 4].
X = A, A = 1,
B = 3,
C = 4 ;
false.

<强>版本

如果比较仅用于实例化变量,则可以更改promise_relation/3以仅在两个变量实例化时检查关系:

promise_relation(Rel_2, X, Y):-
    when((ground(X), ground(Y)), call(Rel_2, X, Y)).

一个简单的测试:

?- L = [_, _, _, _], min_member_1(X, L), L = [3,4,1,2].
L = [3, 4, 1, 2],
X = 1 ;
false.

由于错误的评论和建议,我们修改了首发帖子。

答案 4 :(得分:1)

这就是min_member/2的实施方式:

min_member(Min, [H|T]) :-
    min_member_(T, H, Min).

min_member_([], Min, Min).
min_member_([H|T], Min0, Min) :-
    (   H @>= Min0
    ->  min_member_(T, Min0, Min)
    ;   min_member_(T, H, Min)
    ).

所以似乎min_member/2实际上尝试统一 Min(第一个参数),其中List中的最小元素是标准的术语顺序。

答案 5 :(得分:0)

我对您的xmin_member实施情况有所了解。它在此查询中失败:

?- xmin_member(1, [X, 2, 3]).
false.

当列表可能包含自由变量时,我尝试包含这种情况。所以,我想出了这个:

ymin_member(Min, Lst):-
    member(Min, Lst),
    maplist(@=<(Min), Lst).

当然,在效率方面更糟糕,但它适用于这种情况:

?- ymin_member(1, [X, 2, 3]).
X = 1 ;
false.

?- ymin_member(X, [X, 2, 3]).
true ;
X = 2 ;
false.