Пролог. Аргументы недостаточно

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

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

Может кто-нибудь объяснить, что не так с кодом? Я новичок в прологе.

Ответы

Ответ 1

Я пишу этот ответ, потому что лучший ответ еще был в comment 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 создается, когда она вызывается.

Ответ 2

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

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

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

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

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

cat(adam).
cat(eve).

а затем спросите:

?- cat(X).

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

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

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

number(Head).

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

Чтобы заставить Prolog найти другие результаты, вы должны нажать ";" на клавиатуре (например, в этом примере adam и eve).

Ответ 3

Общее решение таких задач - использование ограничений.

Например, ваша программа работает точно так, как ожидалось, если вы просто используете clpfd. Просто замените (is)/2 на (#=)/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.