【问题标题】:prolog, find list elements in a list of tuplesprolog,在元组列表中查找列表元素
【发布时间】:2016-07-18 07:27:34
【问题描述】:

我正在尝试用 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).

提前感谢您的宝贵时间。

【问题讨论】:

  • 为什么称谓词check/2?你没有检查你的例子。
  • 嗯,它只是一个名字,我知道我正在构建....
  • “只是一个名字”,...

标签: list prolog tuples


【解决方案1】:

为了更容易理解的版本,我提出以下建议:

:- 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).

如果你想编写一个不使用库(列表)的程序,你必须替换 keys_dict_values/3 中的目标 append/3 和 list_set/2 中的目标减去/3。在下面的示例中,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).

上述示例中给出的查询适用于两个版本:

?- 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

【讨论】:

  • KV1 = KV2 = (a,e) 解决方案?嗯,我不会为此解释这个问题。但这是一种解释。
  • @false:是的,这是一个值得商榷的解释。
【解决方案2】:

到目前为止,您的问题陈述和您随后的解释有点矛盾。让我们看看这是否合适。

关系名称。

首先,尝试将您的问题描述为一种关系,而不是一系列操作或命令。为了更好地概括这一点,请尝试为关系找到一个不暗示必须做某事的名称。相反,请一一描述每个论点。你在评论中对此皱眉,观察到“这只是一个名字”。当然,只是一个名字。但这就是程序员所拥有的一切。名字遍地。如果每个名字都是随意选择的,甚至是误导,你的编程时间会很艰难。

那么你有什么:

  1. 包含用作keys 的元素的列表。请注意,keys 是复数形式。在 Prolog 中,许多复数形式代表列表。

  2. 一些字典,或带有(K, V) 元素的dict。实际上,在 Prolog 中,我们更常用的是(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.

这里有一些非常不寻常的例子:

具有两个条目的字典必须如何看起来像 ab 都映射到 e

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

所以有两种可能。

【讨论】:

  • 嗯,非常感谢您的回答。现在,每当我必须选择一个名字时,我都会记住你的建议:)。另一方面,我将更深入地了解您在其他示例中使用的函数。无论如何,我了解了整个过程。是的,我同意有两种可能性,但它是相同的对,只是顺序不同。再次感谢您的建设性回答。
  • @false:关于查询?- keys_dict_uniquevalues([a,b],[KV1,KV2],[e]).:我认为还有六个额外的解决方案。我编辑了我的非 dcg 版本以包含该查询。
【解决方案3】:

再想一想,这种关系实际上都是关于列表的,因此非常适合 DCG:

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 版本更容易阅读(参见代码中的 cmets)。两个版本的界面是相同的,所以我上面的查询对于这个版本是一对一的。查询

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

也会以相反的顺序产生相同的结果。

【讨论】:

  • 非常好!关于removed//2,只有一个非常小的评论:更好的名称和参数顺序:list_without//2。出于几个原因,尤其是为了遵循其他规则的模式,我建议将最后一个子句的正文写成:[Y], { dif(X,Y) }, ...,即重新排列前两个目标。
  • @mat:你是对的。这样看起来更好。将 dif/2 目标放在首位是不习惯的。我喜欢把便宜的目标放在第一位,但在这种情况下,它并没有太大的区别。
【解决方案4】:

首先,什么

check([],_,_).

应该怎么做?我会放弃它。

那么,为什么要对所有内容进行排序?我会避免做无用的工作......

最后,您需要对列表进行 2 次不相关的扫描:第一次扫描获取键,第二次搜索与键关联的值。

因此,如果不从头开始重写,您的代码不容易纠正。然后考虑内置函数:

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

【讨论】:

  • 好吧,我犯了一些错误,正如你所说,第一次检查([],)。,应该是一个空列表,现在它已修复。我使用排序来删除副本,但我现在将使用 list_to_set。我现在将根据您的最终建议工作,关于 2 次扫描。
  • 非常感谢您,在深入了解并理解它之后,现在它可以工作了。我只需要将第二个 memberchk 更改为 member,因为我想要所有结果不仅是第一个,然后删除重复项。再次感谢
  • @CapelliC 一般而言,谓词和谓词之间的区别在哪里chk
  • @AnsPiter:memberchk/2 就像 member/2 后跟一个剪辑。但是在 SWI-Prolog 中是在低级别实现的,在 C 中并且注重效率。
  • @CapelliC 为什么后面跟着一个cut?这是否意味着当它在列表中找到 Elem 的第一次出现时它停止了?
【解决方案5】:
:- 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 将元素与第二个列表中的元组匹配。结果只是简单地附加;最后,删除重复项。我不是专家(刚学过 Prolog),所以我不知道这个解决方案有多好,但它似乎工作正常。

【讨论】:

  • 仅代码的答案并非来自高价值。如果您可以添加一些解释,这将对未来更有帮助。
  • 对不起; check/hcheck 只是形成一个双循环。 check 从第一个列表中选择元素,而 hcheck 将元素与第二个列表中的元组匹配。结果只是简单地附加;最后,删除重复项。我不是专家(刚学过 Prolog),所以我不知道这个解决方案有多好,但它似乎工作正常。
【解决方案6】:

编辑:

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

【讨论】:

  • @false 我用 CUT'!'为了 ;如果我找到元素 X,我停止了返回 Y 的第一次出现的谓词
  • 如果你想学s-t:首先选择一个关系名称。然后可以看看这些其他问题。
  • 谢谢,但这不起作用。例如试试这个:check([a,b],[(a,c),(a,d),(b,c),(b,e),(c,f)],X)。我希望你的建设可以改善我的,我会更深入地看看;)。
  • @rubitops 你能记录下这个查询的预期结果吗
  • 是的,结果是 X = [c, d, e]。
猜你喜欢
  • 1970-01-01
  • 2016-06-10
  • 1970-01-01
  • 2011-01-12
  • 2011-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多