在学习Prolog的过程中,我正在尝试做一些非常简单的事情。但是我很困惑为什么这样做:
?- length( L, 3 ),
maplist( member, L, [[1,2,3], [1,2,3], [1,2,3]] ),
sort( L, L ).
L = [1, 2, 3] ;
但是使用clpfd看似等效的变化不会:
?- use_module(library(clpfd)).
?- length( L, 3 ), L ins 1..3, sort( L, L ).
L = [_G5891, _G5894, _G5897],
_G5891 in 1..3,
_G5894 in 1..3,
_G5897 in 1..3.
这有效:
?- length( L, 3 ), L ins 1..3, chain( L, #< ).
L = [1, 2, 3].
但这不是:
?- length( L, 3 ), L ins 1..3, chain( L, #=< ), all_different( L ).
L = [_G6519, _G6522, _G6525],
_G6519 in 1..3,
all_different([_G6519, _G6522, _G6525]),
_G6522#>=_G6519,
_G6522 in 1..3,
_G6525#>=_G6522,
_G6525 in 1..3.
有人可以解释为什么不工作的例子没有给我我期待的结果吗?
作为一个附带问题,是否有更简洁的方法来重写上面第一个示例中的maplist
表达式?
答案 0 :(得分:1)
内置谓词sort/2
根据包含变量所定义的术语顺序有效地对列表进行排序。因此,不能将此谓词视为实现具体关系。仅在特殊情况下,例如当第一个参数是变量free(即ground(L)
成功)且术语是非循环(acyclic_term(L)
成功)时,内置对应于明显的关系。
这是另一个奇怪的案例sort([X,1],[1,1])
成功,统一X = 1
。
答案 1 :(得分:1)
您正在使用的系统(SWI-Prolog + library(clpfd))是将整数有限域约束求解器嵌入到Prolog系统中的一个示例(其他一些是CHIP,ECLiPSe + fd / ic,B- Prolog,SICStus + clpfd)。这种嵌入是非常自然的,因为许多Prolog概念直接映射到约束编程中的概念,为我们提供了约束逻辑编程(CLP)。
但是,在将CLPR用于CLP时,应该避免使用普通Prolog的许多功能。其中包括剪切(包括if-then-else结构中隐藏的那些和\+/2
否定),还包括元逻辑操作案件。出于我们的目的,元逻辑表示将变量视为对象的任何原语(而不仅仅是作为值的占位符)。这些示例包括var/1
,==/2
,@</2
,以及sort/2
。
元逻辑原语已经可以在简单的Prolog中产生丑陋的效果,例如
?- X\==Y, X=3, Y=3, X==Y.
Yes
但是Prolog程序员已经学会正确使用它们并避免这些问题。但是,使用CLP,更容易陷入这些陷阱,因为执行控制流程不太明显。
至于你的例子,第一个是有效的,因为sort/2
对整数列表进行排序,没有问题。在第二种情况下,sort/2
对域变量列表进行排序,从而以系统定义的任意顺序生成域变量列表。完成此操作后,sort/2
会认为其工作已完成。如果您稍后将整数值分配给那些表面上&#34;排序&#34;变量,列表的排序不再被检查,您的程序现在可以返回未排序的整数列表作为结果。
正确的替代方法是使用仅包含纯逻辑Prolog原语和/或CLP解算器定义的约束谓词的排序检查。您已经在第三个示例中使用chain/2
约束来完成此操作。
在上一个示例中,您可以观察基于本地传播的约束求解器的典型行为:您得到的答案是正确的,但不是很令人满意。通过更深入地分析约束,实际上可以推断唯一正确的解是[1,2,3]
,但解算器不会这样做,因为它可能在计算上很昂贵。但是看看如果只添加一些信息会发生什么:
?- L=[_,Y,_], L ins 1..3, chain(L, #=<), all_different(L), Y=2.
L = [1, 2, 3],
Y = 2
当解算器知道Y为2时,问题变得很容易让解算器完全解决。
一般案例的解决方案是系统地将问题分解为更简单的子问题,确保涵盖所有内容。换句话说,你添加搜索,而Prolog的搜索方式就是分离。在示例中,这可能是一个简单的member(Y,[1,2,3])
。通常,您希望对问题中的所有域变量执行相同操作,只是为了安全起见。 CLP求解器将为您提供方便的原语,例如:在clpfd
?- L=[_,_,_], L ins 1..3, chain(L, #=<), all_different(L), label(L).
L = [1, 2, 3] ;
false.
这是CLP程序的一般结构:首先是约束设置,然后是搜索。