使用带有`length / 2`的约束变量

时间:2015-09-09 11:21:01

标签: prolog clpfd

问题在于:

$ swipl
Welcome to SWI-Prolog (Multi-threaded, 64 bits, Version 7.3.6-5-g5aeabd5)
Copyright (c) 1990-2015 University of Amsterdam, VU Amsterdam
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to redistribute it under certain conditions.
Please visit http://www.swi-prolog.org for details.

For help, use ?- help(Topic). or ?- apropos(Word).

?- use_module(library(clpfd)).
true.

?- N in 1..3, length(L, N).
N = 1,
L = [_G1580] ;
N = 2,
L = [_G1580, _G1583] ;
N = 3,
L = [_G1580, _G1583, _G1586] ;
ERROR: Out of global stack % after a while

(我可以切换子查询的顺序,结果是一样的。)

我想在使用它之前需要标记N,但我想知道问题是什么?我之前没有设法阻止length/2

3 个答案:

答案 0 :(得分:4)

让我们从最明显的一个开始。如果你切换目标,你有:

?- length(L, N), N in 1..3.

具有与以下相同的终止属性:

?- length(L, N), false, N in 1..3.

很明显,这个一定不能以Prolog的执行机制终止。

但是,如果您将N in 1..3放在前面,则此可能会影响终止。要做到这一点,必须有可能用有限的手段来证明4上没有N。如何在没有约束的系统中证明这一点 - 也就是说,只有语法统一存在?好吧,你不能。并且length/2只是commonly defined而没有约束。 对于library(clpfd),事情是微不足道的,因为N #>= 4, N in 1..3只是失败 1 。另请注意,library(clpfd)library(clpq)的合作不多,这可能也是一个有趣的候选人。

因此,您需要定义自己的长度 - 对于您感兴趣的每个约束包。这有点可惜,但目前没有通用的方法可以实现。 ((也就是说,如果你有兴趣并想一想它,你可能会想出一个很好的API,每个约束系统应该坚持。唉,这需要几十年,我怀疑。目前,也有很多很多分歧。))

所以这是fd_length/2的第一个天真的方式:

fd_length([], N) :-
   N #= 0.
fd_length([_|L], N0) :-
   N0 #>= 1,
   N1 #= N0-1,
   fd_length(L, N1).

好的,这可以进行优化以避免多余的选择点。但是有一个更基本的问题:如果要确定长度N列表的长度,这将创建N个约束变量!但我们确实只需要一个。

fd_length(L, N) :-
   N #>= 0,
   fd_length(L, N, 0).

fd_length([], N, N0) :-
   N #= N0.
fd_length([_|L], N, N0) :-
   N1 is N0+1,
   N #>= N1,
   fd_length(L, N, N1).

同样,由于许多原因,这并不完美:它可以使用像当前系统那样的布伦特算法;并将它与所有fd属性结合起来。此外,算术表达式可能不是一个好主意;但是我必须等待SWI中的(#)/1 ...

1:严格来说,这个"简单地失败"仅适用于SICStus,SWI和YAP。因为在那些系统中,由于当前表示的耗尽而没有意外故障。也就是说,他们的失败总是可以当作诚实的。 功能

答案 1 :(得分:4)

基于baroque 的以下tcount/3变通方法怎么样?

:- use_module([library(clpfd), library(lambda)]).

list_FDlen(Xs, N) :-
   tcount(\_^ =(true), Xs, N).

让我们查询!

?- N in 1..3, list_FDlen(Xs, N).
   N = 1, Xs = [_A]
;  N = 2, Xs = [_A,_B]
;  N = 3, Xs = [_A,_B,_C]
;  false.                             % terminates universally

?- N in inf..2, list_FDlen(Xs, N).
   N = 0, Xs = []
;  N = 1, Xs = [_A]
;  N = 2, Xs = [_A,_B]
;  false.                             % terminates universally, too

这个特定的查询怎么样?

?- N in 2..sup, list_FDlen(Xs, N).
   N = 2, Xs = [_A,_B]
;  N = 3, Xs = [_A,_B,_C]
;  N = 4, Xs = [_A,_B,_C,_D]
...                                   % does not terminate (as expected)

答案 2 :(得分:3)

我们提供 - {h}变种 length/2,为@ mat的clpfd实施量身定制。

:- use_module(library(clpfd)).
:- use_module(library(dialect/sicstus)).

:- multifile clpfd:run_propagator/2.

"导出"谓词.png images的定义如下:

lazy_len(Es, N) :-
   N in 0..sup,               % lengths are always non-negative integers
   lazylist_acc_len(Es, 0, N),
   create_mutable(Es+0, State),
   clpfd:make_propagator(list_FD_size(State,N), Propagator),
   clpfd:init_propagator(N, Propagator),
   clpfd:trigger_once(Propagator).

全局约束处理程序lazy_len/2在发生约束传播时逐步修改其内部状态。所有的修改都是落后的,并且在回溯时都没有完成。

clpfd:run_propagator(list_FD_size(State,N), _MState) :- 
   get_mutable(Es0+Min0, State),
   fd_inf(N, Min),
   Diff is Min - Min0,
   length(Delta, Diff),
   append(Delta, Es, Es0),
   (  integer(N)
   -> Es = []
   ;  Delta = []
   -> true                    % unchanged
   ;  update_mutable(Es+Min, State)
   ).

list_FD_size/3从两个方面解决问题;它的约束部分如上所示。只要部分实例允许 1 ,树端就会使用向下遍历列表:

lazylist_acc_len(_, _, N) :-
   integer(N),
   !.
lazylist_acc_len(Es, N0, N) :-
   var(Es),
   !,
   when((nonvar(N);nonvar(Es)), lazylist_acc_len(Es,N0,N)).
lazylist_acc_len([], N, N).
lazylist_acc_len([_|Es], N0, N) :-
   N1 is N0+1,
   N  in N1..sup,
   lazylist_acc_len(Es, N1, N).   

示例查询:

?- lazy_len(Xs, N).
when((nonvar(N);nonvar(Xs)), lazylist_acc_len(Xs,0,N)),
N in 0..sup,
list_FD_size(Xs+0, N).

?- lazy_len(Xs, 3).
Xs = [_A,_B,_C].

?- lazy_len([_,_], L).
L = 2.

?- lazy_len(Xs, L), L #> 0.
Xs = [_A|_B],
when((nonvar(L);nonvar(_B)), lazylist_acc_len(_B,1,L)),
L in 1..sup,
list_FD_size(_B+1, L).

?- lazy_len(Xs, L), L #> 2.
Xs = [_A,_B,_C|_D],
when((nonvar(L);nonvar(_D)), lazylist_acc_len(_D,3,L)),
L in 3..sup,
list_FD_size(_D+3, L).

?- lazy_len(Xs, L), L #> 0, L #> 2.
Xs = [_A,_B,_C|_D],
when((nonvar(L);nonvar(_D)), lazylist_acc_len(_D,3,L)),
L in 3..sup,
list_FD_size(_D+3, L).

而且,最后还有一个问题......好吧,实际上两个更多:一个上升 - 另一个上升。

?- L in 1..4, lazy_len(Xs, L), labeling([up], [L]).
   L = 1, Xs = [_A]
;  L = 2, Xs = [_A,_B]
;  L = 3, Xs = [_A,_B,_C]
;  L = 4, Xs = [_A,_B,_C,_D].

?- L in 1..4, lazy_len(Xs, L), labeling([down], [L]).
   L = 4, Xs = [_A,_B,_C,_D]
;  L = 3, Xs = [_A,_B,_C]
;  L = 2, Xs = [_A,_B]
;  L = 1, Xs = [_A].

脚注1: 在这里,我们专注于通过使用延迟目标来保留决定论(避免创建选择点)。 功能