Пролог: сопоставление одной или нескольких анонимных переменных

[_, [ 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).
Другие вопросы по тегам