Пролог. Аргументы недостаточно
Я пишу небольшую программу, которая подсчитывает, сколько элементов в списке не являются числами.
Вот мой код:
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
.