Подсчет элементов в списке без учета смежных дубликатов
count([], 0).
count(L, N) :- countDistinct(L, 0).
countDistinct([H,H1|T], N) :-
(H == H1,
countDistinct([H1|T], N));
(H =\= H1, N1 is N+1,
countDistinct([H1|T], N1)).
Мой подход состоял в том, чтобы, очевидно, иметь тривиальный базовый случай, а затем вызвать новый предикат countDistinct с начальным N равным 0. Тогда N увеличивается, только если смежные элементы различны.
Моя идея называть countDistinct как это неправильно? Как я должен адаптировать это.
1 ответ
Поскольку вы пытаетесь решить эту проблему с помощью рекурсии, этот ответ будет иметь такой подход. Также этот ответ будет охватывать только режим связывания списка и подсчета несвязанности и не будет использовать сокращения для удаления точек выбора. Вы можете улучшить код при желании.
При создании рекурсивных предикатов для списка я обычно начинаю с шаблона вроде:
process_list([H|T],R) :-
process_item(H,R),
process_list(T,R).
process_list([],R).
с рекурсивным случаем:
process_list([H|T],R) :-
process_item(H,R),
process_list(T,R).
и базовый вариант:
process_list([],R).
Список деконструирован с использованием [H|T]
где H
для главы списка и T
для хвоста списка. R
для результата.
Головной элемент обрабатывается с использованием:
process_item(H,R)
и хвост списка обрабатывается с помощью:
process_list(T,R)
Поскольку для этого требуется обработка двух смежных элементов в списке, необходимы модификации:
process_list([H1,H2|T],R) :-
process_item(H1,H2,R),
process_list([H2|T],R).
process_list([],0).
process_list([_],1).
NB Теперь есть два базовых случая вместо одного. То, что рекурсивные предикаты обычно являются одним предложением рекурсии и одним базовым случаем, не означает, что они всегда являются одним рекурсивным предложением и одним базовым случаем.
Следующее обновление process_item
process_item(I1,I1,N,N).
process_item(I1,I2,N0,N) :-
I1 \== I2,
N is N0 + 1.
поскольку is/2
используется для увеличения количества, состояние счета должно быть передано, обновлено и передано, таким образом, переменные, N0
а также N
,
При использовании переменных состояния или потоковых переменных соглашение об именах должно добавлять 0
к входному значению не добавляйте число к выходному значению и увеличивайте добавленное число в том же пункте, что и процесс потоков.
Когда элементы одинаковы, счет не увеличивается, что делается с помощью:
process_item(I1,I1,N,N).
Когда элементы отличаются, счет увеличивается, используя:
process_item(I1,I2,N0,N) :-
I1 \== I2,
N is N0 + 1.
В процессе смены process_item
, R
стал N0
а также N
так что это требует изменения process_list
process_list([H1,H2|T],N0,N) :-
process_item(H1,H2,N0,N1),
process_list([H2|T],N1,N).
и для использования этого добавляется вспомогательный предикат, так что сигнатура исходного предиката может оставаться неизменной.
count(L,N) :-
process_list(L,0,N).
Полный код
count(L,N) :-
process_list(L,0,N).
process_list([H1,H2|T],N0,N) :-
process_item(H1,H2,N0,N1),
process_list([H2|T],N1,N).
process_list([],N,N).
process_list([_],N0,N) :-
N is N0 + 1.
process_item(I1,I1,N,N).
process_item(I1,I2,N0,N) :-
I1 \== I2,
N is N0 + 1.
Контрольные примеры
:- 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 ........ done
% All 8 tests passed
true.
Решение с использованием DCG
% Uses DCG Semicontext
lookahead(C),[C] -->
[C].
% empty list
% No lookahead needed because last item in list.
count_dcg(N,N) --> [].
% single item in list
% No lookahead needed because only one in list.
count_dcg(N0,N) -->
[_],
\+ [_],
{ N is N0 + 1 }.
% Lookahead needed because two items in list and
% only want to remove first item.
count_dcg(N0,N) -->
[C1],
lookahead(C2),
{ C1 == C2 },
count_dcg(N0,N).
% Lookahead needed because two items in list and
% only want to remove first item.
count_dcg(N0,N) -->
[C1],
lookahead(C2),
{
C1 \== C2,
N1 is N0 + 1
},
count_dcg(N1,N).
count(L,N) :-
DCG = count_dcg(0,N),
phrase(DCG,L).
Пример выполнения:
?- run_tests.
% PL-Unit: count ........ done
% All 8 tests passed
true.
Лучшее решение с использованием DCG.
Примечание
В вашем примере кода используется ;/2
Типичное соглашение при наборе кода с ;/2
это отформатировать как таковой
(
;
)
таким образом ;
выделяется
Ваш код переформатирован
countDistinct([H,H1|T], N) :-
(
(
H == H1,
countDistinct([H1|T], N)
)
;
(
H =\= H1,
N1 is N+1,
countDistinct([H1|T], N1)
)
).