Prolog 使用CLP(FD)对任意长度子列表的约束
我正试着绕着CLP(FD)转。下面是一个简单的例子,我不确定哪种方法最惯用。假设我们有一个数字列表(Prolog 使用CLP(FD)对任意长度子列表的约束,prolog,clpfd,prolog-defaulty,dcg,logical-purity,Prolog,Clpfd,Prolog Defaulty,Dcg,Logical Purity,我正试着绕着CLP(FD)转。下面是一个简单的例子,我不确定哪种方法最惯用。假设我们有一个数字列表(L),其中一些数字已经填充,如下所示 L = [_, _, _, 3, _, _, _, 4, _, _, 2, _] 我想说的是,一个固定数字的左右两边的数字必须加起来。上述问题的一个可能解决方案是: L = [0, 0, 1, 3, 0, 1, 1, 4, 2, 0, 2, 0] L = [0, 0, 0, 3, 1, 1, 1, 5, 1, 1, 2, 0] 当然,还有其他解决办法。我
L
),其中一些数字已经填充,如下所示
L = [_, _, _, 3, _, _, _, 4, _, _, 2, _]
我想说的是,一个固定数字的左右两边的数字必须加起来。上述问题的一个可能解决方案是:
L = [0, 0, 1, 3, 0, 1, 1, 4, 2, 0, 2, 0]
L = [0, 0, 0, 3, 1, 1, 1, 5, 1, 1, 2, 0]
当然,还有其他解决办法。我的第一种方法是:
追加
两个列表李>
sum/3
,将结果列表约束为#=
到轴第2级。我认为谓词
sum/3
完成了大部分工作。让我们稍微改变一下问题:
枢轴左右两侧的连续字符串1必须与枢轴相加
鉴于此:
L = [_, _, _, 3, _, _, _, 5, _, _, 2, _]
一个可能的解决办法是:
L = [0, 0, 1, 3, 0, 1, 1, 4, 2, 0, 2, 0]
L = [0, 0, 0, 3, 1, 1, 1, 5, 1, 1, 2, 0]
现在更棘手了。。。我想说的是,轴N的左、右附加子列表必须包含长度为N的1的子列表。这有意义吗?我该如何使用CLP(FD)说出这些话?“可能的解决方案”
你的推理完全正确,会起作用的
然而,它在一个关键方面还存在不足:您的描述目前是非常必要的,如果您以您描述的方式实现了这一点,那么您将能够解决给定的实例,但是您将无法使用相同的程序来生成此类难题实例
让我更详细地解释我的意思
清晰的表述
我们从这些谜题的表示开始。您当前正在使用:
L = [_, _, _, 3, _, _, _, 4, _, _, 2, _]
这称为默认的表示,因为您无法清楚地区分不同的情况。如果你从逻辑上思考,认为这是一个谜题的“实例”,那么这个术语的任何更特殊的情况都必须被视为实例的更具体的情况,例如部分或完整的解决方案。但在这种表示中,情况并非如此,因为实例化其中一个参数会使其突然变成完全不同的状态,而不仅仅是更具体的表现形式
因此,这里有一个清晰的表示此类谜题:
example([?,?,?,p(3),?,?,?,p(4),?,?,p(2),?]).
这个DCG是整个答案的关键点
以下是如何使用它,以了解它所描述的内容:
?- example(Es),
phrase(is_p_is(Ls), Es).
Es = [?, ?, ?, p(3), ?, ?, ?, p(4), ?, ?, p(2), ?],
Ls = [[_G902, _G905, _G908], 3, [_G920, _G923, _G926], [_G920, _G923, _G926], 4, [_G938, _G941], [_G938, _G941], 2, [_G950]] ;
false.
其中“三元组”是隐式的,可以按如下方式分组:
[(Left0,Pivot0,Right0),(Left1,Pivot1,Right1),...]
而且,Right
n与Left
n+1相同
过帐限制
鉴于此类列表,发布CLP(FD)约束是很简单的:
constraints([]).
constraints([Left,Pivot,Right|Rest]) :-
Left ins 0..sup,
Right ins 0..sup,
sum(Left, #=, SL),
sum(Right, #=, SR),
Pivot #= SL + SR,
constraints(Rest).
variables([]) --> [].
variables([Left,_,Right|Rest]) -->
seq(Left),
seq(Right),
variables(Rest).
seq([]) --> [].
seq([L|Ls]) --> [L], seq(Ls).
示例查询和答案
以下是一个示例查询和一些答案:
?- example(Es),
phrase(is_p_is(Ls), Es),
constraints(Ls),
phrase(variables(Ls), Vs),
label(Vs).
Es = [?, ?, ?, p(3), ?, ?, ?, p(4), ?, ?, p(2), ?],
Ls = [[0, 0, 0], 3, [0, 0, 3], [0, 0, 3], 4, [0, 1], [0, 1], 2, [1]],
Vs = [0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 1, 0, 1, 1] ;
Es = [?, ?, ?, p(3), ?, ?, ?, p(4), ?, ?, p(2), ?],
Ls = [[0, 0, 0], 3, [0, 0, 3], [0, 0, 3], 4, [1, 0], [1, 0], 2, [1]],
Vs = [0, 0, 0, 0, 0, 3, 0, 0, 3, 1, 0, 1, 0, 1] ;
Es = [?, ?, ?, p(3), ?, ?, ?, p(4), ?, ?, p(2), ?],
Ls = [[0, 0, 0], 3, [0, 1, 2], [0, 1, 2], 4, [0, 1], [0, 1], 2, [1]],
Vs = [0, 0, 0, 0, 1, 2, 0, 1, 2, 0, 1, 0, 1, 1] ;
etc.
例如:
?- length(Es, _), phrase(is_p_is(Ls), Es).
Es = [p(_G874)],
Ls = [[], _G874, []] ;
Es = [p(_G877), ?],
Ls = [[], _G877, [_G885]] ;
Es = [p(_G877), p(_G888)],
Ls = [[], _G877, [], [], _G888, []] ;
Es = [?, p(_G880)],
Ls = [[_G877], _G880, []] ;
Es = [p(_G880), ?, ?],
Ls = [[], _G880, [_G888, _G891]] .
?- phrase(variables([i(X),p(3),i(Y)]), Vs).
Vs = [X, Y].
这当然不是一个非常公平的枚举,但它远远超出了提取内容的范围,因为查询中甚至没有给出任何内容
你多地道啊!
因此,为了回答您的问题:在我看来,我们认为那些最令人印象深刻地展示了我们期望从关系中获得的声明性属性以及逻辑程序的独特优势的解决方案是最具说服力的序言,例如:
- 可在所有方向使用
- 承认阅读是更具体或更一般的
- 使用反映关系一般性的名称
一个优雅的、惯用的Prolog解决方案通常从干净的实例表示和描述性谓词开始。现在,这就是我所说的美好的答案!确实很有教育意义。。。我现在正绞尽脑汁研究您的DCG描述,并试图将其推广到以前指定列表结构时使用的方法。最后,我会发布我对第二个问题的回答。在此期间,我会等几天,说服其他人参与这个帖子,然后接受你的答案作为最终答案。荣誉顺便问一下,您是否推荐一本现代的Prolog书籍,其中包括CLP(FD)和元编程?经典教科书,例如,尽管非常优雅,但与当前实践和口译员的扩展点(例如SWI的term_expansion/2)不符,并且完全跳过CLP(*)。我还非常有兴趣理解CLP(FD)作为CLP(H)的推广的理论方面。变量/3不能简单地定义为
变量-->排除(整数),附加。
?还是我遗漏了什么?据我所知,它的目的是要有一个自由变量列表传递给label/1,对吗?六羟甲基三聚氰胺六甲醚。。。或者也许这只是我必须再次打破这个美丽的对称定义:谢谢你的鼓励,谢谢你提出了一个有见地的问题!请参阅我的个人资料,获取一些介绍最新发展的序言链接。我希望你觉得它们有用!尤其是古朴,无论在学习还是教授序言方面都值得一看!
?- phrase(variables([i(X),p(3),i(Y)]), Vs).
Vs = [X, Y].
?- length(Ls, _), phrase(variables(Ls), Vs).
Ls = Vs, Vs = [] ;
Ls = [i(_2066)],
Vs = [_2066] ;
Ls = [p(_2066)],
Vs = [] ;
Ls = [i(_2072), i(_2082)],
Vs = [_2072, _2082] ;
Ls = [i(_2072), p(_2082)],
Vs = [_2072] ;
Ls = [p(_2072), i(_2076)],
Vs = [_2076] .
?- phrase(variables(Ts), Vs).
Ts = Vs, Vs = [] ;
Ts = [[], _1960, []],
Vs = [] ;
Ts = [[], _1960, [], [], _1978, []],
Vs = [] ;
Ts = [[], _1960, [], [], _1978, [], [], _1996, []],
Vs = [] .