Prolog-Аргументы недостаточно инстанцированы

Я пишу небольшую программу, которая подсчитывает количество элементов в списке не числились. Вот мой код:

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).  

если я выполняю такой код:

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

я получаю, что r = 0 (как и должно быть)

R = 0.

но если я помещу символ в список:

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

затем я получаю эту ошибку:

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

может кто-то объяснит что не так с кодом? Я новичок в prolog.

3 ответов


Я пишу этот ответ, потому что лучший ответ был в комментарий by lurker. Я хочу, чтобы это было реальным ответом.

ваш код не работает, потому что вы делаете R1 is R+1, когда R не создается экземпляр в случае not_number([X|T], R). Ваш рекурсивный случай немного отстает. Вы хотите сделать это:

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

теперь правая сторона is создается экземпляр при его вызове.


ваша проблема в том, что в арифметических вычислениях, как это:

A - Это B

все на правой стороне (B) должно быть уже известно. Никаких переменных.

вы можете сделать что-то вроде этого:

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).

(тестировал этот код, он работает).

теперь третий аргумент-аккумулятор. Он подсчитывает, сколько существует не-чисел. Когда список пуст, этот третий аргумент объединяется со вторым и становится правильным ответ.

Prolog, когда предоставляется возможность, будет проходить через все возможные маршруты. Если вы сделаете что-то вроде этого:

cat(adam).
cat(eve).

а потом спрашивают:

?- cat(X).

вы можете получить оба ответа: X = Адам и X = Ева. Это относится и к вашему коду: обратите внимание, что когда глава списка не является числом, вы все равно можете сделать это:

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

который дает не тот ответ, который вы хотели бы. Вы должны отрезать маршруты, которые вас не интересуют. В данном случае я добавлю

number(Head).

чтобы убедиться, что мы пропускаем элемент в списке без увеличения счетчика на 1, только тогда, когда этот элемент не является числом.

чтобы применить Пролог, чтобы найти другие результаты, вы должны нажать "; " на клавиатуре (как в этом примере Адама и Евы).


общим решением таких проблем является использование ограничения.

например, ваша программа работает точно так, как ожидалось, если вы просто используете clpfd ограничения. Просто замените (is)/2 by (#=)/2 чтобы получить целочисленную арифметику, которая работает во всех направлениях:

:- 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).

Образец запроса и его результат:

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

Обратите также внимание, что я изменил код, чтобы использовать ISO-предикат (\+)/1 вместо not/1.