简体   繁体   中英

Get list of atoms by index from i to j in Prolog

I want to get a list of atoms from my atom list based on given index_from i to index_to j. I tried many times but it did not return me the list but with a 'true' Below is my code, what do I miss? How do I get my output list instead of a 'true'.

% splitList(INPUT_LIST, INDEX_FROM, INDEX_TO, INDEX_FROM, OUTPUTLIST).
splitList(INPUT_LIST,INDEX_FROM,INDEX_TO, S,LIST) :-
    INPUT_LIST = INPUT_LIST,
    S = S,
    INDEX_FROM =:= INDEX_TO + 1,
    write(LIST),
    LIST = LIST.
splitList(INPUT_LIST,INDEX_FROM,INDEX_TO,S,LIST) :-
    INDEX_FROM < INDEX_TO + 1,
    nth0(INDEX_FROM, INPUT_LIST, ELEMENT),
    (  INDEX_FROM =:= S ->
    L = []
    ;   
    L = LIST
    ),
    IF is INDEX_FROM + 1,
    append([ELEMENT],L,NLIST),
    splitList(INPUT_LIST,IF,INDEX_TO,S,NLIST),
    !.

?- splitList(['TYPE_INT', 'IDENTIFIER', 'OPEN_P', 'TYPE_INT', 'IDENTIFIER', 'COMMA'],0,3,0,L).
?- true.

My atom list: ['TYPE_INT', 'IDENTIFIER', 'OPEN_P', 'TYPE_INT', 'IDENTIFIER', 'COMMA'],

if index_from is 1, index_to is 3, I am expecting a returned output_list:
['TYPE_INT', 'IDENTIFIER', 'OPEN_P', 'TYPE_INT'] instead of a 'true', 

On my base case, 'write(LIST) does write the expected list', Any idea?

You can write such a predicate without using the cut. Think about what your predicate should describe: a list, some indices for start, end and current position and a sublist of the first list that fits your start and end indices. So list_indices_sublist might be a good name. Now consider what cases you can have:

If the current position and the end position are the same, the head of the first list is the last to be in the sublist:

list_indices_sublist([X|_Xs],_Start,End,End,[X]).

If the current position is between the start and end positions the head of the first list is in the sublist. For the tails of both lists the relation (=the predicate) must hold too (=recursion):

list_indices_sublist([X|Xs],Start,End,Pos,[X|Zs]) :-
    Pos #>= Start,
    Pos #< End,
    Nextpos #= Pos + 1,
    list_indices_sublist(Xs,Start,End,Nextpos,Zs).

If the current position is smaller than the start position the head of the list is not in the sublist. For the tails, same as above:

list_indices_sublist([X|Xs],Start,End,Pos,Zs) :-
    Pos #< Start,
    Nextpos #= Pos+1,
    list_indices_sublist(Xs,Start,End,Nextpos,Zs).

You might also consider providing a cleaner interface that hides the auxilary variable accounting for the current position in the list. Such a predicate would also be a good place for constraints that should hold for all three rules of list_indices_sublist/5, eg the start index must be smaller or equal to the end index. Let's call this predicate list_sublist_from_to/4. Putting it all together:

:- use_module(library(clpfd)).


list_sublist_from_to(L,S,Start,End) :-
    Start #=< End,
    list_indices_sublist(L,Start,End,1,S).

list_indices_sublist([X|_Xs],_Start,End,End,[X]).
list_indices_sublist([X|Xs],Start,End,Pos,[X|Zs]) :-
    Pos #>= Start,
    Pos #< End,
    Nextpos #= Pos + 1,
    list_indices_sublist(Xs,Start,End,Nextpos,Zs).
list_indices_sublist([X|Xs],Start,End,Pos,Zs) :-
    Pos #< Start,
    Nextpos #= Pos+1,
    list_indices_sublist(Xs,Start,End,Nextpos,Zs).

Concerning the example you provided: You mean 1 to 4, right?

?- list_sublist_from_to(['TYPE_INT', 'IDENTIFIER', 'OPEN_P', 'TYPE_INT', 'IDENTIFIER', 'COMMA'] ,L,1,4).
L = ['TYPE_INT','IDENTIFIER','OPEN_P','TYPE_INT'] ? ;
no

Or 1 to 3 and an answer with 3 elements in the sublist:

list_sublist_from_to(['TYPE_INT', 'IDENTIFIER', 'OPEN_P', 'TYPE_INT', 'IDENTIFIER', 'COMMA'] ,L,1,3).
L = ['TYPE_INT','IDENTIFIER','OPEN_P'] ? ;
no 

Note: If you want to start counting your list positions with 0 rather than 1 just change the 1 to 0 in the second goal of list_sublist_from_to/4

First, you should note that Prolog is not based on assignment, but unification. Then, for instance,

INPUT_LIST = INPUT_LIST,

is a tautology (unify the same term with itself, always true). You can safely remove them.

Second, your code has an additional parameter S that has no clear role. Probably a residual detail of your attempted implementation, but then it should really be 'hidden', for instance:

% splitList(INPUT_LIST, INDEX_FROM, INDEX_TO, OUTPUTLIST).
splitList(INPUT_LIST, INDEX_FROM, INDEX_TO, LIST) :-
  splitList(INPUT_LIST, INDEX_FROM, INDEX_TO, 0, LIST).

Third, a cut at end of last clause of a predicate is totally useless, since all alternatives have already been tried - with success. Nothing left to be cut...

Fourth, since you use the builtin nth0/3, then note that a much simpler implementation could be

splitList(INPUT_LIST,INDEX_FROM,INDEX_TO, LIST) :-
  findall(E, (between(INDEX_FROM,INDEX_TO,I),nth0(I,INPUT_LIST,E)), LIST).

?- splitList(['TYPE_INT', 'IDENTIFIER', 'OPEN_P', 'TYPE_INT', 'IDENTIFIER', 'COMMA'],0,3,L).
L = ['TYPE_INT', 'IDENTIFIER', 'OPEN_P', 'TYPE_INT'].

But let's try to correct your code. Could be

splitList(INPUT_LIST,INDEX_FROM,INDEX_TO, LIST) :-
  splitList(INPUT_LIST,INDEX_FROM,INDEX_TO, 0, LIST).

splitList(INPUT_LIST,INDEX_FROM,INDEX_TO, S,LIST) :-
  S < INDEX_FROM,
  S1 is S+1,
  splitList(INPUT_LIST,INDEX_FROM,INDEX_TO, S1,LIST).
splitList(INPUT_LIST,INDEX_FROM,INDEX_TO, S,[ELEMENT|LIST]) :-
  S >= INDEX_FROM, S =< INDEX_TO,
  nth0(S, INPUT_LIST, ELEMENT),
  S1 is S+1,
  splitList(INPUT_LIST,INDEX_FROM,INDEX_TO, S1,LIST).
splitList(_INPUT_LIST,_INDEX_FROM,INDEX_TO, S,[]) :-
  S > INDEX_TO.

and will get you a similar result as the findall/3 based one-liner listed above. Note that instead of using append/3, the ELEMENT is 'consed' into the 'output' parameter. See if you can spot the problem that is inherent with this code, specifically, try to understand why

?- splitList(['TYPE_INT', 'IDENTIFIER', 'OPEN_P', 'TYPE_INT', 'IDENTIFIER', 'COMMA'],0,10,L).
false.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM