4

I'm writing a simple code generating a simple list with 5 numbers whose first variable should be positive and I'm trying to understand why this code fails

test([H|T]) :- H > 0, length(T,4).

when I call with

 length(X,5), test(X).

it shows me the following error:

ERROR: Arguments are not sufficiently instantiated

When I debug the code, the H variable in test isn't instantiated.

Anyone know why?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • How can it even know it should be a number? – Eugene Sh. Jun 28 '17 at 16:27
  • 3
    try the query `length(X, 5).`. – Will Ness Jun 28 '17 at 16:31
  • `length(X, 5)` gives you a list of... well.... length 5, but whose elements aren't known yet. So they're variable. `length/2` doesn't generate the actual elements. – lurker Jun 28 '17 at 16:33
  • 3
    You get an instantiation error for the same reason that, simply, `H > 0` gives you one. – Wouter Beek Jun 28 '17 at 20:40
  • in other words, try the query `H > 0.` at your Prolog prompt. `H` is not instantiated because *you* have not instantiated it to anything yet. But if you do e.g. `H = 3, H > 0.`, or `H is 1 + 2, H > 0.`, *then* it works. see also https://stackoverflow.com/tags/instantiation-error/info. – Will Ness Jun 30 '17 at 14:28

1 Answers1

5

The issue here is that your rule for test([H|T]) doesn't describe in Prolog that H is a positive integer. It only tests if H > 0, which fails since H has not instantiation. Just attempting to compare an uninstantiated variable with a number (H > 0 in this case) doesn't cause Prolog to assume you intended H to be a number, and further, doesn't instantiate H.

Further, your rule for test/1 doesn't describe the rest of the list (T) other than to force that it be length 4. Since you're query establishes the rule that the length of the original list be 5, this stipulation is redundant.

You appear to be wanting to define test(L) such that it means L is an arbitrary list of positive integers. This is generally done using CLP(FD):

:- use_module(library(clpfd)).

test(X) :- X ins 1..10000.

This rule says that X is a list whose values are in the range 1 to 10000. The appropriate query to generate the lists of length 5 would then be:

?- length(X, 5), test(X), label(X).
X = [1, 1, 1, 1, 1] ;
X = [1, 1, 1, 1, 2] ;
X = [1, 1, 1, 1, 3] ;
X = [1, 1, 1, 1, 4] ;
X = [1, 1, 1, 1, 5] ;
...

If you want to restrict it further and say that elements need to be unique, you can use all_different/1:

test(X) :- X ins 1..10000, all_different(X).

?- length(X, 5), test(X), label(X).
X = [1, 2, 3, 4, 5] ;
X = [1, 2, 3, 4, 6] ;
X = [1, 2, 3, 4, 7] ;
X = [1, 2, 3, 4, 8] ;
X = [1, 2, 3, 4, 9] ;
X = [1, 2, 3, 4, 10] ;
...
lurker
  • 56,987
  • 9
  • 69
  • 103
  • 3
    `all_distinct/1` has stronger propagation and should typically be used instead of `all_different/1`, though I doubt this makes any difference in this particular case. – Fatalize Jun 29 '17 at 14:28
  • 2
    @Fatalize thank you, good suggestion. And I agree, it's probably splitting hairs in this question/answer scenario. – lurker Jun 29 '17 at 14:29