PROLOG Неопределенная процедура ERROR (рекурсия двух параметров)

count([], 0, 0).
count([X|T], M, N) :- 1 is X, count(T, MRec, NRec), 
                              M is MRec, N is NRec+1.

count([X|T], M, N) :- 0 is X, count(T, MRec, NRec), 
                              M is MRec+1, N is NRec.

control_number(L) :- count_digit(L, M, N), 2 is M, 3 is N.


?- control_number([1,1,0,0,1]).
ERROR: count_number/3: Undefined procedure: count/3

Привет всем, мне нужна помощь. Этот код должен предоставлять счет двух отдельных номеров рекурсивно. Однако я не могу предоставить рекурсию с 2 параметрами. Похоже MRec а также NRec не действует ни в коем случае. Любая помощь будет оценена. Спасибо сейчас...

3 ответа

Как уже указывалось @false, этот предикат вполне подходит для clpfd. Кроме того, я добавил ограничения (помечены как % <-) чтобы убедиться, что M а также N больше чем 0 в рекурсивных случаях, поэтому Пролог не продолжает искать дальнейшие решения после того, как эти переменные были уменьшены до 0,

:- use_module(library(clpfd)).

count_digits([], 0, 0).
count_digits([1|T], M, N) :-
   N #> 0,                        % <-
   NRec #= N-1,
   count_digits(T, M, NRec).
count_digits([0|T], M, N) :-
   M #> 0,                        % <-
   MRec #= M-1,
   count_digits(T, MRec, N).

С этими незначительными изменениями вы уже можете использовать count_digits/3 несколькими способами. Например, попросить все списки с 2 0и 3 1"S:

   ?- count_digits(L,2,3).
L = [1,1,1,0,0] ? ;
L = [1,1,0,1,0] ? ;
L = [1,1,0,0,1] ? ;
L = [1,0,1,1,0] ? ;
L = [1,0,1,0,1] ? ;
L = [1,0,0,1,1] ? ;
L = [0,1,1,1,0] ? ;
L = [0,1,1,0,1] ? ;
L = [0,1,0,1,1] ? ;
L = [0,0,1,1,1] ? ;
no

Или посчитать случаи 0и 1находится в данном списке:

   ?- count_digits([1,1,0,0,1],M,N).
M = 2,
N = 3
% 1

Или даже спросить количество 0и 1в списке, содержащем переменные:

   ?- count_digits([1,0,X,Y],M,N).
M = X = Y = 1,
N = 3 ? ;
M = N = 2,
X = 1,
Y = 0 ? ;
M = N = 2,
X = 0,
Y = 1 ? ;
M = 3,
N = 1,
X = Y = 0

Это уже неплохо, и предикатом можно довольствоваться как есть. Это, конечно, хорошо, если вы собираетесь использовать его с control_number/1, как предложено @false. Однако, возможно, стоит потратить немного времени на некоторые другие вопросы. Например, самый общий запрос: какие списки есть с M0и N1"S?

   ?- count_digits(L,M,N).
L = [],
M = N = 0 ? ;
L = [1],
M = 0,
N = 1 ? ;
L = [1,1],
M = 0,
N = 2 ? ;
L = [1,1,1],
M = 0,
N = 3 ?
...

Это только производит списки, которые состоят из 1исключительно. Это потому, что первое рекурсивное правило - это правило, описывающее случай с 1 как первый элемент списка. Таким образом, решения приходят в несправедливом порядке. То, что происходит со следующим запросом, может быть даже несколько менее интуитивно понятно: какие списки существуют с таким же (но не фиксированным) числом 0и 1"S:

   ?- count_digits(L,M,M).
L = [],
M = 0 ? ;

Есть ответ, а затем цикл предикатов. Это не совсем желаемое свойство. Интересное наблюдение об этом запросе: если его использовать в списках с фиксированной длиной, результат на самом деле будет таким, как ожидалось:

   ?- length(L,_), count_digits(L,M,M).
L = [],
M = 0 ? ;
L = [1,0],
M = 1 ? ;
L = [0,1],
M = 1 ? ;
L = [1,1,0,0],
M = 2 ? ;
L = [1,0,1,0],
M = 2 ? ;
...

Применение этой идеи к предыдущему запросу дает справедливое упорядочение результатов:

   ?- length(L,_), count_digits(L,M,N).
L = [],
M = N = 0 ? ;
L = [1],
M = 0,
N = 1 ? ;
L = [0],
M = 1,
N = 0 ? ;
L = [1,1],
M = 0,
N = 2 ? ;
L = [1,0],
M = N = 1 ? ;
...

Конечно, было бы неплохо получить эти результаты без добавления префикса к вспомогательной цели. И, присмотревшись немного поближе к описанию, описанному в count_digits/3, мы увидим другое наблюдение: если есть M0и N1длина списка фактически фиксирована, а именно M+N, Чтобы эти наблюдения работали, можно переименовать count_digits/3 в list_0s_1s/3 и переопределить count_digits/3 в качестве вызывающего предиката со следующими ограничениями:

:- use_module(library(clpfd)).

count_digits(L,M,N) :-
   X #= M+N,
   length(L,X),               % L is of length M+N
   list_0s_1s(L,M,N).

list_0s_1s([], 0, 0).
list_0s_1s([1|T], M, N) :-
   N #> 0,
   NRec #= N-1,
   list_0s_1s(T, M, NRec).
list_0s_1s([0|T], M, N) :-
   M #> 0,
   MRec #= M-1,
   list_0s_1s(T, MRec, N).

Первые три запроса выше дают те же результаты, что и раньше, но эти два теперь дают результаты в правильном порядке без зацикливания:

   ?- count_digits(L,M,N).
L = [],
M = N = 0 ? ;
L = [1],
M = 0,
N = 1 ? ;
L = [0],
M = 1,
N = 0 ? ;
L = [1,1],
M = 0,
N = 2 ? ;
L = [1,0],
M = N = 1 ? 
...

   ?- count_digits(L,M,M).
L = [],
M = 0 ? ;
L = [1,0],
M = 1 ? ;
L = [0,1],
M = 1 ? ;
L = [1,1,0,0],
M = 2 ? ;
L = [1,0,1,0],
M = 2 ? 
...

Два последних замечания о вашем предикате control_number/1: во-первых, если вы используете is/2, убедитесь, что используете его так:

   ?- M is 2.
M = 2
% 1

вместо (как используется в вашем определении control_number/1):

   ?- 2 is M.
     ERROR!!
     INSTANTIATION ERROR- in arithmetic: expected bound value
% 1

А во-вторых, если вы собираетесь использовать предикат вроде control_number/1 для вызова count_digits/3, не ставьте такие цели, как M is 2 а также N is 3 после фактического вызова count_digits/3. Таким образом, вы запрашиваете все решения count_digits(L,M,N)которых бесконечно много, и в последующих целях вы затем отфильтровываете те, которые удовлетворяют вашим ограничениям (M is 2 а также N is 3). С этим упорядочением целей вы убедитесь, что control_number/1 не завершается после создания конечного числа решений, поскольку бесконечное число кандидатов-решений создается первой целью, которая впоследствии завершается неудачей в соответствии с вашими ограничениями. Вместо этого сначала поместите такие ограничения или поместите их непосредственно в качестве аргументов в цель, опубликованную @false.

Вот более идиоматическое переписывание:

count_digits([], 0, 0).
count_digits([1|T], M, N) :-
   count_digits(T, M, NRec),
   N is NRec+1.
count_digits([0|T], M, N) :-
   count_digits(T, MRec, N),
   M is MRec+1.

control_number(L) :-
   count_digits(L, 2, 3).

Это можно улучшить, используя library(clpfd), Может быть, кто-то еще ответит.

Параметры накопления - это путь (вам нужен вспомогательный предикат для инициализации этих параметров):

count(List,C0,C1) :-
    count_aux(List,C0,C1,0,0).

count_aux([],C0,C1,C0,C1).
count_aux([0|Rest],C0,C1,PartialC0,PartialC1) :-
    IncC0 is PartialC0+1,
    !,
    count_aux(Rest,C0,C1,IncC0,PartialC1).
count_aux([1|Rest],C0,C1,PartialC0,PartialC1) :-
    IncC1 is PartialC1+1,
    !,
    count_aux(Rest,C0,C1,PartialC0,IncC1).
count_aux([_|Rest],C0,C1,PartialC0,PartialC1) :-
    count_aux(Rest,C0,C1,PartialC0,PartialC1).

Замечания:

  • Вы должны звонить count/3, а не count_aux/5.
  • Последние два параметра для count_aux / 5 являются параметрами накопления, инициализированными в ноль.
  • Первое предложение count_aux / 5 - это базовый случай, в котором возвращаются накопленные параметры.
  • Последнее предложение count_aux / 5 предотвращает сбой предиката, если элементы списка не равны 0 или 1.

Пример:

?- count([1,1,0,0,0,k],A,B).
A = 3,
B = 2.
Другие вопросы по тегам