Reputation: 202
sublist(L1,L2,I,J)
I have a list, L1
being passed in, along with 2 indices I
and J
, and I want L2
to be the inclusive sublist containing the elements between I
and J
.
If J
is outside of the bounds, I'd like it to go from I
to the end of the list. Similarly, if I
is less than 0, I'd like to get it to go from the start of the list to J
.
I
> J
returns empty list.
What I have so far:
sublist(_,[],I,J):-
I > J.
sublist(Xs,Ys,I,Length):-
length(Xs,Length),
N > L,
sublist(Xs,Ys,I,J).
sublist([X|_],[X],1,1).
sublist([X|Xs],[X|Ys],1,J):-
J > 1,
J1 is J - 1,
sublist(Xs,Ys,1,J1).
sublist([_|Xs],Ys,I,J):-
I > 1,
I1 is I - 1,
J1 is J - 1,
sublist(Xs,Ys,I1,J1).
Upvotes: 2
Views: 3318
Reputation: 60034
I think you have just some typo in second clause. Try this (untested) correction, required to fit your requirement
If J is outside of the bounds, I'd like it to go from I to the end of the list.
sublist(Xs,Ys,I,J):-
length(Xs,Length),
J > Length,
sublist(Xs,Ys,I,Length).
Upvotes: 0
Reputation: 2336
You can solve this by rethinking your strategy.
First, what are your base cases?
I > J
In both of those cases, you would append the empty list to whatever you have so far, and call it good.
Next, what are your edge cases you care about?
J
is beyond the end of the listI
is before the beginning of the listIn the first case, you just traverse to the end of the input list. In the second, you start at the beginning of the input list.
Okay, let's try and implement that, I'm going to use an accumulator and wrap the call around it.
sublist(L1, L2, I, J):-
sublist(L1, Temp, I, J, []),
!,
reverse(Temp, L2).
We take the input list L1
, a variable to unify as our output list L2
, and the indices, I
and J
. I'm using a cut
so that I don't have to worry about backtracking for other solutions, and reverse it because the accumulated list is built in reverse.
Let's work on the base cases.
Empty list as input, just unify the accumulator with our output list. We don't care about the indices at this point. As it turns out, this also satisfies the edge case where J
is beyond the end of the list. Since at that point we will have accumulated all of the input list into the accumulator and still have a J value left over.
sublist([], L2, _I, _J, L2).
I > J, again, unify the accumulator with our output list. We don't care about the input list anymore.
sublist(_L1, L2, I, J, L2):-
I > J.
Now the edge cases.
J
beyond the end of the list was solved above.
I
is before the beginning of the list, just set that index to 0, and move on.
sublist(L1, L2, I, J, L2):-
I < 0,
sublist(L1, L2, 0, J, L2).
Now we just need to implement the actual logic. We only want to accumulate starting from the correct I
. So let's decrement I
and throw away pieces of the input list until we get to where we want to be. In order to make the end of the index match up, we need to decrement J
as well. This way we keep the same distance between indices.
sublist([_L|Ls], L2, I, J, Acc):-
I > 0,
sublist(Ls, L2, I-1, J-1, Acc).
We're finally where we want to be. So, let's start building up the list with pieces from the input list. This continues until we hit one of our base cases. After which the accumulator is returned to the original sublist
clause.
sublist([L|Ls], L2, I, J, Acc):-
sublist(Ls, L2, I, J-1, [L|Acc]).
Putting it all together we end up with:
sublist(L1, L2, I, J):-
sublist(L1, Temp, I, J, []),
!,
reverse(Temp, L2).
sublist([], L2, _I, _J, L2).
sublist(_L1, L2, I, J, L2):-
I > J.
sublist(L1, L2, I, J, L2):-
I < 0,
sublist(L1, L2, 0, J, L2).
sublist([_L|Ls], L2, I, J, Acc):-
I > 0,
sublist(Ls, L2, I-1, J-1, Acc).
sublist([L|Ls], L2, I, J, Acc):-
sublist(Ls, L2, I, J-1, [L|Acc]).
And we can test it like so:
?- sublist([1,2,3,4,5], S, 0,3).
S = [1, 2, 3, 4].
?- sublist([1,2,3,4,5], S, -1,30).
S = [1, 2, 3, 4, 5].
?- sublist([1,2,3,4,5], S, 3,1).
S = [].
?- sublist([1,2,3,4,5], S, 3,3).
S = [4].
?- sublist([1,2,3,4,5], S, 3,4).
S = [4, 5].
Upvotes: 4