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

Prolog 使用带有'length/2'的约束变量`,prolog,clpfd,meta-predicate,prolog-coroutining,Prolog,Clpfd,Meta Predicate,Prolog Coroutining,问题是: $ 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 redistrib

问题是:

$ 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
,但我想知道问题出在哪里?我以前没有把
长度/2
塞住。

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

?- length(L, N), N in 1..3.
具有与以下相同的终止属性:

?- length(L, N), false, N in 1..3. 好的,这可以被优化以避免多余的选择点。但还有一个更基本的问题:如果您要确定长度
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属性相结合。此外,算术表达式可能不是一个好主意,允许;但是我必须在瑞士等待
(#)/1


1:严格地说,这“完全失败”只适用于SICStus、SWI和YAP。因为在这些系统中,不会由于耗尽当前表示而发生意外故障。也就是说,他们的失败总是可以被视为诚实的拒绝。

以下基于和的解决方案如何

:- ([, ]). 列表_FDlen(Xs,N):- ((对),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在1..3中,列出(Xs,N)。 N=1,Xs=[[u A] ; N=2,Xs=[[u A,u B] ; N=3,Xs=[[u A,[u B,[u C] ; 错.%普遍终止 -在inf..2中的-N,列表_FDlen(Xs,N)。 N=0,Xs=[] ; N=1,Xs=[[u A] ; N=2,Xs=[[u A,u B] ; 错.%也普遍终止 那么这个特殊的查询呢

?- 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) ?-N在2..sup,list_FDlen(Xs,N)中。 N=2,Xs=[[u A,u B] ; N=3,Xs=[[u A,[u B,[u C] ; N=4,Xs=[[u A,[u B,[u C,[u D] ... % 不终止(如预期)
与不太确定的
长度/2
相比,可能更有用的是适当的列表长度约束。您可以找到它的一个实现,称为
len/2
。通过此操作,您可以获得以下行为:

?- N :: 1..3, len(Xs, N).
N = N{1 .. 3}
Xs = [_431|_482]               % note it must contain at least one element!
There is 1 delayed goal.
Yes (0.00s cpu)
然后,您可以通过枚举
N
来枚举有效列表:

?- N :: 1..3, len(Xs, N), indomain(N).
N = 1
Xs = [_478]
Yes (0.00s cpu, solution 1, maybe more)
N = 2
Xs = [_478, _557]
Yes (0.02s cpu, solution 2, maybe more)
N = 3
Xs = [_478, _557, _561]
Yes (0.02s cpu, solution 3)
或者通过使用良好的旧标准
长度/2
生成列表:

fd_length([], N) :-
   N #= 0.
fd_length([_|L], N0) :-
   N0 #>= 1,
   N1 #= N0-1,
   fd_length(L, N1).
?- N :: 1..3, len(Xs, N), length(Xs, _).
N = 1
Xs = [_488]
Yes (0.00s cpu, solution 1, maybe more)
N = 2
Xs = [_488, _555]
Yes (0.02s cpu, solution 2, maybe more)
N = 3
Xs = [_488, _555, _636]
Yes (0.02s cpu, solution 3)
我们为@mat的clpfd实现定制了一个-ish变体

:- use_module(library(clpfd)). :- use_module(library(dialect/sicstus)). :- multifile clpfd:run_propagator/2. 全局约束处理程序
list\u FD\u size/3
在约束传播发生时增量修改其内部状态。所有修改都是跟踪的,并且在回溯时取消

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) ). 示例查询:

?- 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). ?-懒惰_len(Xs,N)。 当((nonvar(N);nonvar(Xs)),lazylist_acc_len(Xs,0,N)), N在0..sup中, 列表大小(Xs+0,N)。 ?-懒惰(Xs,3)。 Xs=[[u A,[u B,[u C]。 ?懒惰的人([[uu,[uu],L])。 L=2。 ?-懒散的_len(Xs,L),L#>0。 Xs=[[u A | u B], 当((非动词(L);非动词(_B)),懒散的人(_B,1,L)), 我在1..sup, 列出FD大小(_B+1,L)。 ?-懒散的(x,L),L#>2。 Xs=[_A,_B,_C | u D], 当((非动词(L);非动词(_D)),懒散的人(_D,3,L)), 我在3..sup, 列出FD大小(D+3,L)。 ?-懒散(x,L),L#>0,L#>2。 Xs=[_A,_B,_C | u D], 当((非动词(L);非动词(_D)),懒散的人(_D,3,L)), 我在3..sup, 列出FD大小(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..4中的L,lazy_len(Xs,L),([up],[L])。 L=1,Xs=[[u A] ; L=2,Xs=[[u A,u B] ; L=3,Xs=[[u A,[u B,[u C] ; L=4,Xs=[[u A,[u B,[u C,[u D]。 ?1..4中的-L,lazy_len(Xs,L),标记([down],[L])。 L=4,Xs=[[u A,[u B,[u C,[u D] ; L=3,Xs=[[u A,[u B,[u C] ; L=2,Xs=[[u A,u B] ; L=1,Xs=[[u A]。
脚注1: 在这里,我们通过使用延迟目标来保持决定论(避免创建选择点)。

对于:SICStus,GNU,也有同样的问题……这是一个多么复杂的Prolog和扩展的好例子。。。谢谢你解决了这个问题…我很想看到这个功能变得生动起来@重复:我认为,一旦修复了语义,可能会非常简单。。。只需扩展长度/2处理属性变量…@capelical。听起来不错!但是,恕我直言,clpfd实现(在SWI中使用,也可以在yap中使用)是100%Prolog和0%C。当黑客攻击C实现时,例如,
length/2
我们将失去“100%Prolog”属性。。。如何将编码工作保持在合理的范围内?类似地,人们可能希望
?-N#<0,length(L,N)。
应该失败,但它当然不会失败。@Boris:至少让你感到安慰的是,你只需要一个约束变量来定义
fd#length/2
-我相信我们已经有过好几次了。你明白这是怎么可能的吗?@Boris:这个查询不能终止!对于
?-fd_长度(L,N)。
不能终止以描述整个解决方案集。@Boris。因此,在序言中谈论终结时,我们指的是普遍终结,而不是存在终结。简言之,如果连词
G,false
有限地失败,那么一些目标
G
就会普遍终止。可以说我有偏见,但如果我能选择一个
length/2
(和
same_length/3
)这样的朋友可以一起工作的域,那就是整数(clpfd)。我不知道如何通过合理的努力做到这一点,但从用户的角度来看这一特殊功能,我只能说“哇!那太好了!”这似乎正是我想要的!您知道这是否可以在SWI Prolog中实现,还是使用特定于ECLiPSe的扩展?(源代码至少包含一些不熟悉的语法)。关键行是
suspend(len(Xs,L,N),0,[[Xs0,N]->inst,N->min])
,其中目标延迟到Xs0或N实例化或N的下限更改。您必须研究如何使用SWI的clpfd实现这一点。为什么不将该功能添加到
length/2
?在我看来,这将是最好的,尤其是对于初学者。还是有(技术上的 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].