如何在 Prolog 中将列表转换为循环列表?

问题描述 投票:0回答:4

我有一个列表

[5, 4, 8, 9, 7, 6]
,我需要将每个数字与下一个数字进行比较,并将第一个数字和最后一个数字相互比较。想象一下这个列表是围绕着一张圆桌的。任何帮助将不胜感激。现在谢谢...

list prolog circular-list
4个回答
3
投票

您可能不需要循环列表——非循环列表就可以了...

:-use_module列表),[最后/2])。

怎么会这样呢?非常简单:添加最后一项并使用“滞后”来强制您在所有相邻列表项之间的选择限制。

wrapped_adj_dif([E|Es]) :-
 最后(Es, E0),
   adj_dif_prev([E|Es], E0).

adj_dif_prev([], _).
adj_dif_prev([E|Es], E0) :-
 差异 (E, E0),
   adj_dif_prev(Es, E)。

使用 SICStus Prolog 4.3.2 的示例查询1

?- wrapped_adj_dif(Xs).
   Xs = [_A,_B]      , dif(_B,_A), dif(_A,_B)
;  Xs = [_A,_B,_C]   , dif(_C,_A), dif(_A,_B), dif(_B,_C)
;  Xs = [_A,_B,_C,_D], dif(_D,_A), dif(_A,_B), dif(_B,_C), dif(_C,_D)
...

请注意,当第一个参数是非循环基础列表时,上面的代码是确定性的:

?-wrapped_adj_dif([a,b,c,d])。
真的。
?-wrapped_adj_dif([a,b,c,a])。

编辑

如果您使用上述具有不同约束的“环绕相邻”访问模式,请考虑提升共同功能!您可以将它们转换为可重用的,如下所示:

for_all_wrapped_adjacent(P_2, Es) :-
 最后(Es, E0),
   i_mapadj_prev(Es, P_2, E0)。

i_mapadj_prev([], _, _).
i_mapadj_prev([E|Es], P_2, E0) :-
 调用(P_2, E0, E),
   i_mapadj_prev(Es, P_2, E)。

使用上述元谓词

wrapped_adj_difNU/1
的新定义 归结为:

wrapped_adj_difNU(西班牙语):-
   for_all_wrapped_adjacent(dif,Es)。

这里又是上面的查询2——这次使用

wrapped_adj_difNU/1
:

?- wrapped_adj_difNU([a,b,c,d]).
true.                                                   % (unchanged)

?- wrapped_adj_difNU([a,b,c,a]).
false.                                                  % (unchanged)

?- wrapped_adj_difNU(Xs).
   Xs = [_A,_B]      , dif(_B,_A), dif(_A,_B)
;  Xs = [_A,_B,_C]   , dif(_C,_A), dif(_A,_B), dif(_B,_C)
;  Xs = [_A,_B,_C,_D], dif(_D,_A), dif(_A,_B), dif(_B,_C), dif(_C,_D)
...                                                     % (unchanged)

脚注1: 为了简洁和可读性, 答案经过手动后处理。
脚注2:不用担心! Prolog 给我们的答案没有改变——一点也没有改变。


1
投票

创建一个新列表以便比较:

comp_list([X|Rest],L) :-
    append([X|Rest],[X],L).

示例:

?- comp_list([1,2,3],L).
L = [1, 2, 3, 1].

0
投票

在递归访问期间只进行第一个元素,最后停止:

compare_adj([First|Rest]) :- compare_adj([First|Rest], First).
compare_adj([A,B|T], F) :-
 writeln(compare(A,B)),
 compare_adj([B|T], F).
compare_adj([Last], First) :-
 writeln(compare(Last, First)).

?- compare_adj([5, 4, 8, 9, 7, 6]).
compare(5,4)
compare(4,8)
compare(8,9)
compare(9,7)
compare(7,6)
compare(6,5)
true ;
false.

编辑

虽然效率相当低,但我们可以组合一些内置函数,以节省一些代码:

compare_adj([First|Rest]) :-
 append([First|Rest],[First],Temp),
 forall(append(_, [A,B|_], Temp), writeln(compare(A,B))).

0
投票

这是 3 项循环列表的语法,通过尝试从开头取出 15 项进行测试:

?- Loop = [1,2,3|Loop],
   length(Goal, 15), append(Goal, _, Loop).

Goal = [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3].

要将列表转换为循环列表,

append/3
可以做到:

?- Nums = [1,2,3],
   append(Nums, Loop, Loop),
   length(Goal, 15), append(Goal, _, Loop).

Goal = [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]

参见:SWI Prolog Discourse 上的“append(X, Y, Y) 创建循环项” 以及注释“我以声明方式阅读append(X, Y, Y) 的方式是“Y 是开头为的列表” X 和尾部是 Y””。即“Loop 是开头为 Nums、尾部为 Loop 的列表”。

SWI Prolog 的 occurrs_check 可用于阻止自指术语:

set_prolog_flag(occurs_check, error)
。 [此答案假设 SWI Prolog]。

© www.soinside.com 2019 - 2024. All rights reserved.