25

I am writing a little program which counts how many elements in a list are not numbers. Here is my code:

not_number([],0).
not_number([X|T],R):- 
    not(number(X)),
    R1 is R+1,  
    not_number(T,R1). 

not_number([_|Tail],Result):-
    not_number(Tail,Result).  

If I execute code like this :

?- not_number([1,2,3,5], R).

I am getting that R = 0 (as it should be)

R = 0.

But if I put a character in the list:

?- not_number([1,2,3,5,a], R).

then I am getting this error:

ERROR: not_number/2: Arguments are not sufficiently instantiated
   Exception: (10) not_number([a], _G247) ? 

Can someone explain whats wrong with code? I am new to prolog.

Will Ness
  • 62,652
  • 8
  • 86
  • 167
Eddwhis
  • 984
  • 2
  • 14
  • 31
  • 13
    Because you're doing `R1 is R+1` when `R` isn't instantiated in the case `not_number([a], R)`. Your recursive case is strung a little backwards. You want to do `not_number(T, R1), R is R1+1`. – lurker May 22 '14 at 20:16
  • [This page](http://www.learnprolognow.org/lpnpage.php?pagetype=html&pageid=lpn-htmlse19) also has good discussions on `is` – Yibo Yang Apr 02 '16 at 20:59

4 Answers4

28

I am writing this answer, because the best answer yet was in a comment by lurker. I want to have it show up as an actual answer.

Your code is not working, because you're doing R1 is R+1 when R isn't instantiated in the case not_number([X|T], R). Your recursive case is strung a little backwards. You want to do this:

not_number([X|T],R):- 
    not(number(X)),
    not_number(T,R1),
    R is R1+1.

Now the right side of the is is instantiated when it is called.

Community
  • 1
  • 1
Rialgar
  • 643
  • 5
  • 9
4

Your problem is that in arithmetic computation like this:

A is B

everything on the right side (B) has to be already known. No variables there.

You may do something like this:

not_number(X, Y) :- not_number(X, Y, 0).
not_number([], Y, Y).
not_number([H|T], Y, Z) :-
    \+ (number(H)), 
    Z1 is Z+1,
    not_number(T, Y, Z1).

not_number([H|T], Y, Z) :-
    number(H),
    not_number(T, Y, Z).

(tested this code now, it works).

Now the third argument is an accumulator. It counts how many not-numbers there are. When the list is empty, this third argument is unified with the second one and it becomes the proper answer.

Prolog, when given the chance, will go through all the routes possible. If you do something like this:

cat(adam).
cat(eve).

and then ask:

?- cat(X).

You could get both answers: X = adam and X = eve. It applies to your code too: notice that when the head of the list is not a number, you can still do this:

not_number([_|Tail],Result):-
    not_number(Tail,Result).  

which gives not the answer you would like. You have to cut off the routes that don't interest you. In this case, I would add

number(Head).

to ensure that we skip an element in a list without increasing the counter by 1 only when this element is not a number.

To enforce Prolog to find some other results, you must press ";" on your keyboard (like in this adam and eve example).

Darge Elar
  • 85
  • 7
2

The general solution to such problems is to use constraints.

For example, your program works exactly as expected if you just use constraints. Simply replace (is)/2 by (#=)/2 to obtain integer arithmetic that works in all directions:

:- use_module(library(clpfd)).

not_number([],0).
not_number([X|T],R):- 
    \+ number(X),
    R1 #= R+1,  
    not_number(T,R1). 

not_number([_|Tail],Result):-
    not_number(Tail,Result).

Sample query and its result:

?- not_number([1,2,3,5], R).
R = 0.

Note also that I have changed the code to use the ISO-predicate (\+)/1 instead of not/1.

mat
  • 39,707
  • 3
  • 42
  • 68
  • `?- not_number([A,A,B],N).` has one solution, `N = -3`. That does not look right to me. – repeat Dec 14 '15 at 20:56
  • 1
    The culprit? `number/1`, of course, as it mixes testing for instantiation with testing the type. Workaround: `functor(X,_,_), \+ number(X)` instead of `\+ number(X)`. Or, even better, `if_/3` with the right reified test predicate... – repeat Dec 14 '15 at 21:05
  • Very true. I suggest `must_be(nonvar, X)`. This additional point can now be discussed since the fundamental problem reported by OP is fixed by using declarative arithmetic. – mat Dec 14 '15 at 21:11
  • Right, I missed the actual point, as I did not notice how similar the two codes are. – repeat Dec 14 '15 at 21:15
0

In my case instead of is I had to use =

Instead of this

X is AnotherVariable

I had to write

X = AnotherVariable
Justice Bringer
  • 597
  • 6
  • 19