我可以像这样制作升序整数列表:
?- findall(L,between(1,5,L),List).
我知道我也可以使用以下方法生成值:
?- length(_,X).
但我不认为我可以在findall中使用它,如下面的循环:
?- findall(X,(length(_,X),X<6),Xs).
我还可以使用clpfd生成一个列表。
:- use_module(library(clpfd)).
list_to_n(N,List) :-
length(List,N),
List ins 1..N,
all_different(List),
once(label(List)).
或
list_to_n2(N,List) :-
length(List,N),
List ins 1..N,
chain(List,#<),
label(List).
最后一种方法对我来说最好,因为它是最具声明性的,不使用once/1
或between/3
或findall/3
等。
还有其他方法吗?在纯粹的&#39;中是否有一种声明性的方式来做到这一点。 Prolog的?有没有最好的&#39;方式是什么?
答案 0 :(得分:3)
“最佳”方式取决于您的具体用例!这是使用clpfd执行此操作的另一种方法:
:- use_module(library(clpfd)).
我们在a previous answer of a related question的评论中根据@mat的建议定义谓词equidistant_stride/2
:
equidistant_stride([],_).
equidistant_stride([Z|Zs],D) :-
foldl(equidistant_stride_(D),Zs,Z,_).
equidistant_stride_(D,Z1,Z0,Z1) :-
Z1 #= Z0+D.
基于equidistant_stride/2
,我们定义:
consecutive_ascending_integers(Zs) :-
equidistant_stride(Zs,1).
consecutive_ascending_integers_from(Zs,Z0) :-
Zs = [Z0|_],
consecutive_ascending_integers(Zs).
consecutive_ascending_integers_from_1(Zs) :-
consecutive_ascending_integers_from(Zs,1).
让我们运行一些查询!首先,您的原始用例:
?- length(Zs,N), consecutive_ascending_integers_from_1(Zs).
N = 1, Zs = [1]
; N = 2, Zs = [1,2]
; N = 3, Zs = [1,2,3]
; N = 4, Zs = [1,2,3,4]
; N = 5, Zs = [1,2,3,4,5]
...
使用clpfd,我们可以提出相当一般的查询 - 并获得逻辑上合理的答案!
?- consecutive_ascending_integers([A,B,0,D,E]). A = -2, B = -1, D = 1, E = 2. ?- consecutive_ascending_integers([A,B,C,D,E]). A+1#=B, B+1#=C, C+1#=D, D+1#=E.
equidistant_stride/2
的替代实现:
我希望新代码能更好地利用约束传播。
感谢@WillNess建议推动此重写的测试用例!
equidistant_from_nth_stride([],_,_,_).
equidistant_from_nth_stride([Z|Zs],Z0,N,D) :-
Z #= Z0 + N*D,
N1 #= N+1,
equidistant_from_nth_stride(Zs,Z0,N1,D).
equidistant_stride([],_).
equidistant_stride([Z0|Zs],D) :-
equidistant_from_nth_stride(Zs,Z0,1,D).
使用@ mat的clpfd:
比较旧版本与新版本首先,旧版本:
?- equidistant_stride([1,_,_,_,14],D).
_G1133+D#=14,
_G1145+D#=_G1133,
_G1157+D#=_G1145,
1+D#=_G1157. % succeeds with Scheinlösung
?- equidistant_stride([1,_,_,_,14|_],D).
_G1136+D#=14, _G1148+D#=_G1136, _G1160+D#=_G1148, 1+D#=_G1160
; 14+D#=_G1340, _G1354+D#=14, _G1366+D#=_G1354, _G1378+D#=_G1366, 1+D#=_G1378
... % does not terminate universally
现在让我们切换到新版本并询问相同的查询!
?- equidistant_stride([1,_,_,_,14],D). false. % fails, as it should ?- equidistant_stride([1,_,_,_,14|_],D). false. % fails, as it should
更多,现在,再次!我们可以暂时通过暂时使用冗余约束来失败吗?
以前,我们建议使用约束Z1 #= Z0+D*1, Z2 #= Z0+D*2, Z3 #= Z0+D*3
代替Z1 #= Z0+D, Z2 #= Z1+D, Z3 #= Z2+D
(这个答案的第一个版本的代码确实如此)。
再次感谢@WillNess推动这个小实验
注意到目标equidistant_stride([_,4,_,_,14],D)
没有失败,而是以未决目标成功:
?- Zs = [_,4,_,_,14], equidistant_stride(Zs,D).
Zs = [_G2650, 4, _G2656, _G2659, 14],
14#=_G2650+4*D,
_G2659#=_G2650+3*D,
_G2656#=_G2650+2*D,
_G2650+D#=4.
让我们用equidistantRED_stride/2
添加一些冗余约束:
equidistantRED_stride([],_).
equidistantRED_stride([Z|Zs],D) :-
equidistant_from_nth_stride(Zs,Z,1,D),
equidistantRED_stride(Zs,D).
示例查询:
?- Zs = [_,4,_,_,14], equidistant_stride(Zs,D), equidistantRED_stride(Zs,D).
false.
完成?还没!通常,我们不希望二次数量的冗余约束。原因如下:
?- Zs = [_,_,_,_,14], equidistant_stride(Zs,D).
Zs = [_G2683, _G2686, _G2689, _G2692, 14],
14#=_G2683+4*D,
_G2692#=_G2683+3*D,
_G2689#=_G2683+2*D,
_G2686#=_G2683+D.
?- Zs = [_,_,_,_,14], equidistant_stride(Zs,D), equidistantRED_stride(Zs,D).
Zs = [_G831, _G834, _G837, _G840, 14],
14#=_G831+4*D,
_G840#=_G831+3*D,
_G837#=_G831+2*D,
_G834#=_G831+D,
14#=_G831+4*D,
_G840#=_G831+3*D,
_G837#=_G831+2*D,
_G834#=_G831+D,
D+_G840#=14,
14#=2*D+_G837,
_G840#=D+_G837,
14#=_G834+3*D,
_G840#=_G834+2*D,
_G837#=_G834+D.
但是如果我们使用双重否定技巧,残留物仍然存在于成功的情况下......
?- Zs = [_,_,_,_,14], equidistant_stride(Zs,D), \+ \+ equidistantRED_stride(Zs,D).
Zs = [_G454, _G457, _G460, _G463, 14],
14#=_G454+4*D,
_G463#=_G454+3*D,
_G460#=_G454+2*D,
_G457#=_G454+D.
......和......
?- Zs = [_,4,_,_,14], equidistant_stride(Zs,D), \+ \+ equidistantRED_stride(Zs,D). false.
......我们在比以往更多的情况下检测到失败!
让我们深入挖掘一下!我们能否在更广泛的用途中及早发现失败?
到目前为止,使用代码,这两个逻辑错误的查询不会终止:
?- Zs = [_,4,_,_,14|_], \+ \+ equidistantRED_stride(Zs,D), equidistant_stride(Zs,D). ... % Execution Aborted ?- Zs = [_,4,_,_,14|_], equidistant_stride(Zs,D), \+ \+ equidistantRED_stride(Zs,D). ... % Execution Aborted
得到修复? 得到了黑客!
?- use_module(library(lambda)). true. ?- Zs = [_,4,_,_,14|_], \+ ( term_variables(Zs,Vs), maplist(\X^when(nonvar(X),integer(X)),Vs), \+ equidistantRED_stride(Zs,D)), equidistant_stride(Zs,D). false.
黑客不保证终止冗余约束“部分”,但IMO对于快速第一次射击来说并不算太糟糕。在integer/1
中实例化任何变量时的测试Zs
意味着允许clpfd求解器将变量域约束为单例,而使用cons对进行实例化(直接导致非基于列表的谓词的终止被抑制。
我做意识到黑客可以通过多种方式很容易地破解(例如,使用循环术语)。欢迎任何建议和意见!
答案 1 :(得分:3)
在下文中,我们将讨论this previous answer中提供的代码。
目标consecutive_ascending_integers_from_1([2,3,5,8|non_list])
失败,但为什么?
让我们一步一步地进行:
以下是我们开始的代码:
:- use_module(library(clpfd)). equidistant_from_nth_stride([],_,_,_). equidistant_from_nth_stride([Z|Zs],Z0,I0,D) :- Z #= Z0 + I0*D, I1 #= I0 + 1, equidistant_from_nth_stride(Zs,Z0,I1,D). equidistant_stride([],_). equidistant_stride([Z0|Zs],D) :- equidistant_from_nth_stride(Zs,Z0,1,D). consecutive_ascending_integers(Zs) :- equidistant_stride(Zs,1). consecutive_ascending_integers_from(Zs,Z0) :- Zs = [Z0|_], consecutive_ascending_integers(Zs). consecutive_ascending_integers_from_1(Zs) :- consecutive_ascending_integers_from(Zs,1).
首先,我们使(某些)统一更明确:
equidistant_from_nth_stride([],_,_,_). equidistant_from_nth_stride([Z|Zs],Z0,I0,D) :- Z #= Z0 + I0*D, I1 #= I0 + 1, equidistant_from_nth_stride(Zs,Z0,I1,D). equidistant_stride([],_). equidistant_stride([Z0|Zs],D) :- I = 1, equidistant_from_nth_stride(Zs,Z0,I,D). consecutive_ascending_integers(Zs) :- D = 1, equidistant_stride(Zs,D). consecutive_ascending_integers_from(Zs,Z0) :- Zs = [Z0|_], consecutive_ascending_integers(Zs). consecutive_ascending_integers_from_1(Zs) :- Z0 = 1, consecutive_ascending_integers_from(Zs,Z0).
我们遵循方法和惯例introduced in this fine answer:
通过删除目标,我们可以推广一个程序。这是我最喜欢的方式。通过像这样添加谓词
(*)/1
:
:- op(920,fy, *). *_.
@WillNess正确地指出:
consecutive_ascending_integers_from_1([2|_])
失败,因此其专业化consecutive_ascending_integers_from_1([2,3,5,8|non_list])
也必定失败。
如果最大限度地概括代码以使consecutive_ascending_integers_from_1([2|_])
失败,我们“确切地知道:程序的可见剩余部分中的某些内容必须修复。”
consecutive_ascending_integers_from(Zs,Z0) :- Zs = [Z0|_], *consecutive_ascending_integers(Zs). consecutive_ascending_integers_from_1(Zs) :- Start = 1, consecutive_ascending_integers_from(Zs,Start).
让我们另一个解释!
对于版本#2(见上文),我们观察到以下广义目标也失败了:
?- consecutive_ascending_integers_from_1([_,_,_,_|non_list]). false.
为什么此失败?让我们最大限度地概括代码,使目标失败:
equidistant_from_nth_stride([],_,_,_). equidistant_from_nth_stride([Z|Zs],Z0,I0,D) :- *Z #= Z0 + I0*D, *I1 #= I0 + 1, equidistant_from_nth_stride(Zs,Z0,I1,D). equidistant_stride([],_). equidistant_stride([Z0|Zs],D) :- *I = 1, equidistant_from_nth_stride(Zs,Z0,I,D). consecutive_ascending_integers(Zs) :- *D = 1, equidistant_stride(Zs,D). consecutive_ascending_integers_from(Zs,Z0) :- *Zs = [Z0|_], consecutive_ascending_integers(Zs). consecutive_ascending_integers_from_1(Zs) :- *Start = 1, consecutive_ascending_integers_from(Zs,Start).
为什么目标consecutive_ascending_integers_from_1([2,3,5,8|non_list])
会失败?
到目前为止,我们已经看到了两种解释,但可能还有更多......
事实就是这样:加入狩猎!
答案 2 :(得分:1)
我们将升序列表定义为包含至少两个增加整数的元素(非递减列表可以为空或单例,但“升序”是更明确的属性)。这有点武断。
在SWI Prolog:
ascending( [A,B|R] ):-
freeze(A,
freeze(B, (A < B, freeze(R, (R=[] -> true ; ascending([B|R])))) )).
为了轻松填写,我们可以使用
mselect([A|B],S,S2):- select(A,S,S1), mselect(B,S1,S2).
mselect([], S2, S2).
测试:
15? - 上升(LS),mselect(LS,[10,2,8,5],[])。
LS = [2,5,8,10] ;
错误。
16? - mselect(LS,[10,2,8,5],[]),升序(LS)。
LS = [2,5,8,10] ;
假的。的
关于赏金问题,根据https://stackoverflow.com/tags/logical-purity/info,
只有单调(也称为“单调”)谓词是纯粹的:如果谓词对任何参数都成功,那么它对于这些参数的任何泛化都不会失败,并且如果它对任何参数组合都失败,那么它对这些论点的任何专业化都没有成功。
consecutive_ascending_integers_from_1([2|B])
失败,因此其专业化consecutive_ascending_integers_from_1([2,3,5,8|non_list])
也必定失败。
对于延期奖金" consecutive_ascending_integers_from_1([2,3,5,8|non_list])
fails, but why?",其他失败的目标是:( 1 )
consecutive_ascending_integers_from_1([_,3|_])
代码
equidistant_from_nth_stride([],_,_,_).
equidistant_from_nth_stride([Z|Zs],Z0,I0,D) :-
Z #= Z0 + I0*D, % C1
*( I1 #= I0 + 1 ),
equidistant_from_nth_stride(Zs,Z0,I1,D).
,其余未更改,因为C1变为3 #= 1 + 1*1
。此外,( 2 和 3 )
consecutive_ascending_integers_from_1([A,B,5|_])
consecutive_ascending_integers_from_1([A,B,C,8|_])
都会因未更改的代码而失败,因为第1个定义了
A = 1, B #= 1 + 1*1, 5 #= 1 + 2*1
和第二个定义
A = 1, B #= 1 + 1*1, C #= 1 + 2*1, 8 #= 1 + 3*1
另一种可能性( 4 )是
consecutive_ascending_integers_from_1([_,3,5|_])
与广义
consecutive_ascending_integers_from_1(Zs) :-
*( Z0 = 1 ),
consecutive_ascending_integers_from(Zs,Z0).
consecutive_ascending_integers_from(Zs,Z0) :-
*( Zs = [Z0|_] ),
consecutive_ascending_integers(Zs).
因为
26 ?- 3 #= Z + 1*1, 5 #= Z + 2*1.
false.
同样,使用类似修改后的代码,目标( 5 )
consecutive_ascending_integers_from_1([_,3,_,8|_])
,因为
27 ?- 3 #= Z + 1*1, 8 #= Z + 3*1.
false.
以及( 6 ... 9 )
consecutive_ascending_integers_from_1([2,3,_,8|_])
consecutive_ascending_integers_from_1([2,_,_,8|_])
consecutive_ascending_integers_from_1([2,_,5,8|_])
consecutive_ascending_integers_from_1([2,_,5|_])
出于同样的原因。另一种可能的代码概括是保持D
未初始化(原始代码的其余部分保持不变):
consecutive_ascending_integers(Zs) :-
*( D = 1 ),
equidistant_stride(Zs,D).
因为目标( 5 )...[_,3,_,8|_]...
再次失败,因为
49 ?- 3 #= 1 + 1*D, 8 #= 1 + 3*D.
false.
但是,
50 ?- 3 #= 1 + 1*D, 5 #= 1 + 2*D.
D = 2.
所以...[_,3,5|_]...
会成功(确实如此)。 ( 10 )
consecutive_ascending_integers_from_1([_,_,5,8|_])
由于同样的原因,也失败了。
可能会有更多的组合,但它的一般要点变得更加清晰:这一切都取决于该谓词创建的约束如何运作。