I am trying to solve a simple prolog question but I am not able to solve it. From a list a need to create a sublist given the index I and then from I the next elements given as N. If the index is greater than the list lenght I will get the sublist empty. If N (number of elements) is greater than the rest of elements in the list I will get all the elements from I until the end.
Here, I got one part of the assignment, I can get from the index I, the next elements N. Now I ask about the other parts in the assignment:
1) When I
(index) is longer than the list length, I have to get an empty list in the sublist.
?- sublist([a,b,c,d],5,2,L)
L=[]
2) When N
(Next elements) is greater than the number of elements we have rest, I need to get all the elements from that position till the end.
?- sublist([a,b,c,d],4,4,L)
L=[d]
The code I already have is the next one, this one is working:
sublist([X|_],1,1,[X]).
sublist([],_,_,[]).% I use this one for the case bases
sublist([X|Xs],1,K,[X|Ys]):-
K>1,
K1 is K-1,
sublist(Xs,1,K1,Ys).
sublist([_|Xs],I,K,Ys):-
I > 1,
I1 is I-1,
sublist(Xs,I1,K,Ys).
sublist([X|_], 1, 1, [X]).
This is a good clause. It says that a sublist of length 1 starting at 1 taken from the list [X|_]
is [X]
.
sublist([X|Xs], 1, K, [X|Ys]) :-
K > 1,
K1 is K - 1,
sublist(Xs, 1, K1, Ys).
This is also a good clause. It says that the sublist of length K
starting at 1 taken from [X|Xs]
starts with X
and has a tail Ys
which is the sublist of length K-1
from the tail of the first list ( Xs
) starting at 1.
sublist([_|Xs], I, K, Ys) :-
I > 1,
I1 is I - 1,
K1 is K - 1,
sublist(Xs, I1, K1, Ys).
This clause has an issue. If you have a list [_|Xs]
and want to take a sublist of length K
start at I
(for I
greater than 1), you take the sublist of length K-1
from its tail starting at position I-1
. The question is: why would the sublist now need to be length K-1
? The purpose of this clause should be to reduce the problem to the case where you're dealing with a starting index of 1
, then let the second clause take care of the rest.
Then in your definition of the desired behavior, you have: If N (number of elements) is greater than the rest of elements in the list I will get all the elements from I until the end. This notion currently isn't in any of the clauses. The base case is currently your first clause which specifically requires a length of 1 to produce a list of length 1. You need another base case clause that handles the case where the first list goes empty but K
might still be any value:
sublist([], ?, _, ?).
Just fill in the ?
with something logical. :)
just to show how nondeterministic builtins like nth1 /3 can help...
sublist(List, From, Count, SubList) :-
findall(E, (nth1(I, List, E), I >= From, I < From + Count), SubList).
edit a note to say that this 'one liner' is actually a lot less efficient than a crafted sublist/4.
Indeed,
2 ?- N=1000000,length(L,N),time(sublist(L,N,1,V)).
% 3,000,014 inferences, 2.129 CPU in 2.134 seconds (100% CPU, 1409024 Lips)
N = 1000000,
L = [_G28, _G31, _G34, _G37, _G40, _G43, _G46, _G49, _G52|...],
V = [_G3000104].
3 ?- N=1000000,length(L,N),time(sublist(L,1,1,V)).
% 4,000,012 inferences, 2.549 CPU in 2.553 seconds (100% CPU, 1569076 Lips)
N = 1000000,
L = [_G28, _G31, _G34, _G37, _G40, _G43, _G46, _G49, _G52|...],
V = [_G3000104].
I'm going to see if some kind of cut inside findall' predicate could solve this problem, but it's unlikely. This one is better:
sublist(List, From, Count, SubList) :-
To is From + Count - 1,
findall(E, (between(From, To, I), nth1(I, List, E)), SubList).
18 ?- N=1000000,length(L,N),time(sublist(L,3,3,V)).
% 28 inferences, 0.000 CPU in 0.000 seconds (93% CPU, 201437 Lips)
N = 1000000,
L = [_G682, _G685, _G688, _G691, _G694, _G697, _G700, _G703, _G706|...],
V = [_G3000762, _G3000759, _G3000756].
Here's one solution (though it's probably not what your professor wants):
sublist( Xs , Offset , Count , Ys ) :- %
length(Prefix,Offset ) , % construct a list of variables of length 'offset'
length(Ys,Count) , % construct a list of variables of length 'count'
append(Prefix,Suffix,Xs) , % strip the first first 'offset' items from the source list ,
append(Ys,_,Suffix) % extract the first 'count' items from what's left.
. % easy!
That's one approach, letting Prolog's built-ins do the work for you.
Here's another approach that doesn't use any built-ins. This one uses one helper predicate that simply splits a list into a prefix of the specified length, and a suffix, consisting of whatever is left over.
sublist( Xs , Offset , Count , Ys ) :-
split(Xs,Offset,_,X1) , % extract the first 'offset' items from the lsit and toss them
split(X1,Count,Ys,_) % extract the first 'count' items from the remainder to get the result.
.
split( [] , 0 , [] , [] ) . % splitting a zero-length prefix from an empty list yields a zero-length prefix and a zero length suffix.
split( [X|Xs] , 0 , [] , [X|Xs] ) . % splitting a zero-length prefix from a non-empty list yields a zero-length prefix and the non-empty list.
split( [X|Xs] , N , [X|Ps] , Qs ) :- % Otherwise...
N > 0 , % - if the count is positive
N1 is N-1 , % - we decrement count
split( Xs , N1 , Ps , Qs ) % - and recurse down, prepending the head of the source list to the prefix
. % Easy!
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.