首页 > 解决方案 > Prolog -> 我有一个列表 [X,...,Y,...Z],我如何获得一个仅从 X 到 Y 的列表?

问题描述

SWI-PROLOG。
我有一个列表 [X, ..., Y, ..., Z],如何获得仅从 X 到 Y 的列表?
即 [ X, ..., Y , ..., Z] --> [X, ..., Y]
我对这种语言知之甚少,所以欢迎大家帮忙!
我试过这个:

bla([Ele|_], Ele, _). 
bla([H|T], Ele, Res):-
   append(Res, H, New_Res),
   bla([T], Ele, New_Res).  

在这里,Ele将是 Y。
我初始化了Res一个空列表。

我再说一遍,我是这种语言的新手,所以我写的可能非常错误,对不起!

标签: prolog

解决方案


或者,由于您的谓词描述了两个列表和一个枢轴元素之间的关系,您可以选择使用 DCG 来完成任务:

list_pivot_sublist(L,P,S) :-
   phrase(sublist_till(L,P),S).  % the sublist up to the pivot element
                                 % is described by the DCG sublist_till//2

sublist_till([P|_],P) -->        % if the head of the list is P
   [P].                          % it's the last element in the sublist
sublist_till([X|Xs],P) -->       % if the head of the list is any element X
   [X],                          % X is in the sublist and
   sublist_till(Xs,P).           % the same goes for the tail and P

现在让我们看一些查询:

[1,2,3,4,5] 到元素 3 的子列表是什么?

?- list_pivot_sublist([1,2,3,4,5],3,S).
S = [1, 2, 3] ;
false.

[1,2,3,4,5] 有哪些子列表枢轴对?

?- list_pivot_sublist([1,2,3,4,5],P,S).
P = 1,
S = [1] ;
P = 2,
S = [1, 2] ;
P = 3,
S = [1, 2, 3] ;
P = 4,
S = [1, 2, 3, 4] ;
P = 5,
S = [1, 2, 3, 4, 5] ;
false.

对于子列表 [1,2,3],原始列表和合适的枢轴元素会是什么样子?

?- list_pivot_sublist(L,P,[1,2,3]).
L = [1, 2, 3|_G4751],
P = 3 ;
false.

或者最一般的查询:有哪些对应的列表、枢轴元素和子列表?

?- list_pivot_sublist(L,P,S).
L = [P|_G4739],
S = [P] ;
L = [_G4738, P|_G4745],
S = [_G4738, P] ;
L = [_G4738, _G4744, P|_G4751],
S = [_G4738, _G4744, P] ;
L = [_G4738, _G4744, _G4750, P|_G4757],
S = [_G4738, _G4744, _G4750, P] ;
.
.
.

这些答案与您使用@gusbro 的版本得到的答案相同,bla/3但此查询除外...

?- bla(L,P,[1,2,3]).
L = [1, 2, 3|_G4739],
P = 3 ;

ERROR: Out of global stack

append/3...在提供唯一答案后与第一个版本(以两个作为目标的版本)循环。原因是第一个目标提供了无限多的候选解决方案......

?- append(NL1, [Ele|_], L).
NL1 = [],
L = [Ele|_G900] ;
NL1 = [_G1000],
L = [_G1000, Ele|_G900] ;
NL1 = [_G1000, _G1006],                                   % this candidate leads to
L = [_G1000, _G1006, Ele|_G900] ;                         % the only solution
NL1 = [_G1000, _G1006, _G1012],
L = [_G1000, _G1006, _G1012, Ele|_G900] ;
.
.
.

...所有这些都失败了,除了上面查询中标记的那个:

?- append(NL1, [Ele|_], L), append(NL1, [Ele], [1,2,3]).
NL1 = [1, 2],                                             % corresponding answer
Ele = 3,                                                  % to the third candidate
L = [1, 2, 3|_G4641] ;                                    % above
ERROR: Out of global stack

因此,非append/3版本显然更可取。参考@lurker 的评论,我还想指出,对于这些版本,如果枢轴元素在列表中多次出现,您将得到多个答案:

?- list_pivot_sublist([1,2,3,4,5,3],3,S).
S = [1, 2, 3] ;
S = [1, 2, 3, 4, 5, 3] ;

如果您只想获得第一个子列表,您可以通过添加一个约束来更改 DCG 版本,X不同于P,这可以防止 Prolog 在第一次成功后进一步递归:

sublist_till([P|_],P) -->
   [P].
sublist_till([X|Xs],P) -->
   {dif(X,P)},                % <- new constraint
   [X],
   sublist_till(Xs,P).

现在上面的查询只产生一个答案:

?- list_pivot_sublist([1,2,3,4,5,3],3,S).
S = [1, 2, 3] ;
false.

最通用的查询也略有不同,因为现在它将dif/2约束传播到答案:

?- list_pivot_sublist(L,P,S).
L = [P|_G4739],
S = [P] ;
L = [_G4886, P|_G4890],
S = [_G4886, P],
dif(_G4886, P) ;
L = [_G4942, _G4945, P|_G4949],
S = [_G4942, _G4945, P],
dif(_G4942, P),
dif(_G4945, P) ;
.
.
.

您可以通过向递归规则添加相同的约束来更改 @gusbro 的第二个版本以实现相同的行为:

bla([Ele|_], Ele, [Ele]).
bla([E|L], Ele, [E|NL]):-
  dif(E,Ele),                  % <- new constraint
  bla(L, Ele, NL).

推荐阅读