Пролог - Аргументы недостаточно проработаны
Я пишу небольшую программу, которая подсчитывает, сколько элементов в списке не являются числами. Вот мой код:
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) ?
Может кто-нибудь объяснить, что не так с кодом? Я новичок в прологе.
3 ответа
Я пишу этот ответ, потому что лучшим ответом был комментарий 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
создается, когда он вызывается.
Ваша проблема в том, что в арифметических вычислениях это так:
А есть Б
все на правой стороне (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 = Адам и X = Ева. Это относится и к вашему коду: обратите внимание, что когда заголовок списка не является числом, вы все равно можете сделать это:
not_number([_|Tail],Result):-
not_number(Tail,Result).
который дает не тот ответ, который вы хотели бы. Вы должны отрезать маршруты, которые вас не интересуют. В этом случае я бы добавил
number(Head).
чтобы гарантировать, что мы пропускаем элемент в списке, не увеличивая счетчик на 1, только когда этот элемент не является числом.
Чтобы заставить Пролог найти другие результаты, вы должны нажать ";" на вашей клавиатуре (как в этом примере Адама и Евы).
Общее решение таких проблем заключается в использовании ограничений.
Например, ваша программа работает точно так, как ожидалось, если вы просто используете ограничения 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
,
В моем случае вместо is
Мне пришлось использовать =
Вместо этого
X is AnotherVariable
Мне пришлось написать
X = AnotherVariable