scooby_m
scooby_m

Reputation: 1

Prolog. How to return a list of integers between a range

I want to return a list of integers given an input: rList :- (X,Y,[]) (1,4, N) returns N= [1,2,3,4]. This also needs to work for negative integers and for the other way around such that the input (4,-2, N) returns N= [4,3,2,1,0,-1,-2].

So far i have programmed the beginning, but I am stuck. Can someone explain to me how I should continue?

rList(_ , _ ,[ ]).
rList(X, Y, [X|T])  :-
    must_be(integer, X),
    must_be(integer, Y),
    X =< Y,
    N is X + 1,
    rList(N,Y,T).

this returns for: rList(1,4,N) N= [], [1], [1,2], [1,2,3], [1,2,3,4], false. Also all those first steps should not be given back as a result. I just need the last list ([1,2,3,4]) as an output.

Upvotes: 0

Views: 276

Answers (4)

brebs
brebs

Reputation: 4422

The increment-or-decrement decision should be made once, for performance, so here it is:

range_list2(IntStart, IntEnd, Lst) :-
    integer(IntStart),
    integer(IntEnd),
    IntInc is sign(IntEnd - IntStart),
    range_list2_(IntStart, IntEnd, IntInc, Lst).
    
range_list2_(Int, Int, _IntInc, [Int]) :- !.
range_list2_(IntUpto, IntEnd, IntInc, [IntUpto|Lst]) :-
    IntUpto1 is IntUpto + IntInc,
    range_list2_(IntUpto1, IntEnd, IntInc, Lst).

Result in swi-prolog:

?- time(range_list2(-5, 5, L)).
% 23 inferences, 0.000 CPU in 0.000 seconds (89% CPU, 438237 Lips)
L = [-5,-4,-3,-2,-1,0,1,2,3,4,5].

?- time(range_list2(5, -5, L)).
% 23 inferences, 0.000 CPU in 0.000 seconds (89% CPU, 430623 Lips)
L = [5,4,3,2,1,0,-1,-2,-3,-4,-5].

Upvotes: 1

Nicholas Carey
Nicholas Carey

Reputation: 74197

Just a simple

range(B,B,[B]).
range(A,B,[A|Ns]) :- A < B , A1 is A+1, range(A1,B,Ns) .
range(A,B,[A|Ns]) :- A > B , A1 is A-1, range(A1,B,Ns) .

should do the trick. If you need to guard it, a simple decorator will do:

range( A , B , Ns ) :- integer(A), integer(B), range_(A,B,Ns) .

range_( B , B , [B]    ) .
range_( A , B , [A|Ns] ) :- A < B , A1 is A+1, range_(A1,B,Ns) .
range_( A , B , [A|Ns] ) :- A > B , A1 is A-1, range_(A1,B,Ns) .

Upvotes: 0

chansey
chansey

Reputation: 1409

Solution 1 (common solution):

?- findall(X, between(-2,4,X) , L).
L = [-2,-1,0,1,2,3,4].

Solution 2 (implement built-in between):

my_between(L, U, X) :-
  L =< U, !,
  (   X = L 
  ;   L1 is L + 1,
      my_between(L1, U, X) ).

?- my_between(-2,4,X).
X = -2 ;
X = -1 ;
X = 0 ;
X = 1 ;
X = 2 ;
X = 3 ;
X = 4 ;
false.;

Solution 3 (follow your solution):

rList(X, Y ,[]) :- X > Y, !.
rList(X, Y, [X|T]) :-
  N is X + 1,
  rList(N,Y,T).

?- rList(-2, 4, L).
L = [-2,-1,0,1,2,3,4].

?- rList(4, 4, L).
L = [4].

?- rList(5, 4, L).
L = [].

--- Edit ---

Thanks @slago, I didn't notice the two-way requirement, but it can be easily implemented:

my_between2(U, L, X) :-
  L =< U, !,
  (   X = U 
  ;   U1 is U - 1,
      my_between2(U1, L, X) ).


bi_between(L, U, X) :- L =< U, !, my_between(L, U, X).
bi_between(U, L, X) :- my_between2(U, L, X).

?- findall(X, bi_between(-2,4,X) , L).
L = [-2,-1,0,1,2,3,4].

?- findall(X, bi_between(4,-2,X) , L).
L = [4,3,2,1,0,-1,-2].

Also, for rList:

bi_rList(X, Y ,L) :- X =< Y, !, rList(X,Y,L).
bi_rList(X, Y, L) :- rList(Y,X,L1), reverse(L1,L).

?- bi_rList(-2, 4, L).
L = [-2,-1,0,1,2,3,4].

?- bi_rList(4, -2, L).
L = [4,3,2,1,0,-1,-2].

Upvotes: 0

slago
slago

Reputation: 5509

A possible solution is:

range(Start, Stop, List) :-
    must_be(integer, Start),
    must_be(integer, Stop),
    findall(X, elem(Start,Stop,X), List).

elem(Start, Stop, Element) :-
    Step is sign(Stop-Start),
    N is abs(Stop-Start),
    between(0, N, I),
    Element is Start + I*Step.

Examples:

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

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

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

?- range(5, 5, L).
L = [5].

?- range(5, 5, []).
false.

?- range(5, 6, [5, 6]).
true.

?- range(-5, -1, L).
L = [-5, -4, -3, -2, -1].

?- range(-1, -5, L).
L = [-1, -2, -3, -4, -5].

Upvotes: 0

Related Questions