min_member/2的反直觉行为

10 prolog min clpfd

min_member(-Min ,+ List)

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

?- min_member(3, [1,2,X]).
X = 3.
Run Code Online (Sandbox Code Playgroud)

解释当然是变量在标准的术语顺序中位于所有其他术语之前,并且使用统一.但是,报告的解决方案感觉有些不对劲.

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

编辑:

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

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)
    ).
Run Code Online (Sandbox Code Playgroud)

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

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

对此的答案是(至少对我来说)一个明确的"不",而不是"我无法说出来".

这与以下行为属于同一类sort/2:

?- sort([A,B,C], [3,1,2]).
A = 3,
B = 1,
C = 2.
Run Code Online (Sandbox Code Playgroud)

适用相同的技巧:

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

?- var(B), min_member(3, [1,2,A,B]).
B = 3.
Run Code Online (Sandbox Code Playgroud)

fal*_*lse 7

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

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

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

min_member(-Min,+ List)

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

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

?- length(L, 1000000), maplist(=(1),L), min_member(2, L).
Run Code Online (Sandbox Code Playgroud)

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

这种处理输出统一的方式同样在标准中处理.当(否则)声明性描述(内置的第一个)明确引用统一时,您可以发现这些情况,例如

8.5.4 copy_term/2

8.5.4.1描述

copy_term(Term_1, Term_2)如果 与一个重命名的副本(7.1.6.2)的术语Term_2统一
,则为真.T
Term_1

要么

8.4.3 sort/2

8.4.3.1描述

sort(List, Sorted)如果Sorted与(7.1.6.5)
的排序列表统一,则为真List.

以下是内置函数的那些参数(括号中),只能被理解为输出参数.请注意,还有更多有效的输出参数,但某些操作不需要统一的过程.考虑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没有直接提供更高阶的谓词.call/N是两年后发明的(1984年); 但需要几十年才能被普遍接受.正是出于这个原因,Prolog程序员对排序有一种漠不关心的态度.他们通常打算对某种类型的术语进行排序,但更喜欢sort/2用于此目的 - 无需任何额外的错误检查.另一个原因是sort/2几十年后在其他编程语言中击败各种"高效"库的出色表现(我相信STL也有这样的错误).代码中的完整魔法 - 我记得一个变量被命名Omniumgatherum- 没有邀请复制和修改代码.

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

无论SICStus和SWI有min_member/2,但只有SICStus具有min_member/3与指定使用的顺序一个额外的参数.所以目标

?- min_member(=<, M, Ms).
Run Code Online (Sandbox Code Playgroud)

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


脚注:

1我以标准的顺序引用标准,因为这个概念自1982年左右开始存在,而标准是1995年出版的.


mat*_*mat 5

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

?- min_member(X, [X,0]), X = 1.
X = 1.
Run Code Online (Sandbox Code Playgroud)

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

?- X = 1, min_member(X, [X,0]).
false.
Run Code Online (Sandbox Code Playgroud)

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

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

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


Jan*_*ker 4

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

?- X @< 2, X = 3.
X = 3.
Run Code Online (Sandbox Code Playgroud)

大多数使用-Value参数注释的谓词表示与pred(Value)相同pred(Var), Value = Var。这是另一个例子:

?- sort([2,X], [3,2]).
X = 3.
Run Code Online (Sandbox Code Playgroud)

如果输入是ground 的话,这些谓词仅表示干净的关系。要求输入是基础的要求太多了,因为只要用户意识到他/她不应该进一步实例化任何有序项,它们就可以有意义地与变量一起使用。从这个意义上说,我不同意@mat。我确实同意,限制肯定可以使其中一些关系变得健全。