Question

I am trying to write a function - decListRange(X,List) which give a list in range [X-1:1] by descending order. For example -
decListRange(9,List).

Will give -

List = [8,7,6,5,4,3,2,1].

I tried the following but it goes into infinite loop -

decListRange(1,[]) :- !. 
decListRange(X,[H|Rest]) :-
    H  = X-1, NextX = X - 1 ,decListRange(NextX,Rest).
Was it helpful?

Solution

You have two problems. The first real one is that you need to use is instead of =:

H is X-1

This is needed to trigger arithmetic evaluation. Your second problem isn't a real problem but speaks to a bigger misunderstanding, which is that H and NextX are equivalent. Because Prolog only has bindings and not "assignables" as it were, you should never really need to create two "variables" with the same binding. There's no state being kept around for you to modify later.

Cleaning up both you get this:

decListRange(1, []) :- !.
decListRange(X, [H|Rest]) :-
  X > 1,
  H is X-1,
  decListRange(H, Rest).

Edit 2: a clpfd implementation

:- use_module(library(clpfd)).

declist(N, L) :- N == 1, !, L = []. % green cut
declist(1, []). 
declist(N, [N1|Ns]) :- 
  N #> 1, 
  N1 #= N - 1, 
  declist(N1, Ns).

This one has the properties @false mentions below in the comments:

?- declist(3, L).
L = [2, 1] ;
false.

?- declist(3, [2,1]).
true ;
false.

?- declist(N, [3,2,1]).
N = 4.

?- declist(N, X).
N = 1,
X = [] ;
N = 2,
X = [1] ;
N = 3,
X = [2, 1] ;
N = 4,
X = [3, 2, 1] ;
N = 5,
X = [4, 3, 2, 1] .

Edit: a short interlude on the difference between = and is.

In procedural languages = is almost always syntax for assigning a particular value to a variable. In Prolog, variables are bindings, and once established they cannot be directly modified by reassigning the variable a different value. Instead they work more like variables in math and logic, where the variable "stands in" for interesting values, but those values are themselves basically immutable. In Prolog, = essentially asks the unification engine to establish bindings. So if you were to do something like this:

?- name(X, Y) = name(bob, tony).

Prolog responds with variable bindings:

X = bob,
Y = tony.

Once those bindings exist, contradictory bindings will fail and affirmative bindings will succeed:

?- name(X, Y) = name(bob, tony), X = bob.
X = bob,
Y = tony.

?- name(X, Y) = name(bob, tony), X = william.
false.

The unification algorithm itself doesn't know anything about arithmetic. This has the pleasant side-effect that you can use any expression raw. For instance:

?- Expr = X + 3, Z + Q = Expr.
Expr = Z+3,
X = Z,
Q = 3.

This is probably really surprising looking. You may expect that somehow Prolog was smart enough to keep the expression around because it noticed X was a variable or something, but that isn't true either:

?- X = 4, Expr = X + 3, Z + Q = Expr.
X = 4,
Expr = 4+3,
Z = 4,
Q = 3.

Another way of looking at this is that Prolog is considering + to be just another operator, so X+3 is a fact just like add(X, 3) that doesn't necessarily have any special meaning. Whichever way you look at it, the is/2 operator exists to apply arithmetic reasoning and produce a value:

?- X = 4, Expr is X + 3.
X = 4,
Expr = 7.

Notice that Expr has the computed value but none of the original structure:

?- X = 4, Expr is X + 3, Z + Q = Expr.
false.

In practice, if you need to do a lot of reasoning with arithmetic, you will want to use a library like clpfd or clpqr depending on whether you're interested in integers or reals. This library enables you to do more interesting things more easily, like specify that an equation holds for values in a certain range and get those values out.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top