Поскольку этот вопрос использует список, я хотел решить его с помощью DCG.В процессе я понял, что можно использовать полуконтекст.( DCG Primer )
Первоначальная проблема заключается в том, чтобы вернуть количество элементов в списке, но если два одинаковых элемента расположены рядом друг с другом, не увеличивайте счет.
Хотя мой код работает для некоторых тестовых случаев, он не работает для других.Это всего лишь один пункт, который терпит неудачу.При просмотре кода с помощью отладчика выясняется, что вторая переменная состояния, возвращаемый список, связана с вызовом предложения, когда я думаю, что она должна быть не связана. РЕДАКТИРОВАТЬ Решил эту часть проблемы ниже.
Я использую SWI-Prolog 8.0.
Предложение, вызывающее проблему:
count_dcg(N0,N),[C2] -->
[C1,C2],
{ N is N0 + 1 }.
Примечание: C1 помечен как Singleton variables: [C1]
Обычно я бы изменил C1
на _
, но в этом случае мне нужно указать, что первый и второй элементы, обрабатываемые в настоящее время, отличаются.Другими словами, он использует неудачу объединения в качестве защиты.
Глядя на DCG с использованием листинга / 1, выявляет использование _
, которое может быть проблемой, но не уверенным.
count_dcg(C, B, A, E) :-
A=[_, F|D],
B is C+1,
G=D,
E=[F|G].
Как правильно решить проблему с помощью DCG?
См. Продолжение вопрос .
Текущий исходный код
% empty list
% No semicontext (push back) needed because last item in list.
count_dcg(N,N) --> [].
% single item in list
% No semicontext (push back) needed because only one item removed from list.
count_dcg(N0,N) -->
[_],
\+ [_],
{ N is N0 + 1 }.
% Semicontext (push back) needed because two items removed from list.
% Need second item to stay on list.
count_dcg(N,N),[C] -->
[C,C].
% Semicontext (push back) needed because two items removed from list.
% Need second item to stay on list.
count_dcg(N0,N),[C2] -->
[C1,C2],
{ N is N0 + 1 }.
count(L,N) :-
DCG = count_dcg(0,N),
phrase(DCG,L).
Контрольные примеры
:- begin_tests(count).
test(1,[nondet]) :-
count([],N),
assertion( N == 0 ).
test(2,[nondet]) :-
count([a],N),
assertion( N == 1 ).
test(3,[nondet]) :-
count([a,a],N),
assertion( N == 1 ).
test(4,[nondet]) :-
count([a,b],N),
assertion( N == 2 ).
test(5,[nondet]) :-
count([b,a],N),
assertion( N == 2 ).
test(6,[nondet]) :-
count([a,a,b],N),
assertion( N == 2 ).
test(7,[nondet]) :-
count([a,b,a],N),
assertion( N == 3 ).
test(8,[nondet]) :-
count([b,a,a],N),
assertion( N == 2 ).
:- end_tests(count).
Выполнение теста
?- run_tests.
% PL-Unit: count ..
ERROR: c:/question_110.pl:80:
test 3: failed
ERROR: c:/question_110.pl:84:
test 4: failed
ERROR: c:/question_110.pl:88:
test 5: failed
ERROR: c:/question_110.pl:92:
test 6: failed
ERROR: c:/question_110.pl:96:
test 7: failed
ERROR: c:/question_110.pl:100:
test 8: failed
done
% 6 tests failed
% 2 tests passed
false.
РЕДАКТИРОВАТЬ 1
Понял, что нужен хвостовой вызов для двух из предикатов
% Semicontext (push back) needed because two items removed from list.
% Need second item to stay on list.
count_dcg(N0,N),[C] -->
[C,C],
count_dcg(N0,N).
% Semicontext (push back) needed because two items removed from list.
% Need second item to stay on list.
count_dcg(N0,N),[C2] -->
[C1,C2],
{
C1 \== C2,
N1 is N0 + 1
},
count_dcg(N1,N).
Код ещене работает, но это объясняет, почему переменная состояния была связана, когда я ожидал, что она не будет связана.
РЕДАКТИРОВАТЬ 2
Хотя не использовался полуконтекст DCG, как я надеялся, с использованием вариантаполуконтекст в качестве заглядывания, код работает.Не публикуйте это как ответ, потому что я хотел бы, чтобы в ответе либо отображался код DCG, работающий с полуконтекстом в заголовке предложения, либо объяснялся, почему это не так.
lookahead(C),[C] -->
[C].
% empty list
% No lookahead needed because last item in list.
count_3_dcg(N,N) --> [].
% single item in list
% No lookahead needed because only one in list.
count_3_dcg(N0,N) -->
[_],
\+ [_],
{ N is N0 + 1 }.
% Lookahead needed because two items in list and
% only want to remove first item.
count_3_dcg(N0,N) -->
[C1],
lookahead(C2),
{ C1 == C2 },
count_3_dcg(N0,N).
% Lookahead needed because two items in list and
% only want to remove first item.
count_3_dcg(N0,N) -->
[C1],
lookahead(C2),
{
C1 \== C2,
N1 is N0 + 1
},
count_3_dcg(N1,N).
count(L,N) :-
DCG = count_3_dcg(0,N),
phrase(DCG,L).
Выполнение теста
?- run_tests.
% PL-Unit: count ........ done
% All 8 tests passed
true.