Пролог: сопоставление одной или нескольких анонимных переменных
[_, [ X, _ ],_] будет соответствовать списку, подобному [d, [X, a], s]. Есть ли способ сопоставить его с любым шаблоном, где есть одна или несколько анонимных переменных? то есть. [[X, a], s] и [[d,a],[p,z], [X,b]] будут совпадать?
Я пытаюсь написать программу для подсчета элементов в списке, т.е. [a,a,a,b,a,b] => [[a,4],[b,2]] но я застрял:
listcount(L, N) :- listcountA(LS, [], N).
listcountA([X|Tail], [? [X, B], ?], N) :- B is B+1, listcountA(Tail, [? [X,B] ?], N).
listcountA([X|Tail], AL, N) :- listcountA(Tail, [[X,0]|AL], N).
Благодарю.
3 ответа
Переменная соответствует термину, и переменная anonimus не является исключением. Список - это просто синтаксический сахар для бинарного отношения между головой и хвостом. Таким образом, переменная может соответствовать списку, голове или хвосту, но не определенной последовательности.
Некоторые заметки, надеюсь, помогут вам:
listcount (L, N): - listcountA (LS, [], N).
В Прологе предикаты идентифицируются по имени и num.of.arguments, так называемым функторам и арности. Поэтому обычно предикаты "service" с добавленными аргументами сохраняют одно и то же имя.
listcountA ([X | Tail], [? [X, B],?], N): - B - B+1, listcountA (Tail, [? [X, B]?], N).
B - B+1 никогда не будет успешным, вы должны использовать новую переменную. И нет никакого способа сопоставить внутри списка, используя "подстановочный знак", как вы, кажется, делаете. Вместо этого напишите предикат, чтобы найти и обновить счетчик.
Последнее замечание: обычно пары элементов обозначаются с помощью бинарного отношения, обычно некоторого (произвольного) оператора. Например, чаще всего используется тире.
Так я бы написал
listcount(L, Counters) :-
listcount(L, [], Counters).
listcount([X | Tail], Counted, Counters) :-
update(X, Counted, Updated),
!, listcount(Tail, Updated, Counters).
listcount([], Counters, Counters).
update(X, [X - C | R], [X - S | R]) :-
S is C + 1.
update(X, [H | T], [H | R]) :-
update(X, T, R).
update(X, [], [X - 1]). % X just inserted
update/3 может быть упрощен с использованием некоторого предиката библиотеки, "движущегося внутри" рекурсии. Например, используя select/3:
listcount([X | Tail], Counted, Counters) :-
( select(X - C, Counted, Without)
-> S is C + 1
; S = 1, Without = Counted
),
listcount(Tail, [X - S | Without], Counters).
listcount([], Counters, Counters).
Я предвосхищу этот пост, сказав, что если вам нравится этот ответ, рассмотрите возможность присвоения правильного ответа @chac, поскольку этот ответ основан на их ответе.
Вот версия, которая также использует аккумулятор и обрабатывает переменные в списке ввода, предоставляя вам структуру выходных терминов, которую вы запросили напрямую:
listcount(L, C) :-
listcount(L, [], C).
listcount([], PL, PL).
listcount([X|Xs], Acc, L) :-
select([X0,C], Acc, RAcc),
X == X0, !,
NewC is C + 1,
listcount(Xs, [[X0, NewC]|RAcc], L).
listcount([X|Xs], Acc, L) :-
listcount(Xs, [[X, 1]|Acc], L).
Обратите внимание, что listcount/2
откладывается на аккумуляторную версию, listcount/3
который поддерживает счетчики в аккумуляторе и не предполагает упорядочения ввода или списка входов заземления (именованные / помеченные переменные будут работать нормально).
[_, [X, _], _] будут соответствовать только спискам, которые имеют 3 элемента, 1-й и 3-й могут быть атомами или списками, второй элемент должен быть списком длины 2, но я полагаю, что вы это знаете. Он не будет соответствовать списку из 2 элементов, лучше использовать рекурсию "голова к хвосту", чтобы найти элемент и вставить его в список результатов. Вот эскиз предиката, который, я готов поспорить, не сработает, если скопировать вставить;)
% find_and_inc(+element_to_search, +list_to_search, ?result_list)
find_and_inc(E, [], [[E, 1]]);
find_and_inc(E, [[E,C]|T1], [[E,C1]|T2]) :- C1 is C+1;
find_and_inc(E, [[K,C]|T1], [[K,C]|T2]) :- find_and_inc(E, T1, T2).