prolog, найти элементы списка в списке кортежей

Я пытаюсь решить новую программу с Prolog, и я застрял, и не знаю, как продолжить... Я должен сделать предикат, который имеет 3 аргумента, первый-это список элементов, второй-это список кортежей, а третий-это возвращаемый список, содержащий второй элемент кортежей, если первый элемент кортежа совпадает с элементом первого списка аргументов. Он также должен удалить копии!!

например,

check([a,c],[(a,aa),(bb,bbb),(a,aa),(c,def)],X).
X = [aa, def] .

как вы можете видеть, a и C совпадают в списке кортежей, поэтому верните второй элемент кортежей.

Так это работает, но если есть более одного кортежа, который содержит первый элемент, который соответствует в первом списке, это займет только один раз, например:

check([a,b],[(a,c),(a,d),(b,c),(b,e),(c,f)],X).
X = [c] .

он находит a в первый раз и взял c, и b в первый раз и снова взял c, но не итерации, чтобы найти больше a или b,правильный результат должен быть X=[c,d, e].

поэтому, пожалуйста, я прошу Вас помочь в решении этой ситуации или любой ключ к разгадке...

вот мой код:

check([],_,[]).
check(L,DIC,Xord) :- inter(L,DIC,X), list_to_set(X,Xord).

inter([],_,[]).
inter(L1, DIC, L3) :- 
   L1 = [H|T], 
   DIC = [(DIC1,DIC2)|_], 
   H == DIC1, 
   L3 = [DIC2|L3T], 
   inter(T, DIC, L3T).
inter(L1,[_|DIC],L3) :- inter(L1,DIC,L3).
inter([_|T], DIC, L3) :- inter(T, DIC, L3).

заранее спасибо за ваше время.

6 ответов


для более простой понятной версии я предлагаю следующее:

:- use_module(library(lists)).

keys_dict_uniquevalues(Ks,D,UVs) :-
    keys_dict_values(Ks,D,Vs),              % Vs ... values with duplicates
    list_set(Vs,UVs).                       % UVs ... Vs deduplicated

keys_dict_values([],_D,[]).                 % No keys no values
keys_dict_values([Key|Keys],D,Vs) :-    
    key_dict_values(Key,D,Matches),         % all Matches for Key
    keys_dict_values(Keys,D,OtherVs),       % Matches for other Keys
    append(Matches,OtherVs,Vs).             % all found values in Vs

key_dict_values(_K,[],[]).                  % no mathes if dictionary empty
key_dict_values(K,[(K,V)|Pairs],[V|Vs]) :-  % Value is in list if key matches 
    key_dict_values(K,Pairs,Vs).
key_dict_values(K,[(X,_V)|Pairs],Vs) :-     % Value is not in list
    dif(K,X),                               % if key doesn't match
    key_dict_values(K,Pairs,Vs).

list_set([],[]).                            % empty list contains no duplicates
list_set([X|Xs],[X|Ys]) :-                  % head of the first list
    subtract(Xs,[X],Zs),                    % doesn't occur in Zs
    list_set(Zs,Ys).

если вы хотите написать программу без использования библиотеки (списков), вы должны заменить цель append/3 в keys_dict_values/3 и цель вычесть/3 в list_set/2. В приведенном ниже примере lists_appended/3 и list_x_removed / 3:

keys_dict_uniquevalues(Ks,D,UVs) :-
    keys_dict_values(Ks,D,Vs),
    list_set(Vs,UVs).

keys_dict_values([],_D,[]).
keys_dict_values([Key|Keys],D,Vs) :-
    key_dict_values(Key,D,Matches),
    keys_dict_values(Keys,D,OtherVs),
    lists_appended(Matches,OtherVs,Vs).

key_dict_values(_K,[],[]).
key_dict_values(K,[(K,V)|Pairs],[V|Vs]) :-
    key_dict_values(K,Pairs,Vs).
key_dict_values(K,[(X,_V)|Pairs],Vs) :-
    dif(K,X),
    key_dict_values(K,Pairs,Vs).

lists_appended([],L,L).
lists_appended([X|Xs],Ys,[X|Zs]) :-
    lists_appended(Xs,Ys,Zs).

list_set([],[]).
list_set([X|Xs],[X|Ys]) :-
    list_x_removed(Xs,X,Zs),
    list_set(Zs,Ys).

list_x_removed([],_X,[]).
list_x_removed([X|Xs],X,Ys) :-
    list_x_removed(Xs,X,Ys).
list_x_removed([X|Xs],Z,[X|Ys]) :-
    dif(X,Z),
    list_x_removed(Xs,Z,Ys).

запросы, приведенные в приведенном выше exmaple, работают для обеих версий:

?- keys_dict_uniquevalues([a,c],[(a,aa),(bb,bbb),(a,aa),(c,def)],X).
X = [aa,def] ? ;
no
?- keys_dict_uniquevalues([a,b],[(a,c),(a,d),(b,c),(b,e),(c,f)],L).
L = [c,d,e] ? ;
no

контрпример, предоставленный @false, не выполняется для обоих версии, как и ожидалось:

?- keys_dict_uniquevalues([a,b],[(a,c),(a,d),(b,c),(b,e),(c,f)],[c,c]).
no

необычное использование, предложенное @false:

   ?- keys_dict_uniquevalues([a,b],[KV1,KV2],[e]).
KV1 = KV2 = (a,e) ? ;
KV1 = (a,e),
KV2 = (b,e) ? ;
KV1 = (a,e),
KV2 = (_A,_B),
dif(b,_A),
dif(a,_A) ? ;
KV1 = (b,e),
KV2 = (a,e) ? ;
KV1 = (_A,_B),
KV2 = (a,e),
dif(b,_A),
dif(a,_A) ? ;
KV1 = KV2 = (b,e) ? ;
KV1 = (b,e),
KV2 = (_A,_B),
dif(b,_A),
dif(a,_A) ? ;
KV1 = (_A,_B),
KV2 = (b,e),
dif(b,_A),
dif(a,_A) ? ;
no

до сих пор ваше заявление о проблеме и ваши последующие объяснения немного противоречивы. Посмотрим, подойдет ли это.

реляционные имена.

как самый первый, постарайтесь описать вашу проблему как отношение, а не как последовательность действий или команд. Чтобы лучше обрисовать это, попробуйте найти название для отношения, которое не предполагает, что что-то нужно сделать. Вместо этого опишите каждый аргумент один за другим. Вы нахмурились на это в комментарии, заметив, что "это просто имя". Конечно, только имя. Но это все, что есть у программиста. Имена повсюду. Если каждое имя просто произвольно выбрано или даже ошибочно, у вас будет очень грубое Программирование времени.

так что у вас есть:

  1. список с элементами, используемыми в качестве keys. Обратите внимание, что keys - это множественное число. И в прологе много множеств стенд для списков.

  2. какой-то словарь, или dict С (K, V) элементы. На самом деле, в Пролог мы используем чаще (K-V) вместо этого, и называете это pair, следовательно pairs будет достаточно примерочные тоже. Но давайте придерживаться вашего определения.

  3. список значений. В списке нет дубликатов. Мы могли бы назвать это списком уникальных значений или uvalues.

теперь все это вместе составляет хорошее отношение:

 keys_dict_uvalues(Keys, Dict, UValues)

представьте, как его использовать

перед спешить к фактическому кодированию, просто визуализируйте, что вы уже написали его, и теперь вы хотите его использовать. Или: возможно, вы найдете кого-то, кто напишет предикат для вас. Но как вы можете быть уверены, что код работает? Так что соберите несколько тестовых случаев вместе. Лучше всего начать с наземных запросов:

?- keys_dict_uniquevalues([a,c],[(a,aa),(bb,bbb),(a,aa),(c,def)],[aa,def]).

учитывая это, мы могли бы опустить некоторые части, введя переменные:

?- keys_dict_uniquevalues([K1,K2],[(a,aa),(bb,bbb),(a,aa),(c,def)],[aa,def]).

сколько решений вы ожидаете здесь? Я думаю, что только один. Уверен? На тот момент таких соображения очень ценны.

но теперь для кодирования. Мне часто нравится подход сверху вниз:

keys_dict_uniquevalues(Ks, KVs, Vsu) :-
   keys_dict_values(Ks, KVs, Vs),
   list_nub(Vs, Vsu).

keys_dict_values(Ks, KVs, Vs) :-
   maplist(list_pmemberv(KVs), Ks, Vs).

list_pmemberv(KVs, K, V) :-      % the first fitting K
   tmember(k_v_p_t(K,V), KVs).

k_v_p_t(K, V, (Ki, Vi), T) :-
   if_(K = Ki, ( Vi = V, T = true ), T = false).

list_nub([], []).
list_nub([E|Es], [E|Gs]) :-
   tfilter(dif(E), Es, Fs),
   list_nub(Fs, Gs).

выше использует некоторые понятия, определенные в других примерах: maplist/3, if_/3, tmember/2, tfilter/3, (=)/3, dif/3.

вот несколько примеров, которые довольно необычны:

как словарь с двумя записи выглядят так, что a и b оба отображаются на e?

?- keys_dict_uniquevalues([a,b],[KV1,KV2],[e]).
KV1 = (a,e),
KV2 = (b,e) ? ;
KV1 = (b,e),
KV2 = (a,e) ? ;
no

таким образом, есть две возможности.


С другой стороны, это отношение действительно все о списках и, следовательно, отличный кандидат на DCGs:

keys_dict_uniquevalues(K,D,UVs) :-
    phrase(keys_values(K,D),Vs),
    phrase(uniques(Vs),UVs).

keys_values([],_D) -->                 % no keys no values
    [].
keys_values([K|Keys],D) -->
    key_values(K,D),                   % the values for key K
    keys_values(Keys,D).               % followed by values for other keys

key_values(_K,[]) -->                  % no values left for key _K
    [].
key_values(K,[(K2,V)|Pairs]) -->       % values for
    {dif(K,K2)},                       % different keys are not in the list
    key_values(K,Pairs).
key_values(K,[(K,V)|Pairs]) -->        % values for en equal key
    [V],                               % are in the list
    key_values(K,Pairs).

uniques([]) -->                        % the empty list has no duplicates
    [].
uniques([X|Xs]) -->
    [X],                               % X is in the list
    {phrase(list_without(Xs,X),XRs)},  % once
    uniques(XRs).

list_without([],_X) -->                % no X in the empty list
    [].
list_without([X|Xs],X) -->             % X is not in the list
    list_without(Xs,X).
list_without([Y|Ys],X) -->
    [Y],                               % Y is in the list
    {dif(X,Y)},                        % if it is different from X
    list_without(Ys,X).

Я нахожу эту версию еще проще для чтения, чем мою версию без DCG (см. комментарии в коде). Интерфейс одинаков в обеих версиях, поэтому мои вышеуказанные запросы работают один к одному для этой версии. Запрос

   ?- keys_dict_uniquevalues([a,b],[KV1,KV2],[e]).

также дает те же результаты, только в обратном порядке.


прежде, чем

check([],_,_).

предполагается сделать ? Я бы его бросил.

тогда почему вы все сортируете ? Я бы избегал бесполезной работы...

наконец, вам нужно 2 несвязанных сканирования списков: первый, чтобы получить доступ к ключам, второй для поиска значения, связанного с ключом.

Итак, ваш код нелегко исправить без перезаписи с нуля. А затем рассмотрим примитивы:

check(Keys,Dict,Values) :-
  findall(V, (member(K, Keys), memberchk((K,V),Dict)), Values).

:- import append/3, member/2 from basics.

remove_duplicates([], []).

remove_duplicates([X | Y], Z) :- member(X, Y), !, remove_duplicates(Y, Z).

remove_duplicates([X | Y], [X | Z]) :- remove_duplicates(Y, Z).

hcheck(_, [], []).

hcheck(X, [(X, Y) | L], R) :- !, hcheck(X, L, RR), append([Y], RR, R).

hcheck(X, [_ | L], R) :- hcheck(X, L, R).

check([], _, []).

check([X | Y], L, R) :- hcheck(X, L, RR), check(Y, L, RRR), append(RR, RRR, RRRR), remove_duplicates(RRRR, R).

/********/

/* test */

/********/

[check loaded]

yes
| ?- check([a,c],[(a,aa),(bb,bbb),(a,aa),(c,def)],X).

X = [aa,def];

no
| ?- check([a,b],[(a,c),(a,d),(b,c),(b,e),(c,f)],X).

X = [d,c,e];

no

check / hcheck просто образуют двойной цикл. check выбирает элементы из первого списка, в то время как hcheck соответствует элементу кортежам во втором списке. Результаты просто добавляются; в конце дубликаты удаляются. Я не эксперт (только что узнал Пролог), поэтому я не знаю, насколько хорошо это решение, но, похоже, работает нормально.


EDIT:

check([],_,[]).

check([X|Xs],List,[R|Rs]):-
        check_(X,List,R),
        check(Xs,List,Rs).

check_(_,[],[]).
check_(X,[(Z,Y)|Xs],Res):-
        X=Z->
        Res=[Y|Ys],
        check_(X,Xs,Ys);
        Res=Ys,
        check_(X,Xs,Ys).

тест :

    | ?- check([a,c],[(a,aa),(bb,bbb),(a,aa),(c,def)],X).
X = [[aa,aa],[def]] ? ;
no

    check([a,b],[(a,c),(a,d),(b,c),(b,e),(c,f)],X).
X = [[c,d],[c,e]] ? ;
no