Пролог - аргументы не созданы в достаточной степени

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

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

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


person Eddwhis    schedule 22.05.2014    source источник
comment
Потому что вы делаете R1 is R+1, когда R не создается в случае not_number([a], R). Ваш рекурсивный регистр немного сдвинут назад. Вы хотите сделать not_number(T, R1), R is R1+1.   -  person lurker    schedule 23.05.2014
comment
На этой странице также есть хорошие обсуждения на is   -  person Yibo Yang    schedule 02.04.2016


Ответы (4)


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

person Rialgar    schedule 14.12.2015

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

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

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

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

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

number(Head).

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

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

person Darge Elar    schedule 22.05.2014

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

Например, ваша программа работает точно так, как ожидалось, если вы просто используете 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.

person mat    schedule 02.12.2015
comment
?- not_number([A,A,B],N). имеет одно решение, N = -3. Мне это не кажется правильным. - person repeat; 14.12.2015
comment
Виновник? number/1, конечно, поскольку он смешивает тестирование для создания экземпляра с тестированием типа. Решение: functor(X,_,_), \+ number(X) вместо \+ number(X). Или, что еще лучше, if_/3 с правильным овеществленным предикатом теста ... - person repeat; 15.12.2015
comment
Совершенно верно. Предлагаю must_be(nonvar, X). Теперь можно обсудить этот дополнительный момент, поскольку основная проблема, о которой сообщает OP, решена с помощью декларативной арифметики. - person mat; 15.12.2015
comment
Верно, я упустил суть, так как не заметил, насколько похожи два кода. - person repeat; 15.12.2015

В моем случае вместо is пришлось использовать =

Вместо этого

X is AnotherVariable

Мне пришлось написать

X = AnotherVariable
person Justice Bringer    schedule 12.06.2020