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. Однако, возможно, стоит потратить немного времени на некоторые другие вопросы. Например, самый общий запрос: какие списки есть с M
0
и N
1
"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, мы увидим другое наблюдение: если есть M
0
и N
1
длина списка фактически фиксирована, а именно 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.